Wordpress plugin pwnage

7 min. lecture

Share

Aujourd'hui, on va parler failles Wordpress. Ce qu'il faut savoir sur WP, c'est que la plupart des attaques ne se font pas sur le core de WP, qui est assez bien sécurisé, mais sur les plugins, qui eux par contre, n'utilisent pas forcément les best practices au niveau sécurité. C'est ce que je vais détailler dans ce billet. Pour mettre en place ce blog, il a fallu tester plusieurs plugins, dont un qui répond au doux nom de "User Access Manager", et qui permet de gérer l'accès à certains articles selon le rôle des utilisateurs. En l'occurence, il fallait ici que les personnes inscrites puissent lire les articles "privé" (la fonction "privée" intégrée à WP ne convenait pas). Du coup, téléchargement, installation, activation. Rapide coup d'oeil sur les options, le plugin répond plutôt bien aux attentes. Pour être sur de la qualité, je me suis mis en tête de vérifier tout le code source, pour voir s'il n'y avait pas d’injection SQL ou de XSS flagrantes. Le plugin contient plusieurs fichiers php : des defines, les fichiers de template, un index, et 3 classes. Première chose à faire, regarder l'index (qui s'appelle en fait user-access-manager.php, mais qui est chargé par défaut). Quelques rapides coups de molettes, rien de bien méchant de ce coté. Les fichiers de templates et les defines ne sont pas des plus intéressants, je regarde donc les classes un peu plus en détail, et là, c'est le drame, ma vie bascule ! Mes yeux tombent presque par hasard (ou alors était-ce le destin ?) sur la fonction createHtaccess($dir, $objectType). Create Htaccess ! Genre, le plugin crée un htaccess, un fichier qui permet de donner des directives à Apache. Le truc qui permet de dire, ah ben tiens, rediriges moi là, ou des choses du genre. "Woot", comme disent les jeunes. Forcément, je me sens obligé de regarder la fonction en profondeur. Pour faire simple, cette fonction permet d'autoriser ou d'interdire le chargement de certains types de fichiers en utilisant le paramètre <FilesMatch>. La ligne la plus intéressante est celle ci :

if ($uamOptions['lock_file_types'] == 'selected') { $htaccessTxt .= "<FilesMatch '\.(" . $fileTypes . ")'>\n";

Si j'arrive à contrôler $fileTypes, je pourrais donc modifier le comportement du htaccess. En traçant un peu l'utilisation de $fileTypes, je m'apercois rapidement qu'elle est définie plus haut grâce à

$fileTypes = $uamOptions['locked_file_types'];

qui vient lui même de la méthode getAdminOptions(), qui renvoie les paramètres mis en place par l'admin dans les options du plugin. En gros, on contrôle les données. Du coup, je me redis "woot", et j'essayes rapidement une technique de jedi qui consiste à refermer la balise FilesMatch. Pourquoi ? Tout simplement pour modifier l'utilisation du htaccess. Imaginons un peu, qu'à la place de "zip,rar,tar,gz,bz2" (la valeur par défaut de la variable), je mette "zip,rar,tar,gz,bz2'>", que se passerait il ? J'aurais fermé le FilesMatch, et je me retrouverais dans cette situation :

<FilesMatch '\.(zip|rar|tar|gz|bz2)'>DONNEES CONTROLEES ICI'> AuthType Basic AuthName "WP-Files" AuthUserFile C:\wamp\www\wordpress/wp-content/uploads/.htpasswd require valid-user </FilesMatch>

Je pourrais par exemple mettre la commande redirect, qui redirigerait automatiquement tous les liens vers des fichiers zip, rar, tar, gz, et bz2 sur le lien que je voudrais (Qui à dit phishing ?). Bref, on commence à voir l’intérêt de contrôler ces datas :) Seulement, petit problème, on a un '> qui traine, et qui va faire planter le htaccess. Pas de problème, il suffit de continuer à modifier le flux, en fermant la balise FilesMatch, et en rouvrant une autre, comme ceci :

<FilesMatch '\.(zip|rar|tar|gz|bz2)'><FilesMatch>DONNEES CONTROLEES ICI<FilesMatch '\.(zip|rar|tar|gz|bz2)'> AuthType Basic AuthName "WP-Files" AuthUserFile C:\wamp\www\wordpress/wp-content/uploads/.htpasswd require valid-user </FilesMatch>

Ainsi, le premier FilesMatch est fermé proprement, on controle notre flux de datas, et le deuxième FilesMatch est correctement ouvert, ne provoquant pas d'erreur. Reste plus qu'à essayer :) Je charge mon plugin dans mon navigateur, j'ouvres mon Tamper Data pour modifier à la volée ma requete et mettre

zip|rar|tar|gz|bz2)'><FilesMatch><FilesMatch \.(zip|rar|tar|gz|bz2)

je charge la page, et..... Paf, ERREUR ! Wait ? What ? Comment ça une erreur, c'pas possible, ça devrait fonctionner ! J'ouvre mon htaccess fraîchement crée afin de voir d'où pourrait venir cette erreur, pour m'apercevoir que mon attaque ne PEUT PAS fonctionner en l'état. Pourquoi ? Juste à cause d'un caractère. Un caractère tout simple, mais obligatoire (normalement ;)) pour faire fonctionner le htaccess : le ' (quote simple). Et oui, si je reprends ma variable craftée, je suis obligé de fermer mon premier FilesMatch avec une quote. Sauf que pour améliorer la sécurité, WP a mis en place une sécurité qui permet de bloquer les injection SQL en rajoutant automatiquement un anti slash devant les quotes, ce qui fait que mon htaccess ne comprenait pas mon anti slash, et provoquait une erreur. Wordpress : 1 - Corto : 0. OK, pas de problème, j'ai encore quelques idées derrière la tête. En réfléchissant, à quoi sert cette quote ? A refermer la string du FilesMatch, mais, elle est vraiment obligatoire ? Et bien en fait non, parce que, comme les navigateurs par qui comprennent les certaines balises html même si elles sont mal utilisées, peut être qu'Apache peut comprendre mon FilesMatch si je l'utilise mal... en enlevant la quote par exemple ! En gros, faire ça :

<FilesMatch '\.(zip|rar|tar|gz|bz2)>

Je crée donc un htaccess basique, avec juste un FilesMatch mal crafté, pour voir la réaction d'Apache, et, ô joie, ô bonheur, le htaccess est compris par Apache. Du coup, il me suffit d'enlever ma quote dans mon injection pour régler le problème :). Wordpress : 1 - Corto : 1 Je reteste mon crafting de variable, en supprimant ma quote, je charge ma page, et... Erreur. Encore une fois. J'ai l'impression qu'Apache se moque de moi ! WP gagne encore un point. Le match est serré ! Je rouvre mon htaccess pour vérifier, pas de caractère bizarre, rien de choquant. Mais oui ! Suis-je bête ! les retours charriots ! Il faut que les commandes soient sur plusieurs lignes. Je modifie (encore) ma variable, qui ressemble maintenant à ça :

zip|rar|tar|gz|bz2)>'%0D%0A</FilesMatch>%0D%0A<FilesMatch \.(zip|rar|tar|gz|bz2)

Le %0D%0A étant la chaine en ASCII du retour chariot. En chargeant ma nouvelle variable, je dois donc normalement arriver à ce genre de .htaccess :

<FilesMatch '\.(zip|rar|tar|gz|bz2)> </FilesMatch><FilesMatch \.(zip|rar|tar|gz|bz2)'> AuthType Basic AuthName "WP-Files" AuthUserFile C:\wamp\www\wordpress/wp-content/uploads/.htpasswd require valid-user </FilesMatch>

Test... Attente du résultat... Pas d'erreur ! L'injection fonctionne ! Je marque un point contre Wordpress. Si les comptes sont bons, on en est à 2-2. Maintenant, la question principale est que puis-je réellement faire avec tout ça ? Une petite directive sympathique des fichiers .htaccess, c'est AddType. En soi, la commande n'est pas méchante, elle permet juste de lier un type mime à une extension. En gros, on peut dire que les fichier .abc seront liés au type mime application/ABCapp. Ou encore que les fichiers .zip soient liés au mime application/x-httpd-php. Le lecteur attentif comprendra où je veux en venir. Pourquoi ne pas mettre cette commande dans mon injection, afin de pouvoir définir qu'un type de fichier (par exemple .zip) sera traité comme étant du PHP ? Après tout, rien ne nous empêche de mettre du php dans une fichier zippé, si ? Pour finaliser l'attaque, il reste donc deux choses à faire. Modifier le script pour ajouter la directive, et uploader un fichier. Pour le premier point, c'est plutôt facile, et ma variable doit donc ressembler à ceci :

zip%2Crar%2Ctar%2Cgz%2Cbz2)>%0D%0A</FilesMatch>%0D%0AAddType application/x-httpd-php .zip%0D%0A<FilesMatch ^\.(zip%2Crar%2Ctar%2Cgz%2Cbz2

Il ne reste plus qu'à uploader sur le blog un zip crafté (un simple script php renommé en .zip fera l'affaire), et voilà ! Ensuite, il suffit d'aller sur le lien du zip, pour afficher notre petit shell PHP, comme le célèbre c99. A partir de là, je peux exécuter n'importe quel code PHP ! Le mieux, étant de trouver une faille dans la version de PHP (ou ailleurs), afin de pouvoir faire un privilege escalation, et se retrouver root sur la machine ;) Cependant, cette attaque reste à mitiger. En fait, ce plugin est dans sa configuration de base utilisable uniquement par les administrateurs, sauf que les administrateurs peuvent modifier le code Wordpress comme ils veulent. Du coup, l’intérêt est moindre. Par contre, une option du plugin est d'autoriser l'administration du plugin à d'autres rôles, qui eux n'ont pas accès au code pour le modifier. Tout n'est pas perdu donc :). Pour faire une conclusion rapide, et bien évidemment sans remettre en cause le travail du développeur original (que je tiens à remercier pour m'avoir laissé écrire cet article, quand on sait à quel point le disclosure, même en responsible, peut être dangereux), on peut voir qu'il est facile de trouver une faille dans Wordpress. Il s'agit ici d'une vulnérabilité relativement difficile à exploiter, puisqu'il faut réunir plusieurs conditions, mais elle n'est pas pour autant à négliger ! Surtout quand on sait à que la plupart des piratages ont comme point de départ un site web... Corto PS : pour ceux qui voudraient essayer, la version sur laquelle j'ai testé, comporte apparemment un bug. Il faut modifier deux fois la variable (et donc charger deux fois la page) pour que la modification dans le fichier htaccess soit prise en compte. Le but du jeu n'étant pas de débugger l'appli, mais de trouver les failles, je n'ai pas vérifié ce problème.

Inscrivez-vous à notre newsletter

Recevez dans votre boîte aux lettres électronique les dernières nouvelles sur la sécurité, des informations et les tendances du marché.

À la une

Plus de nouveautés