16 avr. 2018

VPN : test de Shellfire

Avertissement : ce billet est l'objet d'un partenariat avec Shellfire, j'ai accepté de rédiger ce test en échange de 6 mois de service au niveau PremiumPlus (et d'un lien vers leur service).

Shellfire est une société qui propose un service de VPN. L'idée derrière ce genre de service est de pouvoir "débloquer" sa connexion Internet, c'est-à-dire de pouvoir contourner certaines limitations, comme :

  • accéder à des sites web ou des services autrement indisponibles à cause d'une limitation gouvernementale ou commerciale de son accès Internet ;
  • accéder à des sites web ou des services autrement indisponibles à cause d'une limitation de leur fait (exemple : service accessible uniquement dans certains pays) ;
  • augmenter le niveau d'anonymat de son accès Internet, en ne divulguant pas la véritable adresse IP de sa connexion Internet ;
  • augmenter son niveau de sécurité lorsqu'on se connecte à un réseau Wi-Fi public (gare, café, espace de coworking, ...), en particulier s'il s'agit d'un réseau ouvert (oui, un portail captif compte comme réseau ouvert).

L'offre de service VPN Shellfire se décompose en trois gammes de prix :

  • l'offre gratuite, qui annonce 2 pays de sortie (Allemage et USA), et limite le débit à 1 Mbit/sec ;
  • l'offre Premium, qui annonce 20 pays de sortie, et limite le débit à 12 Mbit/sec ;
  • l'offre PremiumPlus, qui annonce 34 pays de sortie, et ne limite pas le débit.

Je n'utilise que très peu ce genre de service : mon utilisation habituelle d'un VPN consiste surtout à accéder à mon LAN depuis l'extérieur, voire aussi pour me connecter depuis un lieu public. J'ai la chance d'avoir le choix en matière de FAI, et lorsqu'un fournisseur s'est avéré insuffisant sur un point, j'ai pu aller chez un autre.

Le site web

Commençons par le site web de Shellfire, puisque pas mal de manipulations se passent via celui-ci. Je n'ai pas pu tester l'inscription, ni le paiement, puisque tout ceci a été réalisé pour moi dans le cadre du partenariat. J'ai par contre testé la réinitialisation du mot de passe, et cela m'a fait plaisir de ne pas voir celui-ci apparaître en clair dans un mail ! J'ai aussi pu apprécier que le site soit traduit en français (en plus de l'anglais et de l'allemand), et que de la documentation soit accessible au format PDF selon différents OS et différentes technologies de VPN. D'ailleurs l'assistance se trouve très simplement, un lien est disponible en haut des pages du site. Ce lien renvoie vers une foire au questions, contenant entres autres les documents PDF, mais aussi des informations sur la rétention des données, mais aussi sur comment résilier, ce qui semble se faire assez simplement (on se doute que je n'ai pas encore testé). Dernier point rassurant, tout le site est en HTTPS, et obtient une note de A+ au test SSL Labs.

J'ai cependant noté quelques points d'améliorations, à commencer comme le paragraphe précédent par la traduction. Bien que celle-ci soit globalement compréhensible, il y a parfois des formulations qui me semblent être des traductions littérales de l'anglais. Il y a même quelques passages non traduits, comme un titre dans l'un des PDF de documentation, resté en allemand, les impressions d'écrans de ces documentations ou l'icône de téléchargement du client Shellfire VPN, aussi en allemand. Un peu plus gênant je trouve, j'ai voulu au début utiliser le site sans Javascript, et cela a tourné court : le menu permettant d'accéder aux paramètres VPN n'est accessible que via Javascript. J'ai aussi remarqué qu'un mot de passe est affiché en clair dans l'interface web : comme il ne correspond pas au mot de passe de mon compte et qu'aucun mot de passe n'est requis pour OpenVPN, je suppose qu'il s'agit du mot de passe pour PPTP. Toujours dans les points gênants, j'ai eu la mauvaise surprise de voir des widgets Facebook, Twitter et Google Analytics. Si ceux-ci sont clairement annoncés dans la déclaration de confidentialité, je trouve cela assez dommage pour une entreprise qui se vante de vouloir protéger mes données privées de faire savoir à Facebook, Google et Twitter que je visite un site de VPN, sa fréquence et potentiellement plein d'informations.

Le réseau VPN

Passons maintenant au cœur de notre sujet, le VPN en lui-même.

Protocoles et configurations réseau

Il est possible de se connecter au VPN via trois protocoles principaux : PPTP, IPSec et OpenVPN. Je n'ai pas essayé les deux premiers, et me suis concentré sur le troisième. La première chose que j'ai remarquée est le nombre de points de sorties possibles (nommés serveurs sur le site de Shellfire) : 50 au moment où j'écris ces lignes. J'ai aussi remarqué la variété des pays de sortie, couvrant non seulement l'Amérique du nord et l'Europe (incluant l'Europe de l'est), mais aussi l'Asie, l'Amérique du sud et l'Afrique avec un serveur à Johannesburg ! Il est aussi possible de choisir entre TCP et UDP, ce qui selon les cas peut s'avérer utile.

J'ai, là aussi noté quelques points d'amélioration. Tout d'abord, le changement des points de sortie est assez contraignant : il faut se connecter sur le site, puis télécharger la configuration concernant le serveur qui nous intéresse. Cela rend une configuration précédente inopérante, et si comme moi avez plusieurs machines ou appareils, il faut alors déployer cette nouvelle configuration sur ceux-ci. De la même manière, le choix entre TCP et UDP se fait dans l'interface web, et il faut de nouveau télécharger la configuration pour l'appliquer. J'ai aussi remarqué que les serveurs ne sont accessibles que via un seul port. J'aurais trouvé beaucoup plus pratique que plusieurs ports soient disponibles, car cela veut dire que si un serveur VPN n'est pas accessible pour cause de réseau trop restrictif, je devrai choisir un serveur situé dans un autre pays.

Un autre point qui a retenu mon attention est le paramétrage DNS : en effet, OpenVPN applique (ou tente d'appliquer, on en reparlera plus tard) une configuration DNS pour que les requêtes DNS passent dans le VPN. Chez Shellfire, il a été décidé d'utiliser les DNS publics de Google. Je trouve dommage de s'en remettre aux GAFAM pour de nombreuses choses comme le DNS, mais je suis aussi conscient qu'il n'est pas forcément évident de maintenir une infrastructure de résolution DNS en plus du VPN (et des autres services de Shellfire).

Enfin, je n'ai pas vraiment testé un éventuel filtrage de port, mais je n'ai pas eu de soucis en PremiumPlus pour le web, le SSH, ainsi qu'un peu de mail.

Débits

On l'a vu plus tôt, l'offre tarifaire est entre autres segmentée sur les débits. Mais qu'en est-il réellement ? J'ai fait des tests de débit en utilisant plusieurs sites sites spécialisés :

  • speedtest.net ;
  • fast.com ;
  • speedof.me ;
  • megapath.com ;
  • bandwidthplace.com .

Je me suis basé sur 5 niveaux de connexion : un serveur gratuit (USA/Chicago), un Premium (France/Roubaix), deux PremiumPlus (Singapour et Suisse/Zurich), et bien entendu sans VPN. Sans surprise, sans tunnel VPN, j'ai le meilleur débit : j'ai la chance d'être en fibre optique.

Globalement, sur le serveur gratuit, le bridage est présent et je me retrouve bien avec un débit descendant aux alentours d'1 Mbit/s. Là où c'est amusant, c'est que le débit montant (non spécifié par Shellfire) ne semblait pas bridé, selon les tests j'ai eu entre 4 et 13 Mbit/s.

Le bridage est aussi bien présent sur le serveur Premium, avec selon les tests un débit descendant situé entre 11 et 13 Mbit/s. Comme pour le serveur de l'offre gratuite, le débit montant est bien plus important, entre 19 et 25 Mbit/s.

Alors, comment se comportent les serveurs PremiumPlus, soit disant "sans limite de débit" ? Et bien cela dépend des cas, c'est pour cela que j'en ai testé deux. Le serveur suisse, situé à Zurich, m'a fourni un débit descendant entre 18 et 26 Mbit/s selon les tests, mais j'ai eu entre 15 et 22 Mbit/s sur le débit montant. Je me serais attendu à plus au vu des serveurs moins chers. L'autre cas est un serveur situé à Singapour, qui m'a offert une toute autre expérience : entre 1,6 et 5,25 Mbit/s de débit descendant et entre 3 et 7 Mbit/s pour le débit montant, ce qui le situerait entre un serveur gratuit et un serveur Premium.

Côté latence, les pings ne sont pas corrélés avec l'offre tarifaire, mais plutôt avec ma distance du serveur. Ainsi je ne perds que peu de latence en restant en France (quelques millisecondes), mais je suis monté à plus de 250 ms en utilisant le serveur situé à Singapour, ce qui est somme toute assez logique.

Que conclure de tout cela ? D'abord, qu'il est tout simplement impensable de vouloir jouer au travers d'un VPN, mais je suppose qu'on ne m'a pas attendu pour ce constat. Ensuite, que globalement sur les offres gratuites et Premium, les débits descendant sont respectés, et bonne surprise, que les débits montant sont assez confortables pour envoyer des fichiers un peu volumineux. Pour l'offre PremiumPlus, l'absence de limite contractuelle laisserait à penser que de gigantesques tuyaux sont à disposition, mais en fait le débit dépend plutôt de ce qui est disponible sur place : un VPS ou un serveur dédié pour monter un VPN coûte peu cher en Europe, particulièrement en France et en Allemagne par exemple, mais peut coûter bien plus cher ailleurs.

Chiffrement

Selon les serveurs, le chiffrement n'est pas le même, il y a trois possibilités :

  • AES-128-CBC pour les serveurs de l'offre gratuite ;
  • AES-192-CBC pour les serveurs de l'offre Premium ;
  • et enfin AES-256 pour les serveurs de l'offre PremiumPlus.

D'un côté, je comprends tout à fait cette différence sur les gammes de prix, d'autant que d'après Wikipédia (et ici en anglais), même AES-128 est sûr. Toutefois, les attaques sur celui-ci deviennent de plus en plus nombreuses, même si elles sont de type "canal auxiliaire", c'est-à-dire qu'elles exploitent surtout des implémentations que l'algorithme en lui-même. Il est donc important de rester informé sur le sujet, en particulier si on se limite aux serveurs gratuits.

Géolocalisation

Je n'ai pas fait beaucoup de tests à ce niveau, si ce n'est m'assurer à l'aide d'un service whois que l'adresse IP de sortie (qui est celle du serveur) est bien géolocalisée dans le pays indiqué. Je me suis d'ailleurs fait une petite frayeur, puisque sur une base whois ancienne, l'IP de sortie de Singapour était géolocalisée en Nouvelle-Zélande !

Pour ce qui est des blocages géographiques par contre, mon test s'est limité à Netflix France, malheureusement bloqué sur l'IP de sortie française.

L'assistance

Durant mon test du VPN Shellfire, j'ai eu un problème, ce qui fut l'occasion parfaite pour tester le support technique. Celui-ci n'est disponible que par mail, mais répond dans un français excellent, et m'a toujours répondu en moins de 24h. Quand à l'utilité des réponses du support, si celles-ci n'étaient pas parfaites, elles m'ont mises sur la voie pour comprendre ce qui n'allait pas.

En conclusion

Le service qu'offre Shellfire dispose d'une base solide, avec une connexion réseau de qualité. Mais cela ne fait pas tout, et je trouve dommage que cette société succombe aux sirènes de la facilité en utilisant sans chercher plus loin des serveurs DNS, widgets de réseaux sociaux et outils de statistiques qui vont à l'encontre de son objectif d'anonymat. Je crois aussi que le service gagnerait à être plus pratique (changement de serveur, protocole et port).

Je ne déconseille donc pas Shellfire, mais ne le recommande que sous les conditions suivantes :

  • penser à bloquer les connexions vers les réseaux sociaux et Google dans le navigateur ;
  • modifier les serveurs DNS paramétrés par le VPN, en les remplaçant par les DNS publics FDN par exemple.

Si jamais vous avez apprécié ce test et que vous souhaitez essayer leur service (et pourquoi pas comparer les impressions), des liens de parrainage/affiliation existent, voici le mien : ici.

Vous avez aimé cet article ? Alors partagez-le sur les réseaux sociaux !

Crédit photo : Tom Roberts - Purple Rain.

31 mai 2017

Wordpress : étude d'un site web victime de piratage

A Needle in a Hay StackDurant le mois de février 2017, j'ai été sollicité pour analyser et "nettoyer" un site web piraté. Le site web en question fonctionnait sous Wordpress. Je partage mon expérience ici, en espérant que cela aide certaines personnes plus tard. J'ai choisi de ne pas mentionner le nom du site ou de son webmestre, si des commentaires venaient à divulguer ces informations, je me permettrai de les éditer ou de les refuser.

Mais avant de continuer, une mise en garde : en cas de piratage avéré de votre site, l'option la plus sûre reste de tout effacer, de restaurer des sauvegardes et de mettre à jour votre CMS ainsi que ses plugins ! Malheureusement, tout le monde ne fait pas de sauvegarde, et se retrouve parfois, selon l'hébergeur, avec un site web hors ligne le temps que le "nettoyage" soit fait.

Ah, et au passage : les adresses IP et noms de domaines ont été anonymisés. Si jamais il y a un oubli, faites-le moi savoir, et je corrigerai au plus vite !

Première étape : l'inventaire

J'ai commencé par faire un rapide inventaire de ce que je pouvais récupérer : le webmestre du site m'a aimablement fourni les accès à son hébergement et à son site, de sorte que je puisse effectuer sans difficulté toutes les actions dont j'aurais besoin. Je récupère donc les éléments suivants :

  • logs d'accès sur environ 16 jours ;
  • copie complète des fichiers sur l'hébergement ;
  • export de la base de données.

Première recherche dans les logs

Etudions donc ces logs, et voyons ce qui peut en ressortir. Je regarde d'abord le nombre de lignes de chaque fichier (la rotation semble se faire de manière quotidienne) :

13:19 nils@shell2:~/tmp/exemple/access_logs$ wc -l *.log
   29771 exemple.fr-03-02-2017.log
   11377 exemple.fr-04-02-2017.log
   12504 exemple.fr-05-02-2017.log
   12279 exemple.fr-06-02-2017.log
    9700 exemple.fr-07-02-2017.log
    6182 exemple.fr-08-02-2017.log
   11819 exemple.fr-09-02-2017.log
   11918 exemple.fr-10-02-2017.log
   19616 exemple.fr-11-02-2017.log
   15377 exemple.fr-12-02-2017.log
   11232 exemple.fr-13-02-2017.log
    8253 exemple.fr-14-02-2017.log
    6791 exemple.fr-15-02-2017.log
   13711 exemple.fr-16-02-2017.log
   23480 exemple.fr-17-02-2017.log
   16602 exemple.fr-18-02-2017.log
  220612 total

Trois jours deviennent intéressant, le premier avec 29771 requêtes, un autre 19616, et enfin un dernier à 23480. Je vais donc rechercher dans ces logs, des requêtes étranges, en particulier beaucoup de requêtes POST vers une ou plusieurs pages précises, qui ne semblent pas faire partie du site. Je suis content d'avoir retenu quelque chose de mon expérience précédente.

Pour aller voir si quelque chose ressort des requêtes POST, je réutilise les one-liners awk dont j'avais parlé ici et . Voyons donc ce qui effectue le plus de requêtes POST dans le premier log :

nils@shell2:~/tmp/exemple/access_logs$ grep POST exemple.fr-03-02-2017.log | grep -v "POST /wp-cron.php" | awk '{frequencies[$1]++;} END {for (ip in frequencies) printf "%d\t%s\n" , frequencies[ip] , ip;}' | sort -rn | head -5
98      10.105.31.167
46      10.169.249.134
13      10.0.164.52
11      10.186.33.40
7       10.43.0.21

En effectuant des résolutions DNS inverses et des whois des adresses IP, je retrouve entre autres le FAI du webmestre, ainsi que le cluster de l'hébergeur. Mais je trouve aussi une adresse IP allemande, une autre tchétchène, et une russe. Surprenant pour un blog francophone, n'est-ce pas ? Bon, avant d'être accusé de racisme, allons voir ce que ces adresses IP ont fait comme requêtes. Extrait :

10.0.164.52 www.exemple.fr - [03/Feb/2017:14:56:17 +0100] "POST /wp-content/mybkl.php HTTP/1.1" 200 11 "-" "Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; MAARJS; rv:11.0) like Gecko"
10.0.164.52 www.exemple.fr - [03/Feb/2017:16:25:19 +0100] "POST /wp-content/mbsrd.php HTTP/1.1" 200 11 "-" "Mozilla/5.0 (iPhone; CPU iPhone OS 9_3_2 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Mobile/13F69"
10.0.164.52 www.exemple.fr - [03/Feb/2017:17:41:16 +0100] "POST /wp-content/vipmpnjen.php HTTP/1.1" 200 11 "-" "Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36"
10.0.164.52 www.exemple.fr - [03/Feb/2017:18:06:57 +0100] "POST /wp-content/bvcomjjaf.php HTTP/1.1" 200 11 "-" "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; Trident/7.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E; Tablet PC 2.0)"
10.0.164.52 www.exemple.fr - [03/Feb/2017:18:10:18 +0100] "POST /wp-content/mmgi.php HTTP/1.1" 200 11 "-" "Mozilla/5.0 (Windows NT 5.1; rv:50.0) Gecko/20100101 Firefox/50.0"
10.0.164.52 www.exemple.fr - [03/Feb/2017:18:24:07 +0100] "POST /wp-content/gruk.php HTTP/1.1" 404 56215 "-" "Mozilla/5.0 (iPad; CPU OS 9_2_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13D15 Safari/601.1"
10.0.164.52 www.exemple.fr - [03/Feb/2017:18:24:11 +0100] "POST /wp-content/fkjzhl.php HTTP/1.1" 200 11 "-" "Mozilla/5.0 (Linux; Android 6.0.1; SM-N920V Build/MMB29K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.124 Mobile Safari/537.36"
10.0.164.52 www.exemple.fr - [03/Feb/2017:18:32:00 +0100] "POST /wp-content/ssauz.php HTTP/1.1" 200 11 "-" "Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko"
10.0.164.52 www.exemple.fr - [03/Feb/2017:20:19:02 +0100] "POST /wp-content/zpamh.php HTTP/1.1" 200 11 "-" "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.143 Safari/537.36"
10.0.164.52 www.exemple.fr - [03/Feb/2017:20:47:29 +0100] "POST /wp-content/bvcomjjaf.php HTTP/1.1" 200 11 "-" "Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko"
10.0.164.52 www.exemple.fr - [03/Feb/2017:20:48:09 +0100] "POST /wp-content/ssauz.php HTTP/1.1" 200 11 "-" "Mozilla/5.0 (iPad; CPU OS 10_0_1 like Mac OS X) AppleWebKit/602.1.50 (KHTML, like Gecko) Version/10.0 Mobile/14A403 Safari/602.1"
10.0.164.52 www.exemple.fr - [03/Feb/2017:20:50:33 +0100] "POST /wp-content/vgmq.php HTTP/1.1" 200 11 "-" "Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko"
10.0.164.52 www.exemple.fr - [03/Feb/2017:21:09:03 +0100] "POST /wp-content/pwemtiqeb.php HTTP/1.1" 200 11 "-" "Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; Touch; ASU2JS; rv:11.0) like Gecko"

Je récupère alors une archive propre de Wordpress depuis le site officiel, par acquis de conscience, mais personnellement, fkjzhl.php ou pwemtiqeb.php ça me semble louche comme nom de fichier. Comme prévu, ces fichiers n'ont absolument rien d'officiel, et les plugins Wordpress ne s'installent pas dans /wp-content/.

A ce moment-là, ma conclusion est la suivante : l'intrus (en supposant qu'il soit seul) a déposé une multitude de fichiers un peu partout dans l'arborescence afin de rendre plus difficile un éventuel nettoyage. De plus, en accédant à plusieurs fichiers, depuis plusieurs adresses IP différentes, cela noie les requêtes dans la masse et rend là aussi, la détection plus difficile.

Même joueur joue encore

Passons au deuxième fichier, en utilisant la même méthode :

nils@shell2:~/tmp/exemple/access_logs$ grep POST exemple.fr-17-02-2017.log | grep -v "POST /wp-cron.php" | awk '{frequencies[$1]++;} END {for (ip in frequencies) printf "%d\t%s\n" , frequencies[ip] , ip;}' | sort -rn | head -5
10143   10.9.129.250
240     10.28.47.221
234     10.135.219.59
229     10.213.224.115
199     10.123.209.172

Là aussi, la géolocalisation est assez variée : Allemagne, Hong-Kong, Pologne, Russie, Lituanie. Jetons alors un œil aux requêtes POST les plus visitées :

grep POST exemple.fr-17-02-2017.log | awk '{frequencies[$7]++;} END {for (field in frequencies) printf "%s\t%d\n" , field , frequencies[field];}' | sort -nr -k 2,2 | grep -v "/wp-cron.php"
/hostdata4.php  11299
/wp-includes/js/tinymce/dir.php 1211
/xmlrpc.php     240
/wp-content/from.php    75
/wp-content/common.php  39
/wp-login.php   8
/wp-content/db_model.php        7
/wp-content/rss_feeder.class.php        4
/wp-includes/customize/db2.php  3
/wp-admin/network/plugin-editor.php     3
/wp-includes/js/tinymce/f53585.php      3
/wp-includes/Requests/Exception/HTTP/431.php    3
/wp-content/tongue_lib.php      2
/palaute.php    2
/addfavorites.php       2
/wp-content/uploads/2015/07/lib.php     2
/ranking.php    2
/confirmorder.php       2
/wp-content/press_lib.php       2
/wp-json/wp/v2/posts/5529       1
/wp-content/adodb.class.php?test_url=true       1
/e28441e709.php?test_url=true   1
/index.php/wp-json/wp/v2/posts/5529     1
/wp-content/index.php   1
/wp-content/powerful.inc.php    1
/wp-content/991e700dbd.html     1
/       1
/xmlrpc.php?for=jetpack&token=anonymized      1

Pas mal de fichiers me semblent bizarres, et on peut s'amuser à aller les recherche dans l'archive "saine" de Wordpress. Spoiler Alert : il n'y sont pas.

Prenons un peu de hauteur

Avant de passer à autre chose, j'ai décidé de regarder les requêtes POST les plus visitées sur la totalité des fichiers (en retirant un peu plus de requêtes "classiques") :

nils@shell2:~/tmp/exemple/access_logs$ for i in $(find . -type f -print | sort); do cat $i >> ../exemple.fr-global.log; done
nils@shell2:~/tmp/exemple/access_logs$ grep POST ../exemple.fr-global.log | grep -v "/wp-cron.php\|/wp-login.php\|/xmlrpc.php" | awk '{frequencies[$7]++;} END {for (field in frequencies) printf "%s\t%d\n" , field , frequencies[field];}' | sort -nr -k 2,2 | head -50
/hostdata4.php  24322
/wp-includes/js/tinymce/dir.php 10057
/_index.php     1373
/wp-admin/admin-ajax.php        762
/wp-content/izodltnu.php        753
/wp-content/vgmq.php    705
/wp-content/zpamh.php   699
/wp-content/fkjzhl.php  654
/wp-content/ulimzaggf.php       645
/wp-content/jbrv.php    642
/wp-content/omfxde.php  629
/wp-content/ohvvdgk.php 625
/wp-content/pwemtiqeb.php       614
/wp-content/mmgi.php    613
/wp-content/nrzekbal.php        609
/wp-content/bvcomjjaf.php       605
/wp-content/yuflla.php  601
/wp-content/mybkl.php   598
/wp-content/mbsrd.php   594
/wp-content/vipmpnjen.php       578
/wp-content/ssauz.php   542
/wp-content/common.php  378
/wp-content/gruk.php    313
/wp-content/db_model.php        133
/wp-content/nwjtirmy.php        96
/wp-content/nrkg/wzvzrtnqh.php  95
/wp-content/iari.php    89
/wp-content/from.php    77
/wp-admin/admin.php?page=stats&noheader&chart=flot-stats-data   72
/wp-admin/admin-ajax.php?action=wp_ewwwio_async_optimize_media&nonce=c8aa5f3464 24
/wp-content/plugins/thank-me-later/lib/start37.php      23
/wp-admin/admin-ajax.php?action=wordfence_testAjax      23
/wp-content/uploads/2015/07/lib.php     19
/wp-includes/Requests/Exception/HTTP/431.php    19
/wp-includes/customize/db2.php  17
/wp-includes/js/tinymce/f53585.php      13
/wp-admin/meta/output.php       12
/post.php       11
/wp-admin/post.php      9
/wp-admin/network/plugin-editor.php     9
/wp-admin/admin-ajax.php?action=wp_ewwwio_async_optimize_media&nonce=50967874b9 8
/       7
/wp-comments-post.php?for=jetpack       6
/wp-content/egatl/wnxavysc.php  5
/3cdb6f452a.php?test_url=true   5
/wp-content/sad.func.php        4
/addfavorites.php       4
/palaute.php    4
/wp-content/rss_feeder.class.php        4
/wp-content/hook-filters.php    4

Afin d'éviter que ce blog tire en longueur juste pour des lignes de requête POST, je me suis limité au 50 premières lignes. Rien de plus que dans les autres recherche, on note comme avant que l'intrus a pris ses aises dans les répertoires /wp-content/ et /wp-includes/. L'intérêt de faire cette recherche est de faire remonter certaines requêtes qui seraient passées sous les radars si j'avais continué fichier par fichier.

Résultat des courses

J'ai donc repéré comme ça un certain nombre de fichiers qui n'ont rien à voir avec le contenu réel du blog, et que je vais pouvoir supprimer sans regret. Cela n'est hélas pas suffisant, car rien n'empêche un intrus d'insérer des fichiers qui ne sont presque pas accédés. Dans un prochain billet, j'espère donc comparer au niveau fichier l'export de ce blog avec une archive saine.

Vous avez aimé cet article ? Alors partagez-le sur les réseaux sociaux ! Si en plus vous avez des remarques, ou des propositions d'améliorations, n'hésitez pas : les commentaires sont là pour ça !

Crédit photo : Michael Gil - A Needle in a Hay Stack.

18 avr. 2017

PHP Malware Finder : gestion des listes blanches

Lime & List

Rappelez-vous : dans le billet précédent, nous avons découvert PHP Malware Finder. Aujourd'hui, allons plus loin et passons à la gestion des listes blanches ! Pour cela, prenons l'exemple d'un blog fonctionnant sous Wordpress. Cette gestion des listes blanches se fera en trois étapes :

  • tout d'abord, il s'agit de faire un état de l'existant, en exécutant PHP Malware Finder et en observant des faux-positifs ;
  • ensuite, la deuxième étape sera de comprendre le fonctionnement des règles et des listes blanches ;
  • enfin, la troisième étape consistera à générer la liste blanche et à l'intégrer dans les règles existantes.

Et pour finir, en bonus, un script de génération de liste blanche sera abordé. Au moment de l'écriture de cet article, la dernière version de Wordpress est la 4.7.3. La dernière version de PHP Malware Finder est la 0.3.4.

Exécution de PHP Malware Finder sur un site de test

Nous allons donc commencer par récupérer une archive de Wordpress directement sur le site officiel, afin de s'assurer qu'elle est saine :

nils@Dalaran:~/tmp$ wget https://wordpress.org/wordpress-4.7.3.tar.gz
--2017-04-11 21:54:31--  https://wordpress.org/wordpress-4.7.3.tar.gz
Résolution de wordpress.org… 66.155.40.250, 66.155.40.249
Connexion à wordpress.org|66.155.40.250|:443… connecté.
requête HTTP transmise, en attente de la réponse… 200 OK
Taille : 8008833 (7,6M) [application/octet-stream]
Sauvegarde en : « wordpress-4.7.3.tar.gz »

wordpress-4.7.3.tar.gz                                      100%[=========================================================================================================================================>]   7,64M  3,27MB/s    ds 2,3s    

2017-04-11 21:54:34 (3,27 MB/s) — « wordpress-4.7.3.tar.gz » sauvegardé [8008833/8008833]

nils@Dalaran:~/tmp$ tar -xzf wordpress-4.7.3.tar.gz

Puis, décompressons-la et scannons son contenu :

nils@Dalaran:~/tmp$ cd wordpress
nils@Dalaran:~/tmp/wordpress$ phpmalwarefinder .
ObfuscatedPhp ./wp-admin/includes/class-ftp.php
DodgyStrings ./wp-admin/includes/ajax-actions.php
ObfuscatedPhp ./wp-admin/includes/class-wp-plugins-list-table.php
DodgyPhp ./wp-admin/includes/schema.php
ObfuscatedPhp ./wp-admin/includes/media.php
DodgyStrings ./wp-admin/includes/template.php
ObfuscatedPhp ./wp-admin/includes/template.php
DodgyStrings ./wp-admin/includes/upgrade.php
ObfuscatedPhp ./wp-includes/bookmark-template.php
DodgyPhp ./wp-includes/class-pop3.php
DodgyStrings ./wp-includes/class-phpmailer.php
DodgyPhp ./wp-includes/class-phpmailer.php
ObfuscatedPhp ./wp-includes/class-wp-meta-query.php
ObfuscatedPhp ./wp-includes/class-wp-tax-query.php
DodgyStrings ./wp-includes/class-wp-query.php
DodgyStrings ./wp-includes/comment.php
ObfuscatedPhp ./wp-includes/date.php
DodgyStrings ./wp-includes/deprecated.php
ObfuscatedPhp ./wp-includes/deprecated.php
DodgyStrings ./wp-includes/functions.php
DodgyPhp ./wp-includes/functions.php
DangerousPhp ./wp-includes/functions.php
DodgyStrings ./wp-includes/formatting.php
ObfuscatedPhp ./wp-includes/IXR/class-IXR-date.php
DodgyPhp ./wp-includes/load.php
DodgyStrings ./wp-includes/media.php
ObfuscatedPhp ./wp-includes/post-template.php
ObfuscatedPhp ./wp-includes/js/tinymce/tinymce.min.js
DodgyStrings ./wp-includes/post.php
ObfuscatedPhp ./wp-includes/SimplePie/Parse/Date.php

Certains fichiers sont présentés comme malveillants, mais il n'en est rien : ce sont donc des faux-positifs. Il faut donc les mettre en liste blanche, mais avant, il convient de comprendre où sont les signatures et cette liste blanche.

Signatures et listes blanches

PHP Malware Finder est basé sur YARA, un outil qui recherche des fichiers selon certains critères, comme la présence d'une chaîne de caractères, ou une expression rationnelle. Les signatures sont définies dans les fichiers asp.yar, common.yar et php.yar. On comprend alors qu'un fichier est dédié aux fichiers ASP, un autre aux fichiers PHP, et le troisième regroupe des signatures communes aux deux langages.

Passons ensuite à la lecture des fichiers asp.yar et php.yar : on voit assez vite que pour qu'un fichier soit reconnu comme malveillant, il doit non seulement remplir certaines conditions (les règles définies), mais il doit aussi ne pas remplir les conditions des fichiers de liste blanche.

En fait, les fichiers de liste blanche sontdes sommes de contrôle SHA1 de la taille des fichiers considérés comme faux-positifs. Allons maintenant à la création du fichier contenant ces informations !

Création du fichier de liste blanche

Pour ajouter nos faux-positifs en liste blanche, nous allons avoir besoin de deux choses :

  • d'abord, python-yara, une bibliothèque qui permet d'accéder à yara depuis Python ;
  • ensuite un script, generate_whitelist.py, qui se trouve être écrit... en Python.

Ce script est disponible dans le répertoire utils de PHP Malware Finder, ou bien dans ${PREFIX}/share/php-malware-finder/utils/ s'il est installé depuis pkgsrc (${PREFIX} dépendant de l'installation de pkgsrc).

Testons donc ce script sur notre répertoire wordpress :

nils@Dalaran:~/tmp/php-malware-finder/php-malware-finder/utils$ /opt/pkg/bin/python2.7 ./generate_whitelist.py ma_liste_blanche ~/tmp/wordpress

Le résultat est alors ressemblant à celui-ci :

import "hash"

private rule maListeblanche
{
    condition:
        /* maListeblanche */
        hash.sha1(0, filesize) == "12a18329072bed94b6f9c4d9f16d7a079ca64655" or // /Users/nils/tmp/wordpress/wp-admin/includes/ajax-actions.php
        hash.sha1(0, filesize) == "6bccf04c8b46c8d6cdf79db8b509f4b76689f3bf" or // /Users/nils/tmp/wordpress/wp-admin/includes/class-ftp.php
        hash.sha1(0, filesize) == "aa6a12a0325056b9649f58f8072fa02a1e264551" or // /Users/nils/tmp/wordpress/wp-admin/includes/class-wp-plugins-list-table.php
        hash.sha1(0, filesize) == "3e73204644f0ce7b0971aad885fdcbcabba629fc" or // /Users/nils/tmp/wordpress/wp-admin/includes/media.php
        hash.sha1(0, filesize) == "81b1ae432ba765a43c6d81fb6d6c35ce72efd0e8" or // /Users/nils/tmp/wordpress/wp-admin/includes/schema.php
        hash.sha1(0, filesize) == "2ef50e790fdd42daa8ccd64d4c7c4be75d21742d" or // /Users/nils/tmp/wordpress/wp-admin/includes/template.php
        hash.sha1(0, filesize) == "9835d10a7561deeef1f8381da065b4b45d7f2662" or // /Users/nils/tmp/wordpress/wp-admin/includes/upgrade.php
        hash.sha1(0, filesize) == "b92aefa2917fc319ca7ceab092e183cafc651a6d" or // /Users/nils/tmp/wordpress/wp-includes/bookmark-template.php
        hash.sha1(0, filesize) == "cb0c5a355409d807202bbf52749a3e74a9967a6a" or // /Users/nils/tmp/wordpress/wp-includes/class-phpmailer.php
        hash.sha1(0, filesize) == "e4f0694bc96f99d5e30201171a3e7fc86e9e5ae4" or // /Users/nils/tmp/wordpress/wp-includes/class-pop3.php
        hash.sha1(0, filesize) == "c06a15f4869c5459a782b714572eacea5c82d570" or // /Users/nils/tmp/wordpress/wp-includes/class-wp-meta-query.php
        hash.sha1(0, filesize) == "72dbc1d4f2bbc8efdcdd834ecaf3771cbf17f64e" or // /Users/nils/tmp/wordpress/wp-includes/class-wp-query.php
        hash.sha1(0, filesize) == "348c3a60d99768041be690b65b008628f53badb7" or // /Users/nils/tmp/wordpress/wp-includes/class-wp-tax-query.php
        hash.sha1(0, filesize) == "0aab95245b9668f954151f4312b678fb0ee798cf" or // /Users/nils/tmp/wordpress/wp-includes/comment.php
        hash.sha1(0, filesize) == "c8c9182aa25fb92ca91fcc96c3419847acdcf6e0" or // /Users/nils/tmp/wordpress/wp-includes/date.php
        hash.sha1(0, filesize) == "5877695771fbe7a5667f4a06f4d897a37ef3fceb" or // /Users/nils/tmp/wordpress/wp-includes/deprecated.php
        hash.sha1(0, filesize) == "806d2872676ea22e0a6fa6b32fbd4652298023ee" or // /Users/nils/tmp/wordpress/wp-includes/formatting.php
        hash.sha1(0, filesize) == "3083b9a58e76d42455935811a457f29f57620145" or // /Users/nils/tmp/wordpress/wp-includes/functions.php
        hash.sha1(0, filesize) == "f53f80c4ee7446f0b605443b6d2f05acd8064d13" or // /Users/nils/tmp/wordpress/wp-includes/load.php
        hash.sha1(0, filesize) == "bea5ea598f537e7acb20b77a1421f819c0a9ec75" or // /Users/nils/tmp/wordpress/wp-includes/media.php
        hash.sha1(0, filesize) == "abcf1a0801694db4774cd2abb29b5392e10dd632" or // /Users/nils/tmp/wordpress/wp-includes/post-template.php
        hash.sha1(0, filesize) == "5ddc1e5c5c6302211b1aecbf930f76417b65d678" or // /Users/nils/tmp/wordpress/wp-includes/post.php
        hash.sha1(0, filesize) == "040ef40d245242723de200e494a27545ea0b121b" or // /Users/nils/tmp/wordpress/wp-includes/IXR/class-IXR-date.php
        hash.sha1(0, filesize) == "086986cdf03ede58494034661d38c4842af38fe3"    // /Users/nils/tmp/wordpress/wp-includes/SimplePie/Parse/Date.php
}

Il faut ensuite ajouter la règle générée à l'instant aux listes blanches déjà présentes. Pour aller vite, on peut directement éditer le fichier whitelist.yar et ajouter notre règle juste avant la dernière (IsWhitelisted). Il ne faut donc pas oublier d'ajouter dans cette dernière règle celle qu'on vient de créer. Dans mon cas, la dernière règle ressemble à cela :

private rule IsWhitelisted
{
    condition:
        Symfony or
        Wordpress or
        Prestashop or
        Magento or
        Magento2 or
        Drupal or
        Roundcube or
        Concrete5 or
        Dotclear or
        Owncloud or
        Phpmyadmin or
        Misc or
        maListeblanche
}

Vérifions maintenant que la liste blanche est bien à jour et assurons-nous qu'il n'y a plus de faux-positif dans notre répertoire :

nils@Dalaran:~/tmp/wordpress$ phpmalwarefinder ./
ObfuscatedPhp .//wp-includes/js/tinymce/tinymce.min.js

Et là, c'est le drame. Mais pourquoi ? En fait, c'est parce que le script utilisé à l'instant ne prend en compte que les fichiers PHP. Cela peut être une idée d'amélioration pour une version future.

Création facile de liste blanche pour divers logiciels PHP

Il existe un autre script fort utile : mass_whitelist.py. Son but est de faciliter la création de liste blanche pour des applications PHP connues, cela va de Wordpress à Drupal en passant par PHPMyAdmin. Il suffit de lui donner en argument le nom de l'application, l'URL de téléchargement (en remplaçant le numéro de version avec version), ainsi que les numéros de version mineurs et majeurs à rechercher.

Ce script va alors rechercher toutes les versions de l'application, les télécharger, et afficher une liste blanche les prenant toutes en compte. Par exemple, pour Wordpress :

nils@Dalaran:~/tmp/php-malware-finder/php-malware-finder/utils$ /opt/pkg/bin/python2.7 mass_whitelist.py wordpress https://wordpress.org/wordpress-__version__.tar.gz 4 7 3 | tee -a wordpress.yar

Le fichier de résultat se nomme donc wordpress.yar. Il suffira alors de le copier dans le répertoire de règles (et écraser le précédent) afin de le prendre en compte. Attention, car ce script est long, très long !

Vous avez aimé cet article ? Alors partagez-le sur les réseaux sociaux ! Si en plus vous avez des remarques, ou des propositions d'améliorations, n'hésitez pas : les commentaires sont là pour ça !

Crédit Photo : List_84 - Lime & List

11 avr. 2017

PHP Malware Finder : détecteur d'intrusion sur site PHP

Dans ma loupe - 2Lors des RMLL de Beauvais en 2015, j'ai eu l'occasion de voir une conférence présentant au passage un outil fort sympathique nommé PHP Malware Finder (site web). Le but de ce script est de détecter du code PHP qui semble obfusqué ou malveillant, voire même des fonctions trouvées généralement dans des malwares ou des webshells. On trouve, sur la page du projet, une liste (non exhaustive) des malwares qu'il est capable de trouver.

PHP Malware Finder scanne un répertoire pour trouver les malwares, on peut donc choisir de l'utiliser directement sur son serveur, soit depuis son ordinateur en ayant au préalable copié les fichiers de son site. La première option nécessitera les droits administrateurs pour les dépendances, il vaut mieux donc choisir la deuxième option pour la découverte de cet outil, et aussi parce qu'il est assez gourmand en accès disque.

Installer PHP Malware Finder

Son installation et utilisation sont simples, pourvu que YARA soit installé. Sur une distrbution Linux classique, une fois YARA installé, il suffit d'installer PHP Malware Finder en clonant le dépôt Github :

git clone https://github.com/nbs-system/php-malware-finder.git

Le script phpmalwarefinder se trouve dans le répertoire php-malware-finder/php-malware-finder/.

Il est par contre possible, sur un système NetBSD ou macOS, de l'installer facilement via pkgsrc-wip :

cd /usr/pkgsrc/wip/
make package-install

Avec cette manière, PHP Malware Finder est disponible directement dans $PATH :)

Utilisation

Il suffit maintenant de le lancer en spécifiant un endroit où il y a des pages PHP :

$ phpmalwarefinder /chemin/vers/ses/pages/

Si certains fichiers semblent réagir aux signatures, alors le script affichera le type de problème ainsi que le chemin du fichier. Sinon, il n'affiche rien. Bien sûr, d'autres options sont disponibles, et un résumé de celles-ci est disponible via l'option -h :

$ phpmalwarefinder -h
Usage phpmalwarefinder [-cfhtvl] <file|folder> ...
-c  Optional path to a configuration file
-f  Fast mode
-h  Show this help message
-t  Specify the number of threads to use (8 by default)
-v  Verbose mode
-l  Set language ('asp', 'php')
-L  Check long lines
-u  update rules

Par défaut, PHP Malware Finder va chercher ses signatures dans son répertoire (si utilisé depuis un clone du dépôt git), mais le paquet pkgsrc va les chercher dans /usr/pkg/etc/phpmalwarefinder/ (ou /opt/pkg/ pour macOS). Il est possible de préciser ses propres signatures via l'option -c.

D'autres options sont aussi très intéressantes, comme -f (pour accélérer le scan), ou -t qui permet de limiter le nombre de threads à utiliser en parallèle. Cela peut s'avérer très pratique dans le cas où le scan prend du temps et on veut continuer à utiliser sa machine pendant ce temps. Au passage, une recommandation : il vaut mieux éviter de lancer PHP Malware Finder directement sur son serveur, il a tendance à être assez gourmand en ressources !

Si vous aimez cet article, partagez-le sur les réseaux sociaux. Si vous avez des remarques, ou des propositions d'améliorations, n'hésitez pas : les commentaires sont là pour ça !

Crédit photo : Olivier Penet - Dans ma loupe-2

20 mar. 2017

Nmap : détection et récupération d'information sur Wordpress

Début mars 2017, le moteur de blog Wordpress a fait l'objet d'une nouvelle mise à jour, la version 4.7.3. Cette mise à jour revêt une certaine importance, puisqu'elle corrige 5 vulnérabilités !

Vérifier sur une installation de Wordpress que la dernière version est installée est assez simple à partir du moment où on peut se connecter à l'interface d'administration. Il se peut toutefois que cela ne soit pas envisageable : nombre d'installations, disponibilité des identifiants de connexion (pour des raisons d'organisation). Du fait de la verbosité par défaut de Wordpress, il est possible d'obtenir des informations sans posséder d'identifiants de connexion.

Pour réaliser cette vérification, faisons de nouveau appel à Nmap ! En effet, grâce à la disponibilité du langage de script NSE, il est possible de chercher plusieurs informations, comme la version. De manière générale, on peut voir la liste des scripts officiellement disponibles sur une page dédiée. Dans le cas qui nous intéresse, on notera la présence d'un dépôt contenant des scripts NSE personnalisés concernant Wordpress.

L'installation de scripts NSE dans Nmap est assez facile, il suffit de localiser les scripts existant et de copier les siens au même endroit. Par exemple, sur une Fedora 25 :

[nils@fedora-workstation ~]$ git clone https://github.com/peter-hackertarget/nmap-nse-scripts.git
Clonage dans 'nmap-nse-scripts'...
remote: Counting objects: 12, done.
remote: Total 12 (delta 0), reused 0 (delta 0), pack-reused 12
Dépaquetage des objets: 100% (12/12), fait.
Vérification de la connectivité... fait.
[nils@fedora-workstation ~]$ cd nmap-nse-scripts/
[nils@fedora-workstation nmap-nse-scripts]$ sudo cp -v wp-themes.lst /usr/share/nmap/nselib/
'wp-themes.lst' -> '/usr/share/nmap/nselib/wp-themes.lst'
[nils@fedora-workstation nmap-nse-scripts]$ sudo cp -v *.nse /usr/share/nmap/scripts/
'hostmap-hackertarget.nse' -> '/usr/share/nmap/scripts/hostmap-hackertarget.nse'
'http-wordpress-info.nse' -> '/usr/share/nmap/scripts/http-wordpress-info.nse'
'http-wordpress-plugins.nse' -> '/usr/share/nmap/scripts/http-wordpress-plugins.nse'
'http-wordpress-themes.nse' -> '/usr/share/nmap/scripts/http-wordpress-themes.nse'

Lançons alors un premier scan :

[nils@fedora-workstation ~]$ sudo nmap -sV -p80,443 --script http-wordpress-info exemple.fr

Starting Nmap 7.40 ( https://nmap.org ) at 2017-03-20 09:46 CET
Nmap scan report for exemple.fr (10.172.46.128)
Host is up (0.0056s latency).
rDNS record for 10.172.46.128: www.exemple.fr
PORT    STATE SERVICE VERSION
80/tcp  open  http    Apache httpd
|_http-server-header: Apache
| http-wordpress-info: 
|   version: WordPress 4.7.3
|_  theme: twentyseventeen
443/tcp open  ssl/ssl Apache httpd (SSL-only mode)
|_http-server-header: Apache
| http-wordpress-info: 
|   version: WordPress 4.7.3
|_  theme: twentyseventeen

On dispose donc de la version de Wordpress, et du thème utilisé. Il est possible, grâce au script http-wordpress-themes, de chercher plus en profondeurs d'éventuels thèmes supplémentaires installés. Quant à http-wordpress-plugins, il permet de rechercher des plugins. Le script d'information est néanmoins assez verbeux. Voici son résultat après l'installation de quelques plugins et thèmes autres :

[nils@fedora-workstation ~]$ sudo nmap -sV -p80,443 --script http-wordpress-info exemple.fr

Starting Nmap 7.40 ( https://nmap.org ) at 2017-03-20 09:53 CET
Nmap scan report for exemple.fr (10.172.46.128)
Host is up (0.0058s latency).
rDNS record for 10.172.46.128: www.exemple.fr
PORT    STATE SERVICE VERSION
80/tcp  open  http    Apache httpd
|_http-server-header: Apache
| http-wordpress-info: 
|   version: WordPress 4.7.3
|   theme: fooding
|   plugins: 
|_    jetpack
443/tcp open  ssl/ssl Apache httpd (SSL-only mode)
|_http-server-header: Apache
| http-wordpress-info: 
|   version: WordPress 4.7.3
|   theme: fooding
|   plugins: 
|_    jetpack

On remarquera ici la présence du plugin bien connu Jetpack, ainsi que du thème fooding.

Jusqu'à maintenant, on a testé un script est assez gentil, mais d'autres sont plus brutaux, comme http-wordpress-brute qui cherche à obtenir un accès via bruteforce de l'interface d'administration. Il convient donc de faire très attention lors de l'utilisation de ces outils.

Des remarques, des propositions d'améliorations ? Où même des exemples intéressants sur certaines configurations de Wordpress ? Les commentaires sont là pour ça !

Propulsé par Dotclear