Mirai_ptea Botnet利用KGUARD DVR未公开漏洞报告

版权


版权声明：本文为Netlab原创，依据 CC BY-SA 4.0 许可证进行授权，转载请附上出处链接及本声明。



概述


2021-06-22我们检测到一个我们命名为mirai_ptea的mirai变种样本通过未知漏洞传播。经过分析，该漏洞为KGUARD DVR未公开的漏洞。从我们的分析看该漏洞存在于2016年的固件版本中。我们能找到的2017年之后的固件厂家均已经修复该漏洞。有意思的是，一天以后，6月23日，我们收到安全社区的询问，咨询我们是否看到一种新的DDoS攻击botnet。交叉对比线索，居然就是我们刚发现的这个botnet，目前看。这个僵尸网络处于活跃的攻击状态(见最后)。



时间线


 * 2021-03-22 我们历史数据首次观察到针对该漏洞的探测扫描
 * 2021-06-22 我们观察到mirai_ptea样本利用该漏洞传播
 * 2021-06-23 我们收到安全社区的询问是否看到一种新的DDoS攻击botnet
 * 2021-06-25 我们观察到mirai_aurora样本利用该漏洞传播



漏洞分析


鉴于我们并未发现该漏洞的公开资料，为防止该漏洞被滥用，此处我们将隐藏部分关键信息。


KGUARD DVR 2016年的固件上的***程序监听在0.0.0.0的*****端口无需认证即可远程执行系统命令。 最新的固件中厂家通过修改监听地址为127.0.0.1修复了该漏洞。部分利用payload如下：





受影响设备分析


通过探测我们发现至少3千左右的在线设备依然存在该漏洞。已知仍然受影响设备如下:





DeviceType
ProductType
HardVersion
DefDeviceName




D1004NR
DVR4-1600
DM-268A
DVR4-1600


D1004NR
HY-DVR
DM-268
720P-HY04N


D1004NR
HY-DVR
DM-268A
720P-HY04N


D1004NR
HY-DVR
DM-274
720P-HY04N


D1004NR
HY-DVR
DM-274B
720P-HY04N


D1004NR
NHDR
DM-274
NHDR-3204AHD


D1004NR
RL-AHD4n
DM-268
720P-HY04N


D1008NR
1093/508N-DVRBM08H
DM-292
720P-HY08N


D1008NR
DVR8-1600
DM-298
DVR8-1600


D1008NR
DVR8-HDA10L
DM-292
DVR8-HDA10L


D1008NR
HD881
DM-292
HD881


D1008NR
HY-DVR
DM-292
720P-HY08N


D1008NR
HY-DVR
DM-298
720P-HY08N


D1008NR
NHDR
DM-298
NHDR-3208AHD


D1008NR
RL-AHD8n
DM-292
720P-HY08N


D1016NR
DVR16-HDA10L
DM-303
DVR16-HDA10L


D1016NR
HD1681
DM-303
HD1681


D1016NR
HY-DVR
DM-303A
720P-HY16N


D1016NR
HY-DVR
DM-310
720P-HY16N


D1016NR
HY-DVR
DM-310A
720P-HY16N


D1016NR
NHDR
DM-310
NHDR-3216AHD


D1016NR
RL-MHD16n(21A)
DM-310A
720P-HY16N


D1104
HY-DVR
DM-290A
1080P-HY04


D1104
NHDR
DM-307
NHDR-5304AHD


D1104NR
HD1T4
DM-291A
1080P-04


D1104NR
HD481
DM-291
HD481


D1104NR
HRD-E430L
DM-291A
HRD-E430L


D1104NR
HY-DVR
DM-284
1080P-HY04N


D1104NR
HY-DVR
DM-291
"Panda


D1104NR
HY-DVR
DM-291
1080P-HY04N


D1104NR
HY-DVR
DM-291A
1080P-HY04N


D1104NR
HY-DVR
DM-291C
LRA3040N


D1104NR
NHDR
DM-307
NHDR-5104AHD


D1104NR
SDR-B73303
DM-291A
SDR-B73303


D1104NR
SVR9204H
DM-291A
1080P-HY04N


D1108NR
1093/538P
DM-290
1080P-HY08N


D1108NR
DVR8-4575
DM-290
DVR8-4575


D1108NR
DVR8-HDA10P
DM-290
DVR8-HDA10P


D1108NR
HRD-E830L
DM-290A
HRD-E830L


D1108NR
HY-DVR
DM-290
1080P-HY08N


D1108NR
HY-DVR
DM-290A
1080P-HY08N


D1108NR
HY-DVR
DM-290A
LRA3080N


D1108NR
NHDR
DM-307
NHDR-5108AHD


D1108NR
RL-AHD8p
DM-290
1080P-HY08N


D1108NR
SDR-B74301
DM-290A
SDR-B74301


D1108NR
SDR-B74303
DM-290A
SDR-B74303


D1116
HY-DVR
DM-300
EHR-5164


D1116NR
HRD-E1630L
DM-295
HRD-E1630L


D1116NR
HY-DVR
DM-295
1080P-HY16N


D1116NR
HY-DVR
DM-295
LRA3160N


D1116NR
HY-DVR
DM-299
1080P-HY16N


D1116NR
SDR-B75303
DM-295
SDR-B75303


D1132NR
HY-DVR
DM-300
1080P-HY32


D2116NR
SDR-B85300
DM-300
SDR-B85300


D973215U
F9-DVR32
DM-195
F9-DVR32


D9804AHD
DVR
DM-210
391115


D9804NAHD
AHD7-DVR4
DM-239
AHD7-DVR4


D9804NAHD
DVR
DM-239
720P-DVR04ND


D9804NAHD
NHDR
DM-239
NHDR-3104AHD-II


D9808NRAHD
AHD7-DVR8
DM-228
AHD7-DVR8


D9808NRAHD
DVR
DM-228



D9808NRAHD
DVR
DM-228
391116


D9808NRAHD
NHDR
DM-228
NHDR-3108AHD-II


D9808NRAHD
NHDR
DM-228
NHDR3108AHDII


D9816NAHD
DVR
DM-233
720P-DVR016N


D9816NAHD
NHDR
DM-233
NHDR3116AHDII


D9816NRAHD
AHD7-DVR16
DM-229
AHD7-DVR16


D9816NRAHD
DVR
DM-229
720P-DVR016NB


D9904
D9904
DM-237
1080P-DVR04


D9904
DVR
DM-237
1080P-DVR04


D9904
NHDR
DM-237
NHDR-5204AHD


D9904NR
DVR
DM-244
1080P-DVR04N


D9904NR
DVR
DM-244
BCS-VAVR0401M


D9904NR
HY-DVR
DM-244
CVD-AF04S


D9904NR
N420
DM-244
1080P-DVR04N


D9904NR
NHDR
DM-244
NHDR-5004AHD-II


D9904NR
NHDR
DM-244
NHDR5004AHDII


D9908
DVR
DM-245
BCS-VAVR0802Q


D9908
NHDR
DM-245
NHDR-5208AHD


D9908AHD
DVR
DM-246
1080P-DVR08A


D9908NR
AHD10-DVR8
DM-237
AHD10-DVR8


D9908NR
DVR
DM-237
1080P-DVR08N


D9908NR
DVR
DM-237
SVR9008ATHD/C


D9908NR
HY-DVR
DM-237
CVD-AF08S


D9908NR
N820
DM-237
1080P-DVR08N


D9908NR
NHDR
DM-237
NHDR-5008AHD-II


D9916NR
DVR
DM-245
1080P-DVR016NAT;UI


D9916NR
DVR
DM-245
HR-31-211620;UI


D9916NR
HY-DVR
DM-245
CVD-AF16S


D9916NR
NHDR
DM-245
NHDR-5016AHD-II


D9916NRAHD
DVR
DM-246
1080P-DVR016NA


D9916NRAHD
N1620
DM-246
1080P-DVR016NA


H1104W
SNR-73200W
DM-339
SNR-73200W


H1106W
LHB806
DM-291B
LHB806


H1106W
LHB906
DM-291B
LHB906






Bot规模分析


从我们的数据视野看，该botnet的活跃Bot源IP趋势如下：




Bot源IP地理位置分布如下，主要集中在美国、韩国和巴西：





样本分析


本文选取以下样本为主要分析对象


Verdict:mirai_ptea
MD5:c6ef442bc804fc5290d3617056492d4b
ELF 32-bit LSB executable, ARM, version 1, statically linked, stripped 
Packer:No
Lib:uclibc



c6ef442bc804fc5290d3617056492d4b是一个Mirai的变种，基于其使用Tor Proxy和C2通信，以及TEA算法（Tiny Encryption Algorithm）隐藏敏感的资源信息，我们称之为Mirai_ptea。Mirai_ptea运行时在会Console输出字串come at me krebs rimasuta go BRRT，这就是有的研究人员将它称之为Rimasuta的原因，它在主机行为层面和Mirai很相似，并无亮点，因此这方面不再细述；在网络流量层面采用Tor Proxy，样本内嵌了大量的代理节点，而且Tor-C2被加密，下文将着重讨论加密方法和通信协议。



加密算法


Mirai_ptea将所有的敏感资源信息加密并按一定的顺序存储，在IDA打开样本看到的字串信息如下所示，几乎没有可读的信息。





下面的代码片段来自样本中解密相关函数，通过红框中的常量可以判定它使用的是TEA算法，



密钥为：


0xC26F6A52 0x24AA0006 0x8E1BF2C5 0x4BA51F8C



基于逆向分析，我们实现了附录的解密脚本，通过它可以获得所有的解密后的敏感资源以及它们的表项信息，部分资源信息如下所示：



Mirai_ptea在使用加密资源时有2种操作方式


 * 传统的Mirai方式，解密一个加密项，取值，加密一个解密后的项，即var_unlock-->var_get-->var_lock。例如在获取输出在Console上的信息时，就是通过这种方式。
   
   


表项0x11的值正是come at me krebs rimasuta go BRRT。


 * Mirai_ptea的方式，解密多个加密项，取值，重新加密已解密的项，即rangeVar_unlock-->var_get-->rangeVar_lock。例如在获取伪装进程名时，就是通过这种方式。




表项0x2c到0x2c+10的值如下所示，正是11个供选择的伪装进程名：


index 0x2c, value = /bin/sh
index 0x2d, value = telnetd
index 0x2e, value = upnpc-static
index 0x2f, value = wsdd
index 0x30, value = proftpd
index 0x31, value = mini_httpd
index 0x32, value = udevd
index 0x33, value = /sbin/udhcpc
index 0x34, value = boa
index 0x35, value = /usr/sbin/inetd
index 0x36, value = dnsmasq




通信协议


Mirai_ptea的网络流量概览如下图所示：



整个过程可以分成以下3个步骤：


1：和代理节点建立连接


2：和Tor C2建立连接


3：通过ptea自定义的协议和C2通信，接收C2下发的攻击指令。



0x1. 和代理建立连接


Mirai_ptea样本中内置了2组代理，它们在加密资源中的表项分别为0x2a,0x2b。Bot样本运行时，会在2组代理中随机选一组，然后在选中的组中随机选一个通过下面代码片段建立连接。



其中0x2a中一共有38个代理节点，格式为ip:port



而0x2b中一共有个334个代理节点，格式为ip，这组代理的端口为固定的9050。



详细的代理列表见附录。



0x2. 通过Tor-Proxy协议和C2建立连接





可以看出C2在加密资源中的表项为0xD，解密后得到下面字串：


rkz2f5u57cvs3kdt6amdku2uhly2esj7m2336dttvcygloivcgsmxjjnuickasbuatxajrovi4lvd2zjuejivzrb3vobuoezbc6z3gtu6b3r5tce.onion



将上面字串排除尾部的“.onion”后以长度16分割，然后和尾部的.onion字串进行拼接，就得到了以下7个C2。


rkz2f5u57cvs3kdt.onion
6amdku2uhly2esj7.onion
m2336dttvcygloiv.onion
cgsmxjjnuickasbu.onion
atxajrovi4lvd2zj.onion
uejivzrb3vobuoez.onion
bc6z3gtu6b3r5tce.onion




0x3. 通过自定义的协议和C2进行通信，具体的上线，心跳，攻击如下所示


 * 上线
   
   


msg parsing
----------------------------------------------------------------
3e c7 e3 1e 37 47 61 20						----->hardcoded msg from Bot
b1 2f de ce cb 89 e1 a0						----->cmd from C2,ask Bot to upload info
3a 31 34 b5 02 00							----->hardcoded 6 bytes msg from Bot
b4 a3 e1 16									----->ip of infected device
04											----->group string length
74 65 73 74								    ----->group string
79			                                ----->padding



 * 心跳
   
   


msg parsing
----------------------------------------------------------------
2a 23						-----> random 2 bytes msg from Bot
2a 23						-----> random 2 bytes msg from C2



 * 攻击指令，前4字节AD AF FE 7F为固定的幻数，剩余部分与mirai的攻击指令格式类似


00000000: AD AF FE 7F 1E 00 00 00  00 01 B9 98 42 65 20 00  ............Be .
00000010: 42 65 20 00     




DDoS攻击活动


从我们的DDoS跟踪系统看，该僵尸网络已经发起实际的DDoS攻击，下图为我们观察到的该僵尸网络的一些DDoS攻击指令：





联系我们


感兴趣的读者，可以在 twitter 或者通过邮件netlab[at]360.cn联系我们。



IoC



Tor-C2


bc6z3gtu6b3r5tce.onion:3742
cgsmxjjnuickasbu.onion:992
uejivzrb3vobuoez.onion:5353
rkz2f5u57cvs3kdt.onion:280
atxajrovi4lvd2zj.onion:110
6amdku2uhly2esj7.onion:513
m2336dttvcygloiv.onion:666




Sample MD5


c6ef442bc804fc5290d3617056492d4b
f849fdd79d433e2828473f258ffddaab




Downloader URL


http://193[.177.182.221/boot




Scanner IP


205.185.117.21	AS53667|FranTech_Solutions	United_States|Nevada|Las_Vegas
205.185.114.55	AS53667|FranTech_Solutions	United_States|Nevada|Las_Vegas
68.183.109.6	AS14061|DigitalOcean,_LLC	United_States|New_York|New_York_City
67.205.163.141	AS14061|DigitalOcean,_LLC	United_States|New_York|New_York_City
165.227.88.215	AS14061|DigitalOcean,_LLC	United_States|New_York|New_York_City




Proxys


---------proxys at index 0x2a，count=38---------
149.202.9.7:9898
91.134.216.103:16358
84.32.188.34:1157
51.178.185.237:32
65.21.16.80:23560
149.202.9.14:19765
146.59.11.109:5089
195.189.96.61:29582
84.32.188.37:1454
51.195.209.80:26848
5.199.174.242:27931
95.179.158.147:22413
146.59.11.103:1701
185.150.117.10:29086
149.56.154.210:24709
135.148.11.151:3563
51.195.152.255:25107
45.79.193.124:7158
135.148.11.150:5560
185.150.117.41:20790
135.125.250.120:14498
172.106.70.135:692
195.189.96.60:9700
172.106.70.134:25054
149.56.154.211:21299
108.61.218.205:29240
51.178.185.236:21685
51.81.139.251:6255
51.255.237.164:963
51.81.139.249:32380
139.162.45.218:5165
65.21.16.94:28056
207.148.74.163:32389
172.104.100.78:1039
45.32.8.100:19759
141.164.46.133:2205
172.105.36.167:10843
172.105.180.239:19531

---------proxys at index 0x2b，count=334，port=9050---------
Too many, not list here, you can get them via the IDA script




附录（IDA解密脚本）


# IDAPYTHON SCRIPT for md5 c6ef442bc804fc5290d3617056492d4b only.
# Tested at ida 7.0
from ctypes import *
import struct
print "-------------------decryption start------------------------"

key=[0xC26F6A52,0x24AA0006,0x8E1BF2C5,0x4BA51F8C]
def tea_dec(buf,key):
    rbuf=""
    fmt = '>' + str(len(buf)/4) + 'I'
    tbuf= struct.unpack_from(fmt,buf)
    j=0
    for i in range(0,len(tbuf)/2):
        
        v1=c_uint32(tbuf[i+j])
        v2=c_uint32(tbuf[i+1+j])

        sum=c_uint32(0xC6EF3720)
        while(sum.value):
            v2.value -= ((v1.value>>5)+key[3])    ^(v1.value+sum.value)^  ((v1.value<<4)+key[2])
            v1.value -= ((v2.value>>5)+key[1])   ^(v2.value+sum.value)^ ((v2.value<<4)+key[0])
            sum.value+=0x61C88647
        rbuf +=struct.pack(">I",v1.value)+struct.pack(">I",v2.value)
        j+=1
    return rbuf
def getbuff(addr):
    buf = ""
    while idc.get_bytes(addr, 2) != "\x00\x00":
        buf += idc.get_bytes(addr, 1)
        addr += 1

    return buf



# pay attention to function at 0x0000D074
a=getbuff(idc.get_wide_dword(0x00019C9C))

    

buf=[]
#0x19c9c-0x199f0 --> 684
for i in range(0,684,12):
    offset=idc.get_wide_word(0x000199F4+i)
    length=idc.get_wide_word(0x000199F4+i+2)

    buf.append(a[offset:offset+length])
    
c2=[]
#684/12 --> 57
for i in range(57):
    decbuf=tea_dec(buf[i],key)

    if(".onion" in decbuf):
        c2.append(decbuf)
    print "index %x, value = %s" %(i,decbuf)    
print "-------------------decryption end---------------"

proxya=tea_dec(buf[0x2a],key)
pacnt=struct.unpack("<H",proxya[2:4])


proxy=[]
port=[]
tmp=proxya[4:4+6*(pacnt[0])]
print "------------proxys at index 0x2A, count= %d------------" %(pacnt[0])
for i in range(0,len(tmp),6):
    proxy.append(struct.unpack(">I",tmp[i:i+4])[0])
    port.append(struct.unpack("<H",tmp[i+4:i+6])[0])
for i in range(pacnt[0]):
    a=struct.pack(">I",proxy[i])
    ip=""
    for j in range(4):
        ip+=str(ord(a[j]))
        if j!=3:
            ip+="."
    
    print"%s:%d" %(ip,port[i])



proxyb=tea_dec(buf[0x2b],key)
pbcnt=struct.unpack("<H",proxyb[2:4])
fmt = '>' + str(pbcnt[0]) + 'I'
tmp=proxyb[4:4*(pbcnt[0]+1)]
print "------------proxys at index 0x2B, count= %d------------" %(pbcnt[0])
xxxxx=struct.unpack(fmt,tmp)
for i in xxxxx:
    a=struct.pack(">I",i)
    ip=""
    for i in range(4):
        ip+=str(ord(a[i]))
        if i!=3:
            ip+="."
    print ip


print "-------------------------onion info--------------"    
if len(c2)!=0:
    for i in c2:
        
        pos=i.find(".onion")

        for j in range(0,pos,16):
            print i[j:16+j]+".onion"
else:
    print "Don't find the onion c2"