May 2009

Détection de processus cachés

Nous avons développé lors d'une de nos missions un anti-rootkit pour Windows. Une des fonctionnalités kernel de ce dernier liée à la détection de processus cachés est détaillée dans ce présent billet.

Nous étudierons plus particulièrement une technique qui permet notamment de détecter le très célèbre Futo Enhanced. Pour rappel, ce rootkit va jusqu'à supprimer l'entrée du processus à cacher de la PspCidTable, table utilisée par le noyau Windows pour stocker threads et processus du système.

Détecter Futo Enhanced est très simple si l'on cherche sa signature en mémoire. C'est évidemment sans aucun intérêt puisque la méthodologie n'est pas générique. Nous avons donc préféré cumuler les techniques de récupération d'information et faire une corrélation de ces informations. La solution présentée ici permet de récupérer une liste de structures EPROCESS correspondant aux processus actifs sur le système.

Première étape: récupérer l'adresse de cette fameuse table. On analyse d'abord en mémoire la fonction PsLookupProcessByProcessId. Comme son nom l'indique, cette fonction retourne la structure EPROCESS correspondant au pid donné en argument (note intéressante, les pids sous Windows sont multiples de 4, car ils servent d'offset pour cette table).

Voici un extrait de cette fonction dans lequel on retrouve bien la valeur courante de la PspCidTable :

805c9446 8bff            mov     edi,edi
805c9448 55              push    ebp
805c9449 8bec            mov     ebp,esp
[...]
805c945e ff35e0a25580    push    dword ptr [nt!PspCidTable (8055a2e0)]

La PspCidTable contient la liste des EPROCESS, mais aussi la liste des ETHREAD, structures contenant des informations sur chacun des threads actifs du système d'exploitation.

Seconde étape: récupérer toutes les entrées "valides" de cette table (les entrées inutilisées ou en phase de suppression contiennent un "flag"). Les entrées sont de ce type :

typedef struct _TABLE_ENTRY {
        DWORD object;
        ACCESS_MASK security;
};

hide_cat2Il reste ensuite à différencier les ETHREAD des EPROCESS. Plusieurs solutions existent, la meilleure étant d'en implémenter le maximum (comme toutes les récupérations d'information). Une méthode très simple à mettre en place est de considérer la structure comme une structure ETHREAD, d'en sortir le champ correspondant à la service table, et de le comparer aux deux seules valeurs possibles, à savoir la service table classique et celle des GUI threads. Si la valeur ne correspond pas, il y a de fortes chances que ce soit une structure EPROCESS.

lkd> dt nt!_KTHREAD  88dc85e0
[...]
   +0x0e0 ServiceTable     : 0x80552fe0
[...]

Nous voila enfin avec une liste des structures pointant vers les threads actuellement actifs sur la machine. Ces threads étant liés à un processus, il suffit de rechercher dans la structure l'information suivante (l'offset est dépendant de la version du système, il est ici à titre informatif) :

lkd> dt nt!_ETHREAD
[...]
    +0x220 ThreadsProcess : Ptr32 _EPROCESS
[...]

Après avoir récupéré les pointeurs EPROCESS de ces structures, si un processus non présent dans les précédents listings apparait, il s'avère alors suspicieux.

C'est en cumulant des techniques de ce type qu'on parvient à détecter la plupart des codes malveillants. Le concept est simple, pousser plus loin que le créateur de rootkit.

Contournement de DEP

logo tomcat

Une vulnérabilité de type buffer overflow dans le module mod_jk a été découverte en 2007 (CVE-2007-07774). Elle est présente dans la fonction map_uri_to_worker()  (native/common/jk_uri_worker_map.c) et permet à un attaquant d'exécuter du code arbitraire à distance.

Si l'exploitation de cette vulnérabilité reste triviale sous Windows avec DEP désactivé, elle devient un peu plus complexe lorsque cette protection est activée.

Le document "Contournement de Data Prevention Execution : exploitation de mod_jk 1.2.20" explique comment contourner la protection et exploiter cette vulnérabilité sur un système Windows avec DEP activé.

Vulnérabilité udev

logo udev Une vulnérabilité dans udevd permettant une élévation de privilèges sur les systèmes Linux a récemment été découverte par Sebastian Krahmer. Des explications de la vulnérabilité, la manière de l'exploiter et le patch à appliquer sont présents çà et . Des exploits ont même été diffusés sur milw0rm.

Une des méthodes d'exploitation est d'utiliser le fichier de règles 95-udev-late.rules et d'exécuter des commandes arbitraires  en utilisant l'action remove. L'inconvénient de cette technique est que cette action n'est pas forcément présente sur toutes les distributions Linux (comme par exemple RedHat et ses dérivés) . 

Une meilleure solution, complètement fiable de surcroit, est de créer un noeud de périphérique en mode 0666 qui accéde à une partition montée (/dev/zero vers /dev/sda1 par exemple). Il est alors possible de modifier les droits d'un binaire (typiquement pour le rendre suid) en accédant directement à bas niveau au système de fichiers.

En supposant que le système de fichiers rencontré est toujours ext{2,3} (il est en effet rare en pratique d'observer des partitions principales formatées en reiserfs, xfs ou autre), l'attaquant dispose de deux solutions. La première est d'utiliser debugfs et la commande set_inode_field (si) ou modify_inode (mi) (set_inode_field est uniquement présente dans les dernières versions de debugfs). debugfs étant fourni avec le package e2fsprogs dans lequel sont aussi présents mke2fs et e2fsck,  nous pouvons supposer que cet outil est toujours présent sur les systèmes Linux.

En utilisant debugfs, on obtient alors :

$ ls -alp /dev/hda1
brw-rw---- 1 root disk 3, 1 Apr 30 02:23 /dev/hda1
$ ls -alp /dev/zero
brw-rw-rw- 1 root disk 3, 1 Apr 30 02:26 /dev/zero
$ sync
$ /sbin/debugfs -w /dev/zero
debugfs 1.41.3 (12-Oct-2008)
debugfs:  mkdir .xxx
debugfs:  cd .xxx
debugfs:  write /bin/bash pwn
Allocated inode: 16
debugfs:  set_inode_field pwn mode 0104755
debugfs:  close
debugfs:  quit
$ sync
$ ls -alp /tmp/.xxx/pwn
-rwsr-xr-x 1 compaq compaq 700492 Apr 30 02:30 /tmp/.xxx/pwn

Si debugfs n'est pas présent, il est toujours possible d'utiliser la librairie ext2fs.

Afin de reproduire le scénario précédent, le pseudo algorithme à suivre pour rendre un binaire setuid peut alors être :

1/ Ouverture du périphérique en écriture avec ext2fs_open() et lecture des tables bitmaps de blocs et d'inodes avec ext2fs_read_inode_bitmap() et ext2fs_read_inode_bitmap().

2/ Création d'un répertoire (/tmp/.xxx par exemple en prenant soin de vérifier que /tmp n'est pas monté en nosuid et noexec) avec ext2fs_mkdir().

3/ Copie du binaire dans le répertoire précédement créé avec les fonctions ext2fs_namei(), ext2fs_new_inode() et ext2fs_link().

4/ Modification de la structure inode du fichier nouvellement créé avec les fonctions ext2fs_read_inode() et ext2fs_write_inode() pour lui donner les droits suid.

Il ne faut pas oublier de faire un sync() avant et après avoir accédé au système de fichiers afin qu'il soit mis à jour. A partir de là, on obtient un exploit complètement fiable et générique. La seule contrainte (qui n'en est pas vraiment une) est donc d'être sur un système de fichiers ext{2,3}.