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

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

2021-06-22我们检测到一个我们命名为mirai_ptea的mirai变种样本通过未知漏洞传播。经过分析,该漏洞为KGUARD DVR未公开的漏洞。从我们的分析看该漏洞存在于2016年的固件版本中。我们能找到的2017年之后的固件厂家均已经修复该漏洞

版权

版权声明:本文为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如下:
mirai_ptea_xx_exp_p

受影响设备分析

通过探测我们发现至少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趋势如下:
Snip202110630_10

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

样本分析

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

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打开样本看到的字串信息如下所示,几乎没有可读的信息。

ptea_strtab

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

密钥为:

0xC26F6A52 0x24AA0006 0x8E1BF2C5 0x4BA51F8C

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

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

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

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

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

ptea_strtab
表项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建立连接

ptea_strtab

可以看出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"