Redirection de ports vers localhost sous Linux

Il m'est arrivé récemment de lancer des services uniquement sur la boucle locale d'une machine, par exemple un serveur web. On peut douter du bien-fondé de la démarche, mais je trouve cela intéressant à deux titres : - d'abord, si la configuration du service nécessite une adresse IP, cela sera 127.0.0.1, et n'aura pas besoin d'être modifiée en cas de copie sur une autre machine ; - ensuite, si jamais pour une raison ou une autre le pare-feu vient à être inactif, le service ne sera pas exposé.

Bien sûr, cela ajoute une contrainte, celle d'effectuer une redirection de port en plus de l'ouverture de flux. De plus, je ne sais pas si cela a une influence réelle en terme de performance. Je pourrais tester cela à l'occasion, et en faire un article, tiens :)

Donc me voilà en train d'installer un serveur web, de le lancer sur localhost, je fais ma petite configuration à grands coups d'iptables, et là c'est le drame : le trafic ne passe pas. Quelques recherches plus tard, j'apprends qu'en fait par défaut, le noyau Linux considère que ce n'est pas normal qu'un paquet vienne de l'extérieur et ait comme destination 127.0.0.1. Ce comportement peut être modifié depuis la version 3.6, grâce à un paramètre sysctl :

# sysctl -w net.ipv4.conf.all.route_localnet=1

Bien entendu, pour un résultat permanent, il faut penser à éditer /etc/sysctl.conf.

Petit détail sympathique, activer la retransmission de paquets (le fameux ip_forward) n'est pas nécessaire.

Source : Super User.

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 !

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.

systemd : reconfigurer une unité de service

bricolage bumperDans le billet précédent, j'ai abordé haveged et je terminais sur le fait que certains paramètres pouvaient être accessibles. Cela ne semble pas forcément évident, car si on regarde la liste des fichiers du paquet RPM, on n'y trouve aucun fichier de configuration :

$ rpm -ql haveged
 /usr/lib/systemd/system/haveged.service
 /usr/lib64/libhavege.so.1
 /usr/lib64/libhavege.so.1.1.0
 /usr/sbin/haveged
 /usr/share/doc/haveged
 /usr/share/doc/haveged/AUTHORS
 /usr/share/doc/haveged/COPYING
 /usr/share/doc/haveged/ChangeLog
 /usr/share/doc/haveged/README
 /usr/share/doc/haveged/havege_sample.c
 /usr/share/man/man8/haveged.8.gz

De plus, si on regarde le processus lancé, on remarque que certaines options sont précisées sur la ligne de commande :

$ ps auxwww | grep haveged | grep -v grep
root     22470  0.0  0.7  12132  3824 ?        Rs   May16   0:00 /usr/sbin/haveged -w 1024 -v 1 --Foreground

Allons un peu plus loin, le paquet contient un fichier "haveged.service" :

$ cat /usr/lib/systemd/system/haveged.service
[Unit]
Description=Entropy Daemon based on the HAVEGE algorithm
Documentation=man:haveged(8) http://www.issihosts.com/haveged/

[Service]
Type=simple
ExecStart=/usr/sbin/haveged -w 1024 -v 1 --Foreground
SuccessExitStatus=143

[Install]
WantedBy=multi-user.target

Il ne faut pas succomber à la tentation de modifier directement ce fichier, car une possibilité plus propre existe : la documentation officielle RHEL 7 nous apprend ainsi comment créer un fichier de configuration pour le service.

Dans ce cas précis, je souhaite augmenter la valeur de l'argument -w à 2048. Pour l'anecdote, cette option permet d'augmenter l'utilisation de haveged en définissant une taille minimale du réservoir d'entropie. Nous allons donc d'abord créer un répertoire de configuration de service systemd, puis le fichier lui-même :

# mkdir /etc/systemd/system/haveged.service.d/
# vi /etc/systemd/system/haveged.service.d/custom_args.conf

Bon, peu importe le nom du fichier tant qu'il a pour extension ".conf", mais il est malgré tout préférable de lui donner un nom explicite (en clair, faites ce que je dis, pas ce que je fais).

Nous allons dans ce fichier redéfinir la directive ExecStart, puisque c'est celle qui définit l'option à modifier. Par contre, petite subtilité, cette directive doit être vidée pour être redéfinie. Le fichier a donc cette allure :

[Service]
ExecStart=
ExecStart=/usr/sbin/haveged -w 2048 -v 1 --Foreground

Il faut maintenant recharger les unités avant de redémarrer le service haveged :

# systemctl restart haveged.service
Warning: haveged.service changed on disk. Run 'systemctl daemon-reload' to reload units.
# systemctl daemon-reload
# systemctl restart haveged.service
# ps auxwww | grep haveged | grep -v grep
root     23074  2.4  0.7  12132  3836 ?        Ss   04:02   0:00 /usr/sbin/haveged -w 2048 -v 1 --Foreground

Le démon haveged est alors lancé avec une valeur de 2048 pour l'option -w.

Dernier petit détail, SELinux. J'ai testé cette manipulation sur un système configuré en "enforcing", l'édition du fichier s'est donc faite dans le bon contexte. Au cas où certains se demandent comment sont les labels, les voici :

# ll -Z -d /etc/systemd/system/haveged.service.d
drwxr-xr-x. root root unconfined_u:object_r:systemd_unit_file_t:s0 /etc/systemd/system/haveged.service.d
# ll -Z /etc/systemd/system/haveged.service.d/custom_args.conf
-rw-r--r--. root root unconfined_u:object_r:systemd_unit_file_t:s0 /etc/systemd/system/haveged.service.d/custom_args.conf

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 : Katie Hargrave - bricolage bumper.

Haveged : ajouter de l'entropie à son VPS Linux

EntropyEntre deux bidouilles NetBSD, je me suis retrouvé à des bidouilles Linux. Plus particulièrement en jetant un œil à une certaine documentation utile, j'ai pu lire :

Les clés doivent être générées dans un contexte où la source d’aléa est fiable, ou à défaut dans un environnement où suffisamment d’entropie a été accumulée.

Et là, on commence à se poser des questions : qu'est-ce que l'entropie ? Pourquoi faut-il une source fiable ? Comment avoir une meilleure entropie ?

Entropie et aléa

Pour résumer, disons que l'entropie c'est la qualité de la génération de nombres aléatoires. C'est un raccourci assez grossier j'en conviens, mais cela évitera d'écrire ou de paraphraser des pavés mathématiques.

Mais alors, pourquoi générer des nombres aléatoires ? Tout simplement parce que cela fait partie de nombreuses bases d'outils cryptographiques, comme par exemple la génération de clés SSH. C'est d'ailleurs l'occasion d'aborder la question du risque qu'on prend si on ne génère pas assez d'aléa dans notre exemple : il devient possible de générer deux fois le même couple de clés SSH, et par conséquent, que quelqu'un soit en mesure de se connecter à une machine à laquelle il ne devrait pas avoir accès.

Si vous pensez que cela n'arrive jamais, il suffit de se rappeler la vulnérabilité OpenSSH Debian. En 2008, la version Debian d'OpenSSL s'est trouvée modifiée, et a eu pour conséquence un très faible nombre de possibilités pour générer des clés SSH. La preuve ? On peut trouver sur cette page l'intégralité des clés DSA (1024 et 2048 bits) et RSA (1024 à 4096 bits) possibles via cette version vulnérable. J'admets volontiers que c'est un cas extrême, mais il a le mérite d'être assez parlant.

Bref, tout ça pour dire que plus on a d'entropie, mieux c'est.

Mesurer la qualité de l'entropie

Pour mesurer la qualité de l'entropie, c'est très simple :

$ cat /proc/sys/kernel/random/entropy_avail
175

On voit que cela renvoie un nombre, qui désigne la quantité de nombres aléatoires générés. On dit que ce nombre est la taille de notre réservoir d'entropie. Et donc, plus il est grand, mieux c'est. Sauf que là, 175 sur une VM Vagrant CentOS 7, bein c'est pas glorieux.

Une autre manière de mesurer l'entropie consiste à utiliser l'outil rngtest (disponible dans le paquet rng-tools pour CentOS). Celui-ci va lancer un certain nombre de tests utilisant le standard FIPS-140.

Par exemple :

$ cat /dev/random | rngtest -c 1000
rngtest 5
Copyright (c) 2004 by Henrique de Moraes Holschuh
This is free software; see the source for copying conditions.  There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

rngtest: starting FIPS tests...
rngtest: bits received from input: 96
rngtest: FIPS 140-2 successes: 0
rngtest: FIPS 140-2 failures: 0
rngtest: FIPS 140-2(2001-10-10) Monobit: 0
rngtest: FIPS 140-2(2001-10-10) Poker: 0
rngtest: FIPS 140-2(2001-10-10) Runs: 0
rngtest: FIPS 140-2(2001-10-10) Long run: 0
rngtest: FIPS 140-2(2001-10-10) Continuous run: 0
rngtest: input channel speed: (min=0.000; avg=0.000; max=0.000)bits/s
rngtest: FIPS tests speed: (min=0.000; avg=0.000; max=0.000)bits/s
rngtest: Program run time: 21307295 microseconds

Et là, ce n'est toujours pas glorieux, car j'ai arrêté l'exécution faute de patience.

Avant de remédier à ce problème, comparons avec une machine physique notre premier indicateur :

$ cat /proc/sys/kernel/random/entropy_avail
3217

On peut aussi constater que le problème d'entropie affecte particulièrement les machines virtuelles. Cela s'explique surtout par le fait qu'elles disposent de beaucoup moins d'éléments qu'une machine physique, et donc moins d'éléments à lire pour espérer y trouver de l'aléa.

Bon, ce n'est pas tout, mais il est temps de remédier à ce problème d'entropie sur cette VM !

Haveged : générateur d'entropie en espace utilisateur

Haveged est un logiciel qui se présente sous la forme d'un démon qui reste en espace utilisateur. Il tire son nom de l'algorithme qu'il utilise, HAVEGE (HArdware Volatile Entropy Gathering and Expansion).

Côté installation, rien de plus simple, il suffit, pour CentOS, d'avoir accès au dépôt Fedora EPEL. Une fois que c'est fait, un simple yum -y install haveged suffit à disposer du logiciel.

Comme il s'agit d'un démon, il faut le démarrer. Sous CentOS 7, cela se fait via systemd :

# systemctl start haveged.service

Et voilà ! Bon d'accord, cela fait peu. Maintenant, vérifions que notre entropie augmente :

$ cat /proc/sys/kernel/random/entropy_avail
 1779

Voilà qui est mieux. Vérifions aussi avec rngtest :

$ cat /dev/random | rngtest -c 1000
rngtest 5
Copyright (c) 2004 by Henrique de Moraes Holschuh
This is free software; see the source for copying conditions.  There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

rngtest: starting FIPS tests...
rngtest: bits received from input: 20000032
rngtest: FIPS 140-2 successes: 1000
rngtest: FIPS 140-2 failures: 0
rngtest: FIPS 140-2(2001-10-10) Monobit: 0
rngtest: FIPS 140-2(2001-10-10) Poker: 0
rngtest: FIPS 140-2(2001-10-10) Runs: 0
rngtest: FIPS 140-2(2001-10-10) Long run: 0
rngtest: FIPS 140-2(2001-10-10) Continuous run: 0
rngtest: input channel speed: (min=2.057; avg=17.351; max=25.915)Mibits/s
rngtest: FIPS tests speed: (min=44.564; avg=139.836; max=161.640)Mibits/s
rngtest: Program run time: 1237535 microseconds

Dans ce dernier cas, la récupération des informations fut quasi-instantanée ! On peu d'ailleurs noter le nombre de tests réalisés avec succès, qui correspond mieux à nos attentes.

Pour ce qui est d'activer haveged au démarrage, il ne faut pas oublier la commande systemctl qui va bien :

# systemctl enable haveged.service
 Created symlink from /etc/systemd/system/multi-user.target.wants/haveged.service to /usr/lib/systemd/system/haveged.service.

Selon les distributions, certains paramètres supplémentaires sont accessibles, mais cela fera l'objet d'un autre article ;)

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 : teatimer - Entropy

Xen : installation d'un invité domU NetBSD

the handlerDans le billet précédent, j'ai abordé la création d'un hyperviseur Xen (dom0) NetBSD. Il est donc temps de s'occuper du système invité NetBSD, le domU.

Création du domU Xen

Pour créer notre domU, nous avons besoin de 3 éléments présents sur le dom0 :

  • un fichier de disque dur (on pourrait utiliser LVM ou une partition, mais cela est moins flexible) ;
  • dans le cas de NetBSD, un fichier de noyau ;
  • et un fichier de configuration.

D'abord, le fichier de disque dur. Pour le créer, il suffit d'utiliser la commande dd. La taille de ce fichier déterminera la taille du disque dur de la machine virtuelle. Créons un fichier de 4 Go (4096 blocs d'1 Mo) :

root@rogue:~# dd if=/dev/zero of=/srv/xen/images/disk/netbsd.img bs=1m count=4096

On peut remarquer que cet exemple remplit notre fichier de zéros, et ne crée pas de fichier sparse. Il semble que la gestion des fichiers sparse ne soit pas parfaite sous NetBSD, d'après le tutoriel officiel.

Ensuite, récupérons les fichiers noyau :

root@rogue:~# mkdir -p /srv/xen/images/kernels/NetBSD/NetBSD-7.1/amd64/
root@rogue:~# cd /srv/xen/images/kernels/NetBSD/NetBSD-7.1/amd64/
root@rogue:/srv/xen/images/kernels/NetBSD/NetBSD-7.1/amd64 # ftp http://cdn.netbsd.org/pub/NetBSD/NetBSD-7.1/amd64/binary/kernel/netbsd-INSTALL_XEN3_DOMU.gz
root@rogue:/srv/xen/images/kernels/NetBSD/NetBSD-7.1/amd64 # ftp http://cdn.netbsd.org/pub/NetBSD/NetBSD-7.1/amd64/binary/kernel/netbsd-XEN3_DOMU.gz

On récupère deux fichiers de noyau, car l'un d'entre eux ne sert que pour l'installation. Une fois celle-ci terminée, il faut penser à configurer notre domU avec le noyau "classique".

Nous pouvons enfin créer notre fichier de configuration, /usr/pkg/etc/xen/netbsd :

root@rogue:/usr/pkg/etc/xen# grep -v ^# netbsd | grep -v ^$
name = "netbsd"
uuid = "d0f3e8d3-2f54-11e7-b035-00301bbde894"
kernel = "/srv/xen/images/kernels/NetBSD/NetBSD-7.1/amd64/netbsd-INSTALL_XEN3_DOMU.gz"
memory = 256
vcpus = 1
vif = [ 'bridge=bridge0,mac=00:16:3E:00:00:02' ]
disk = [ '/srv/xen/images/disk/netbsd.img,raw,xvda,rw' ]

Les directives du fichier de configuration sont assez explicites, néanmoins il convient de préciser certaines choses :

  • d'abord, le nom de la machine virtuelle ("name") doit être unique ;
  • et en passant, l'uuid aussi (généré via uuidgen), mais il n'est pas obligatoire, la directive peut être vide ;
  • la mémoire est spécifiée en méga-octets ;
  • on peut spécifier plusieurs interfaces réseau ou disques durs.

On peut ajouter bien d'autres options, mais il est préférable de commencer par un fichier simple, qui démarrera une machine en mode texte, avant d'aller plus loin.

Maintenant que notre fichier de configuration est prêt, démarrons notre domU :

root@rogue:/usr/pkg/etc/xen# xl create -c netbsd

L'option -c permet d'attacher la console locale de la machine virtuelle en mode texte, et donc de pouvoir interagir avec (pour, par exemple, effectuer une installation).

Installation de NetBSD dans le domU

L'installation se passe de manière similaire à ce qui est présenté dans le guide officiel, mais à une différence près : une fois l'installation terminée, il faut quitter l'installeur (au lieu de redémarrer), puis éteindre la machine virtuelle :

# shutdown -p now

De retour dans le dom0, il faut alors changer le fichier de noyau pour un démarrage "classique" :

root@rogue:/usr/pkg/etc/xen# vi netbsd
kernel = "/srv/xen/images/kernels/NetBSD/NetBSD-7.1/amd64/netbsd-XEN3_DOMU.gz"

On peut ensuite démarrer notre machine virtuelle, dans l'exemple suivant sans attacher la console locale de celle-ci :

root@rogue:/usr/pkg/etc/xen# xl create netbsd

On est vraiment obligé de spécifier sur le noyau ?

Dans le domU d'exemple du billet précédent, le système OpenWrt était démarré grâce à pygrub, un chargeur de démarrage pour Xen. Celui-ci n'est hélas pas capable de lire le système de fichiers FFS utilisé par NetBSD. Cela n'est pas non plus possible pour pv-grub, qui n'est de toute façon pas disponible dans les paquets Xen pkgsrc.

Quelles sont alors les possibilités ? La première consiste à créer une partition /boot en ext2/3/4 au début du disque virtuel, et d'y placer noyau et configuration Grub, comme l'indique ce tutoriel. Une autre consiste à compiler soi-même une version de Grub2, qui semble maintenant gérer Xen, tout du moins d'après ce billet du blog officiel Xen, daté de janvier 2015.

Autres actions possibles

Démarrer sa machine virtuelle, c'est bien, pouvoir effectuer d'autres actions et vérifications, c'est mieux ! Voici donc, en vrac, quelques commandes utiles pour gérer ses domU :

  • xl shutdown <chemin vers le fichier de configuration> permet d'arrêter proprement celui-ci ;
  • besoin d'appuyer sur le bouton Off comme un gros barbare ? xl destroy <nomdudomU> ;
  • lister les domU en fonctionnement : xl list ;
  • et pour avoir cette liste en temps réel, présentée à la manière d'un top, on peut utiliser xl top.

D'autres commandes et paramètres sont disponibles dans la page de manuel de la commande xl.

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 : ColonelMustard - the handler''

Xen : installation d'un hyperviseur dom0 NetBSD

ArmyJ'ai écrit dans le passé quelques billets concernant Xen, mais jamais sur l'installation à proprement parler d'un hyperviseur Xen à base de NetBSD. Il est temps de réparer cela ! Mais avant de commencer, quelques rappels :

  • en terminologie Xen, chaque système invité se nomme un "domaine" ;
  • une machine virtuelle est un domaines non-privilégié, en anglais "unprivileged domain", généralement raccourci en "domU" ;
  • l'OS qui fait fonctionner l'hyperviseur est un domaine privilégié, en anglais "privileged domain", ce qui donne en raccourci "dom0".

Il s'agit donc de détailler l'installation et la configuration de Xen en tant que dom0 sur un système NetBSD amd64. Pour valider son bon fonctionnement, une installation rapide d'un domU OpenWrt sera effectuée à la fin.

Mais avant de démarrer, voici quelques informations sur la configuration qui sera effectuée :

  • comme la machine physique ne dispose pas des instructions de virtualisation (Intel Atom 330), seul le mode "paravirtuel" sera abordé ;
  • la machine physique se verra attribuer 256 Mo de RAM sur ses 2 Go pour son fonctionnement ;
  • la configuration réseau sera en mode "bridge", le dom0 et les domU seront donc sur le même réseau.

Allons-y !

Installation et configuration de NetBSD

Commençons par l'installation du système d'exploitation : NetBSD 7.1 amd64. Il n'y a rien en particulier à signaler sur l'installation, cela dépend avant tout de son usage. Cette machine n'étant pas destinée à devenir un environnement de production, j'ai choisi un partitionnement minimal, à savoir juste un / qui prend tout le disque.

A noter aussi qu'à ce moment, il n'y a besoin de rien en particulier concernant le noyau. J'ai pris l'habitude de ne pas installer les sets de compilation ou source sur une machine sauf si j'en ai expressément besoin. Donc, je me suis limité aux sets suivants :

  • base ;
  • etc ;
  • man ;
  • misc ;
  • modules ;
  • tests ;
  • text ;
  • xbase.

Côté réseau, il sera sans doute plus simple de configurer une adresse IP statique. On va aussi dès maintenant configurer le bridge :

root@rogue:~# cat > /etc/ifconfig.bridge0 
create
!brconfig $int add re0 up

A noter que l'interface de la machine physique est re0, il convient de la modifier selon celle disponible. On va aussi autoriser la retransmission de paquets réseau :

root@rogue:~# cat >> /etc/sysctl.conf
net.inet.ip.forwarding=1

Ces modifications seront prises en compte au prochain démarrage du système, qu'il convient de faire dès maintenant.

Pour les paquets logiciels, j'ai choisi d'utiliser mon propre dépôt pkgsrc de paquets binaires. Là aussi, rien d'exceptionnel, j'ai juste installé mon petit confort personnel. Il est néanmoins possible d'utiliser le dépôt binaire pkgsrc officiel (configuré lors de l'installation) ou d'utiliser pkgsrc depuis les sources.

Installation et configuration de Xen 4.6

Maintenant que notre système est installé et prêt, passons à l'installation de Xen. Rien de compliqué non plus à ce niveau, il suffit d'utiliser pkgin :

root@rogue:~# pkgin in xenkernel46 xentools46

Des messages seront affichés durant l'installation des différents paquets, montrant un certain nombre de messages de conseils et de recommandations.

Pour que Xen fonctionne, il faut d'abord démarrer un noyau spécialisé qui chargera de lancer l'hyperviseur. Le noyau NetBSD dom0 est disponible à côté du noyau générique sur les dépôts :

root@rogue:~# wget http://cdn.netbsd.org/pub/NetBSD/NetBSD-7.1/amd64/binary/kernel/netbsd-XEN3_DOM0.gz
root@rogue:~# mv netbsd-XEN3_DOM0.gz /

Configurons maintenant le chargeur de démarrage. Il suffit d'insérer la ligne suivante au début du fichier /boot.cfg :

menu=Xen:load /netbsd-XEN3_DOM0.gz console=pc;multiboot /usr/pkg/xen46-kernel/xen.gz dom0_mem=256M

Parmi les détails de cette ligne de configuration, on remarquera l'allocation de 256 Mo de mémoire vive pour le dom0.

Par contre, si jamais le partitionnement prévoit un /usr séparé, il vaudra mieux copier /usr/pkg/xen46-kernel/xen.gz dans / et de remplacer les chemins en accord avec la nouvelle localisation du fichier. Une fois que le chargeur de démarrage est modifié, un redémarrage est nécessaire, mais juste avant on peut créer les fichiers spéciaux dans /dev :

root@rogue~# cd /dev && sh MAKEDEV xen

Maintenant on peut redémarrer :)

Mais tout n'est pas encore prêt. Par exemple, le service xencommons doit être activé et démarré. On ajoute alors la ligne suivante au fichier /etc/rc.conf :

xencommons=YES

On peut ensuite lancer le service :

root@rogue~# service xencommons start

Il y a encore un fichier de configuration à modifier avant de commencer à faire joujou avec nos machines (para-)virtuelles : /usr/pkg/etc/xl.conf. Je n'ai fait que deux modifications à ce fichier :

root@rogue:~# grep -v ^# /usr/pkg/etc/xen/xl.conf | grep -v ^$
vif.default.script="vif-bridge"
vif.default.bridge="bridge0"

Il est temps d'effectuer rapidement un test de domU !

Test d'un domU OpenWrt

Un moyen de tester rapidement le fonctionnement de son dom0 Xen est d'utiliser un domU OpenWrt : il s'agit en effet d'un OS léger, non seulement d'un point de vue processeur et mémoire, mais aussi d'un point de vue espace disque. Au moment de la rédaction de cet article, la dernière version stable d'OpenWrt est la 15.05.1 et porte le nom de Chaos Calmer.

Deux éléments sont nécessaires : un fichier qui sera le disque dur de la machine virtuelle, et un fichier de configuration. Dans le premier cas c'est très simple, il suffit d'aller le récupérer sur le site d'OpenWrt, de le placer dans un répertoire, et de le décompresser :

root@rogue:~# mkdir -p /srv/xen/images/disk
root@rogue:~# cd /srv/xen/images/disk
root@rogue:/srv/xen/images/disk# wget https://downloads.openwrt.org/latest/x86/xen_domu/openwrt-15.05.1-x86-xen_domu-combined-ext4.img.gzroot@rogue:/srv/xen/images/disk# zcat openwrt-15.05.1-x86-xen_domu-combined-ext4.img.gz > openwrt.img

Continuons avec le fichier de configuration, basé sur un fichier d'exemple. Il doit se trouver dans /usr/pkg/etc/xen/, et se nomme tout simplement openwrt :

name = "openwrt"
uuid = "26824417-2dde-11e7-a2aa-00301bbde894"
bootloader = "/usr/pkg/bin/pygrub"
extra = "root=/dev/xvda2 rw"
memory = 128
vcpus = 1
vif = [ 'bridge=bridge0,mac=00:16:3E:00:00:01' ]
disk = [ '/srv/xen/images/disk/openwrt.img,raw,xvda,rw' ]

Maintenant que tout cela est en place, il ne reste plus qu'à lancer la machine virtuelle :

root@rogue:~# xl create -c /usr/pkg/etc/xen/openwrt

Une fois OpenWrt démarré, il suffit alors d'appuyer sur la touche entrée pour activer la console locale. Côté tests, il faut vérifier le nombre de processeurs (dans /proc/cpuinfo, il doit n'y en avoir qu'un), ainsi que la quantité de mémoire vive (dans /proc/meminfo, ou via free -m, on doit avoir 128 Mo). Si un serveur DHCP est présent, on peut facilement tester le réseau via udhcpc :

root@OpenWrt:/# udhcpc -i br-lan

On peut désormais vérifier que le réseau est bien connecté.

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 : Marcos Leal - Army

Vlog épisode 0 : changement de boitier du NAS

p1020274.jpgAujourd'hui, un article un peu particulier, car il s'agit de signaler la publication de mon premier vlog ! L'idée de passer à la vidéo me trotte dans la tête depuis quelques temps, et j'ai saisi l'occasion de ce projet de changement du boitier de mon NAS pour passer à la réalisation. Pour voir la vidéo, c'est ici :

Le NAS, cette bonne excuse

Dans cette première vidéo, je démonte mon NAS existant afin de remplacer mon boitier. Plusieurs raisons ont poussé ce changement :

  • d'abord, l'ancien boitier manque de place, m'empêchant de disposer de six disques durs 3,5 pouces ;
  • de plus, je voulais passer à un boitier au format rackable 19 pouces, exploitant au mieux la place disponible pour le NAS (une table IKEA Lack) ;
  • enfin, l'ancien boitier ne permet d'installer au mieux qu'une carte mère micro-ATX, ce qui limite l'évolutivité.

J'en ai profité pour faire le ménage de printemps, c'est-à-dire faire les poussières, et changer la pâte thermique sur le processeur. Afin de mieux prévoir mes besoins futurs en stockage et services sur le NAS, j'ai aussi ajouté de la mémoire vive. Le moins qu'on puisse dire, c'est que FreeNAS en a tout de suite profité !

Les composants utilisés pour cette machine ne sont pas de première jeunesse, le NAS ayant déjà deux ans de service, je crois :

Côté logiciel, l'OS installé est FreeNAS.

D'autres vidéos ?

J'espère que vous apprécierez cette vidéo au moins autant que j'ai apprécié de la tourner. Bien sûr, c'est un premier jet, et j'espère à l'occasion d'autres vidéos m'améliorer sur certains points (euh... si si, vraiment !) Comme il s'agit pour l'instant plus d'une expérimentation que d'un véritable engagement à faire des vidéos, dans l'immédiat ce premier vlog n'est disponible que sur Youtube. Selon la demande, les vidéos seront disponibles en téléchargement direct. J'admets que ce faisant, je "nourris" un peu plus l'une des grosses sociétés d'Internet avec mes données, mais c'est aussi un moyen d'aller chercher une audience.

Enfin, tout ceci ne serait pas possible sans le Studio Cyanotype ! Merci à elle d'avoir filmé et monté cette vidéo ! N'hésitez pas à aller voir sa chaine Youtube et son blog !

Crédit Photo : Vincent Battez - P1020274

PHP Malware Finder : gestion des listes blanches

Lime &amp; 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

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

curl : utiliser une version plus récente sur macOS

Le système macOS dispose en standard de curl. Mais ce binaire n'est pas forcément dans une version assez récente, ou alors certaines options ne sont pas compilées.

Installation de curl par pkgin

Nous allons, grâce à pkgsrc, installer une autre version, sans toucher à celle installée par défaut. Pour cela, le prérequis est de suivre mon tutoriel pour installer pkgsrc. Une fois que c'est fait, une commande suffit :

sudo pkgin in curl

Comme vu dans les billets précédents, installer un logiciel grâce à pkgin est très simple. En plus, si la variable d'environnement $PATH définit l'emplacement des programmes issus de pkgsrc avant ceux du système, la prochaine invocation de curl dans le terminal sera celle que nous venons d'installer.

Mais il se peut qu'on ait besoin de plus : par exemple, ajouter ou retirer des options de compilation. Passons donc à une autre méthode d'installation, via les sources.

Installation de curl par compilation des sources

Tout d'abord, comparons les versions et les options de compilation :

nils@dalaran-wifi:~$ /usr/bin/curl -V
curl 7.51.0 (x86_64-apple-darwin16.0) libcurl/7.51.0 SecureTransport zlib/1.2.8
Protocols: dict file ftp ftps gopher http https imap imaps ldap ldaps pop3 pop3s rtsp smb smbs smtp smtps telnet tftp
Features: AsynchDNS IPv6 Largefile GSS-API Kerberos SPNEGO NTLM NTLM_WB SSL libz UnixSockets
nils@dalaran-wifi:~$ /opt/pkg/bin/curl -V
curl 7.53.1 (x86_64-apple-darwin13) libcurl/7.53.1 OpenSSL/1.0.2k zlib/1.2.8 libssh2/1.8.0 nghttp2/1.20.0
Protocols: dict file ftp ftps gopher http https imap imaps ldap ldaps pop3 pop3s rtsp scp sftp smb smbs smtp smtps telnet tftp
Features: IPv6 Largefile NTLM NTLM_WB SSL libz TLS-SRP HTTP2 UnixSockets HTTPS-proxy

Une option dont on a besoin n'est pas présente ? Ce n'est pas grave, car on peut l'ajouter. L'étape suivante consiste à lister les options disponibles :

    nils@dalaran-wifi:/opt/pkgsrc/www/curl$ bmake show-options
    Any of the following general options may be selected:
    gssapi     Enable gssapi (Kerberos V) support.
    http2     Add support for HTTP/2.
    inet6     Enable support for IPv6.
    ldap     Enable LDAP support.
    libidn     Add support for libidn text conversion.
    libssh2     Use libssh2 for SSHv2 protocol support.
    rtmp     Enable rtmp:// support using rtmpdump.

    These options are enabled by default:
    inet6 libidn

    These options are currently enabled:
    inet6 ldap libidn libssh2

    You can select which build options to use by setting PKG_DEFAULT_OPTIONS
    or PKG_OPTIONS.curl.

On peut alors éditer _/opt/pkg/etc/mk.conf.local_ (en tant que root, ou via _sudo_) et ajouter des options, comme par exemple http2 :

PKG_OPTIONS.curl+= http2

Et ensuite, on recompile :

nils@dalaran-wifi:/opt/pkgsrc/www/curl$ bmake package-install

L'étape d'après est de vérifier la présence de l'option http2 :

    nils@dalaran-wifi:/opt/pkgsrc/www/curl$ /opt/pkg/bin/curl -V
    curl 7.53.1 (x86_64-apple-darwin16) libcurl/7.53.1 OpenSSL/1.0.2k zlib/1.2.8 libssh2/1.8.0 nghttp2/1.20.0
    Protocols: dict file ftp ftps gopher http https imap imaps ldap ldaps pop3 pop3s rtsp scp sftp smb smbs smtp smtps telnet tftp
    Features: IPv6 Largefile NTLM NTLM_WB SSL libz TLS-SRP HTTP2 UnixSockets HTTPS-proxy

En conclusion, il est très simple, grâce à pkgsrc, de disposer d'une autre version de logiciel que celle installée par défaut, et de la compiler avec les options dont on a besoin.

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 !

Propulsé par Dotclear