
Vulnérabilité de débordement de tampon de pile et d'exécution de code à distance dans l'IGD UPnP du LINKSYS AC1900 EA7500v3
Cette vulnérabilité permet à des attaquants adjacents au réseau d'exécuter du code arbitraire sur les installations affectées des routeurs LINKSYS EA7500. Aucune authentification n'est requise pour exploiter cette vulnérabilité.
La faille spécifique réside dans le traitement des données de requêtes HTTP envoyées au service IGD UPnP. Lors de l'analyse du contenu d'une variable fournie par l'utilisateur dans une demande d'action SOAP UPnP, le processus ne valide pas correctement la longueur des données fournies par l'utilisateur avant de les copier dans un tampon de pile de longueur fixe. Un attaquant peut exploiter cette vulnérabilité pour exécuter du code avec les privilèges root.
Produit/Firmware : Firmware Linksys EA7500 TOUTES VERSIONS incluses Ver.3.0.1.207964 - Les autres modèles de routeurs et versions de firmware utilisant ce binaire sont également concernés
Le binaire du service UPnP IGD se trouve dans /usr/sbin/IGD et s'exécute par défaut sur le port 49152. Ce service fournit le fichier de description XML UPnP IGDdevicedesc.xml qui détaille plusieurs fonctions pouvant être invoquées via des requêtes HTTP avec un corps XML.
L'une de ces fonctions est SetDefaultConnectionService. Cette fonction nécessite une variable de type chaîne. Lorsque la fonction SetDefaultConnectionService est invoquée et exécutée, le programme ne valide pas la longueur de la variable fournie par l'utilisateur avant d'effectuer un appel strncpy impliquant ce tampon. L'adresse source et la taille de copie de l'appel strncpy sont toutes deux contrôlées par l'utilisateur, la taille de copie étant la longueur de la variable fournie par l'utilisateur.
Les données sont ensuite copiées dans un tampon fixe de 184 octets, ce qui conduit à une vulnérabilité de débordement de tampon de pile.
La fonction SetDefaultConnectionService est étiquetée comme _set_connection_type. La fonction commence par initialiser un tampon de 184 octets, puis obtient un pointeur vers le tampon contenant la variable de chaîne fournie par l'utilisateur incluse dans la requête, si elle n'est pas NULL. Cela est réalisé en appelant PAL_xml_node_GetFirstbyName puis PAL_xml_node_get_value. Voir le code ci-dessous :
int _set_connection_type(int **param_1)
{
int iVar1;
char *var_value;
size_t var_value_length;
undefined uVar2;
undefined1 *puVar3;
char **ppcVar4;
undefined4 *puVar5;
char *pcVar6;
int *piVar7;
char acStack_d4 [184]; -----> /* Initializing 184-byte buffer */
memset(acStack_d4,0,0xb4);
iVar1 = PAL_xml_node_GetFirstbyName((*param_1)[0xf0],"NewConnectionType",0); -----> /* iVar1 now points to the user-supplied value */
if ((iVar1 != 0) && (var_value = (char *)PAL_xml_node_get_value(), var_value != (char *)0x0)) { -----> /* Ensures the user-supplied value is not empty and obtains a pointer to it */
...
Plus loin dans la même fonction, un appel strlen est effectué pour obtenir la taille de la chaîne fournie par l'utilisateur plus un décalage statique de 0x174. La condition vulnérable est déclenchée dans l'appel strncpy suivant, où l'argument de destination est l'adresse du tampon de 184 octets nouvellement initialisé, la source est le pointeur vers la chaîne fournie par l'utilisateur, et la taille de l'opération de copie est la taille de la chaîne fournie par l'utilisateur retournée par l'appel à strlen plus un décalage statique de 0x174. Cela conduit à une vulnérabilité de débordement de tampon, car l'adresse source et les variables de taille sont contrôlées par l'utilisateur et aucun contrôle de validation de taille n'est effectué. Voir le code ci-dessous :
int _set_connection_type(int **param_1)
{
...
var_value_length = strlen((char *)(iVar1 + 0x174)); ----> /* iVar1 is a pointer to the user supplied string */
strncpy(acStack_d4,(char *)(iVar1 + 0x174),var_value_length + 1); ----> /* Vulnerable strncpy call */
...
Le décalage pour écraser une adresse de retour de fonction sur la pile est de 276 octets. Les 4 octets suivants peuvent être utilisés pour rediriger l'exécution vers une adresse arbitraire et ainsi détourner le flux de contrôle du programme.
Utiliser une fonction de copie de chaîne plus sûre : au lieu d'utiliser strncpy, qui ne garantit pas la terminaison par null du tampon de destination, on peut utiliser une fonction de copie de chaîne plus sûre comme strncpy_s ou memcpy_s. Ces fonctions garantissent que le tampon de destination est toujours terminé par null et n'autorisent pas la copie de plus de données que le tampon ne peut en contenir.
Validation de l'entrée : valider l'entrée utilisateur pour s'assurer qu'elle ne dépasse pas la taille du tampon de destination. Si l'entrée est plus longue que la taille du tampon, la rejeter ou la tronquer pour qu'elle tienne dans le tampon.
Utiliser un tampon alloué dynamiquement : allouer la mémoire du tampon de destination dynamiquement au lieu d'utiliser un tampon de pile de taille fixe. Cette approche permet d'ajuster la taille du tampon en fonction de la taille des données d'entrée. (Non recommandé pour un système aux ressources limitées)
Exécution du PoC : python poc.py 192.168.1.1 49152
Lien de téléchargement du logiciel : https://support.linksys.com/kb/article/559-en/ (Version du firmware Ver. 3.0.1.207964)
gdbserver-7.7.1-armel-eabi5-v1-sysv disponible ici : https://github.com/stayliv3/gdb-static-cross/tree/master/prebuilt).pkill IGD pour terminer le binaire, puis relancez-le à l'aide de ./gdbserver :1337 /usr/sbin/IGD. Le fichier .gdbinit suivant peut être utilisé pour démarrer une session de débogage stable :> set follow-fork-mode child
> set detach-on-fork off
> target remote 192.168.1.1:1337
> c