吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 8|回复: 0
上一主题 下一主题
收起左侧

[分享] 【全网首发】Weax与Sorry勒索病毒席卷全国中小企业,深度还原全链路攻击,疑似黑客...

[复制链接]
跳转到指定楼层
楼主
solar应急响应 发表于 2026-7-6 15:25 回帖奖励
使用论坛附件上传样本压缩包时必须使用压缩密码保护,压缩密码:52pojie,否则会导致论坛被杀毒软件等误报,论坛有权随时删除相关附件和帖子!
病毒分析分区附件样本、网址谨慎下载点击,可能对计算机产生破坏,仅供安全人员在法律允许范围内研究,禁止非法用途!
禁止求非法渗透测试、非法网络攻击、获取隐私等违法内容,即使对方是非法内容,也应向警方求助!
本帖最后由 solar应急响应 于 2026-7-6 15:34 编辑

写在前面

关于Weaxor家族(Rox、Weax、Roxeaw)的往期分析文章,也收获了不错的反响,感兴趣的读者可以翻看历史文章。

最近一段时间,.weax.sorry两个勒索病毒在国内中小企业圈里反复作案。Solar应急响应团队接连处置了多起相关事件,把这些案例放在一起对比,发现了一个高度一致的共同点:受害者无一例外都部署了"管家婆"系列管理软件(管家婆辉煌Ⅱ/TOP+等)。

Weaxor家族将文件加密为.weax后缀

Tellyouthepass将文件加密为.sorry后缀

奇怪的是,这些受害者的环境里:

  • SQL Server 的 1433 端口并没有对公网开放
  • 数据库账号也不是弱口令(sa 密码复杂度足够);
  • 边界设备、Web 应用上找不到明显的入侵痕迹
  • 可偏偏就是被加密了,而且加密发生前 SQL Server 日志里干干净净,没有任何爆破、任何异常登录

一个"没开数据库端口、没弱口令、没 Web 漏洞"的内网,是怎么被勒索病毒从公网打进来的?这是本篇要回答的核心问题。

经过深度的逆向分析与活机验证,并通过其它渠道交叉确认,我们最终把入口锁定在了管家婆几乎默认对外开放的211端口上,一个被绝大多数运维忽视、却能让攻击者在完全不需要任何口令、不需要加密狗、不需要客户端的情况下,可导致以 SQL Server 服务身份执行系统级操作,实际权限需以现场服务账户为准的未授权远程代码执行(RCE)漏洞。

通过多渠道验证,漏洞利用入口确认为211端口

更致命的是,这个漏洞不产生任何登录日志,传统的安全设备和日志审计对它几乎无效,这正好解释了为什么受害者环境里"没有任何攻击痕迹"。

本文将从零开始,完整还原排查、逆向、抓包、靶场验证的全过程,每一步都给出可复现的操作与截图,供同行复测与防守参考。

本文整体攻击链路概览

值得警惕的是,截至本文发布时,这两个家族的攻击目标正在进一步扩展。 此前WeaxorTelyouthepass主要利用HW(护网)期间的1day和Nday漏洞实施攻击,但近期态势发生了明显变化,两个家族开始将矛头指向管家婆物联通、管家婆ERP等更多产品线,且基本上都是以0day漏洞作为突破口。这种攻击面的快速转移和武器化速度,与以往的攻击模式形成了强烈反差。

管家婆系列软件在互联网上可以直接下载安装包,任何人都能轻易获取目标程序。从软件公开下载到漏洞被武器化投入实战,这个周期被压缩得极短。结合此次攻击目标快速扩展、0day漏洞接连出现的特点,我们不得不提出一个严肃的假设:勒索组织可能正在借助AI辅助进行自动化漏洞审计,大幅缩短了从"发现漏洞"到"勒索变现"的时间线。这不再是传统的"漏洞挖掘-分析利用-传播攻击"线性流程,而可能演变成AI驱动的批量审计、快速武器化、精准投放的闭环模式。

一、线索汇聚:两起案例的共同点

1.1 案例A:Tellyouthepass家族(某制造业务系统,加密后缀.sorry

第一起案例是一家制造业公司被.sorry加密。在其磁盘镜像里提取到了服务端程序包XXXserver.rar,解压后发现一个关键文件:ScktSrvr.exe(路径hs_extract\ScktSrvr.exe,680960字节),这是后续逆向的核心样本之一。


解压XXXserver.rar后的目录列表,ScktSrvr.exe高亮标注

这台机器对公网开放的端口里,211端口(TCP)处于监听状态,而且加密发生后仍然存活;而1433(SQL Server)并未对公网放行。

1.2 案例B:Weaxor家族(某企业ERP,加密后缀.weax

第二起案例是.weax病毒作案。我们拿到了这台机器的SQL Server默认追踪日志(log_*.trc),里面有一条铁证级的记录:

2026-06-26 19:47:59.30  spid56  Configuration option 'Ole Automation Procedures' changed from 0 to 1
LoginName = sa   (经 .Net SqlClient)


受害者SQL默认trace log_105.trcOle Automation Procedures changed from 0 to 1的记录(使用fn_trace_gettable或日志查看器打开)

这条记录说明:攻击者在加密前一天,用 sa 身份开启了 SQL Server 的 OLE Automation Procedures(这是后续执行 OS 命令的前置动作)。但问题是:sa 是怎么被拿到的?1433 又没开。

1.3 共同点收敛

把两起案例并排放:

维度 案例A(.sorry) 案例B(.weax)
行业 制造业公司 企业ERP
加密后缀 .sorry .weax
1433端口开放
数据库弱口令
Web漏洞痕迹
211端口开放
管家婆部署

线索全部指向同一个东西:那个开着的 211 端口。它不是 Web 端口、不是数据库端口,它是管家婆客户端连服务端用的应用层 socket 端口。在 1433 不暴露的前提下,唯一能让 sa 在本地被执行的路径,就是经这个 211 端口:借管家婆应用自身的 sa 连接,绕过数据库端口的封堵

目标地址211端口可达性确认

研究方向由此明确:搞清楚211端口背后这个程序是什么、协议是什么、能不能在无凭据的情况下执行任意操作。

二、管家婆是什么,211端口又是什么

2.1 下载管家婆安装包,本地搭建

为了可控地复现,我们在本地下载了一份管家婆辉煌ⅡTOP+15的安装包,并在一台干净的Windows Server上完成安装(服务端+数据库+客户端)。


管家婆辉煌ⅡTOP+15安装过程截图(安装向导/安装完成界面)

安装完成后,C:\GRASP TOP+\目录列表如下,scktsrvr.exeGraspSvr.exe高亮标注,这两个文件是本文的主角:


C:\GRASP TOP+\目录列表,scktsrvr.exeGraspSvr.exe高亮标注

安装完毕后,用客户端GraspNet.EXE登录服务端、进入一个测试账套,确认整套环境正常工作。

靶场拓扑如下:


靶场进程截图:任务管理器或PowerShell Get-Process scktsrvr,GraspSvr,sqlservr显示三者运行中及路径

2.2 211 端口的真身:scktsrvr.exe

我们顺着 211 端口找到了对应的进程:C:\GRASPⅡTOP+\scktsrvr.exe

scktsrvr.exe 到底是什么? 这里用大白话解释一下,方便不熟悉 Delphi 技术栈的同学理解:

管家婆这类软件是典型的 C/S(客户端/服务端)两层半架构。你在前台操作的客户端(GraspNet.EXE),并不直接连数据库,而是先连到服务端的一个"中间程序";这个中间程序再统一去连数据库、并把结果回传给客户端。

中间件逻辑运行示意图:客户端→scktsrvr.exe→GraspSvr.exe→SQL Server

scktsrvr.exe 扮演的就是这个"数据中转站 / 中间件"的角色,它来自 Borland/Delphi 的技术体系,正式名称叫 Borland Socket Server(或 Delphi Socket Server),是 Delphi 开发的多层应用(MIDAS / DataSnap 架构)里专门用来在客户端和服务端之间搬运数据的 socket 传输层程序。

可以把它通俗地理解成:

它是管家婆客户端和服务端之间的"快递中转站",客户端把要查的数据、要执行的指令打包发给它(211 端口),它转交给服务端的业务程序(GraspSvr),业务程序再去数据库取数/写数,原路返回。

这个"快递中转站"用的是一个叫 MIDAS 的私有二进制协议(不是 HTTP)。理解这一点非常关键,因为它决定了后面两件事:

2.3 为什么传统防御对它无效

第一,它不是 Web 漏洞。 211 端口上跑的不是 HTTP,是 MIDAS 私有二进制协议。WAF、Web 日志、URL 审计统统看不到它:它根本不在 Web 这条管道里。

第二,它默认不产生登录日志。 我们在逆向时确认:scktsrvr 的连接建立阶段没有任何应用层认证(注册表里的鉴权拦截器 InterceptGUID 是空的),握手成功也不会像 SQL Server 那样写一条登录记录。也就是说,攻击者连上来、握个手、调几个方法,全程在操作系统和应用日志里留不下任何"谁连进来了"的痕迹

这就完美解释了之前的悖论:受害者环境没有痕迹,不是因为没被打,是因为这条管道压根不记日志。

注册表HKLM\SOFTWARE\WOW6432Node\Borland\Socket Server的内容:InterceptGUID值为空(无鉴权拦截器)

既然它是私有协议、又不记日志,那想搞清楚它到底怎么工作、有没有漏洞,就只能靠两条路

  1. 静态逆向:把 scktsrvr.exe(以及它拉起的业务程序 GraspSvr.exe)丢进 IDA,看协议是怎么解析的、有没有鉴权;
  2. 动态抓包:搭一个中间人(MITM),让真实的管家婆客户端从中间过一遍,把协议的真实字节抓下来对照。

后面的方向,我们就分别走这两条路。

三、逆向 scktsrvr.exe:协议与漏洞点

我们把靶机上 C:\GRASPⅡTOP+\scktsrvr.exe(与受害者同款,文件版本 7.0.4.453)拷贝出来,放进 IDA Pro

IDA Pro成功载入scktsrvr.exe程序

3.1 第一个发现:握手阶段没有任何认证

在逆向协议之前,我们先确认了一个最关键的前提:这个服务要不要登录?

scktsrvr 的运行参数读自注册表 HKLM\SOFTWARE\WOW6432Node\Borland\Socket Server。Delphi 的 Socket Server 设计上支持一个叫 InterceptGUID 的"鉴权拦截器",如果配置了这个 GUID,每条连接都会先经过这个 COM 拦截器做认证。但我们查注册表:

读取注册表配置的代码段

证实InterceptGUID 为空

InterceptGUID 是空的,意味着没有任何鉴权拦截器被插入连接流程。 任何人 TCP 连上 211,都能直接进入 MIDAS 协议层,不存在"用户名密码"这一关。这是整个漏洞的根。

3.2 定位协议主循环:分发器 sub_48B820

握手成功后,连接进入解释器主循环。在 IDA 里跳到地址 0x48B820(函数 sub_48B820):

// sub_48B820 —— 分发器
v2 = (*(unsigned __int8 (...))(*(DWORD*)a2 + 32))(a2, ...);  // 读取 1 字节 action
switch (v2) {
    case 2:  sub_48C050(...); break;  // → 方法调用 (Invoke)
    case 3:  sub_48BEE4(...); break;
    case 4:  sub_48BD64(...); break;  // → 连接/握手
    case 5:  sub_48BE5C(...); break;
    case 16: sub_48BBDC(...); break;
    case 18: sub_48BA54(...); break;
    default: ...
}

IDA反编译,展示读取action字节+switch的画面,高亮case 2→sub_48C050

这里先记下一个后来证明非常关键的认知:服务器先读一个字节作为动作类型(action),再根据它分发。我们一开始误以为这个 action 字节在数据包的载荷里,导致后面构造测试包时反复踩坑

3.3 方法调用入口:Invoke 解释器 sub_48C050

action=2 这条路进入 0x48C050**sub_48C050,这是整个方法调用的解释器。它连续读取 6 个"变体(Variant)",然后直接调用 AppServer 的 IDispatch::Invoke**,这是 COM 里的通用方法调用接口。

// sub_48C050 —— Invoke 解释器(精简)
sub_48A89C(..., v35); v49 = sub_410B00();   // 变体1 = dispatch引用(哪个AppServer)
(*v50)[9](&v44, v49, ...);                  // 解析出 IDispatch 对象
sub_48A89C(..., v34); v48 = sub_410B00();   // 变体2 = dispid(调哪个方法)
sub_48A89C(..., v33); v47 = sub_410B00();   // 变体3 = wFlags
sub_48A89C(..., v32); v46 = sub_411F08();   // 变体4 = 标志
sub_48A89C(..., v31); v39 = sub_410B00();   // 变体5 = cArgs(参数个数)
sub_48A89C(..., v30); v40 = sub_410B00();   // 变体6 = cNamedArgs
// ...读取 cArgs 个参数变体...
v11 = (*(int (...))(*(DWORD*)v44 + 24))(v44, v48, ...);  // ← IDispatch::Invoke (vtable第6槽)

IDA反编译,展示读6个变体+调IDispatch::Invoke的画面

这段代码揭示了一个决定性事实:从"握手成功"到"IDispatch::Invoke 被调用"之间,没有任何权限校验代码,没有"未登录返回错误"、没有"权限不足"、没有黑名单。换句话说,只要握手能过,AppServer 暴露的任意一个方法(任意 dispid、任意参数)都可以被自由调用。至于能不能干坏事,就取决于 AppServer(GraspSvr)里有没有"能执行 SQL / 命令"的方法。

3.4 变体是怎么在网络上编码的:变体读取器 sub_48A89C

上文的 6 个"变体"和参数,在网络上是怎么传的?这要看变体读取器 0x48A89C**sub_48A89C)**。它的核心逻辑:

// sub_48A89C —— 变体读取器(精简)
read(v4, &v16, 4);                         // 先读 4 字节 = 变体类型
if (v16 == 0x4000)      { ... }            // byref
else if (v16 & 0x2000)  { sub_48A24C(...); } // 数组
else switch (v16 & 0xFFF) {
    case 0x0: ... break;                    // varEmpty
    case 0x8:  ... read(charcount); read(2*charcount UTF16); break;  // 字符串
    case 0xD:  ... break;
    default:
        read(v4, a4+4, word_491354[v16 & 0xFFF]);  // 其它按大小表读
}

IDA反编译,展示sub_48A89C与default分支引用大小表word_491354的画面

也就是说,每个变体在网络上长这样:

[类型:4字节LE][值]
 - 整数(varInteger=3):  03 00 00 00 + 4字节值
 - 字符串(varOleStr=8): 08 00 00 00 + 字符数(4) + UTF-16LE
 - 布尔(varBoolean=0x0b): 0b 00 00 00 + 2字节
 - 引用(byref=0x4000): 与上述 OR

顺带一提:这个 default 分支里 word_491354[type & 0xFFF] 的大小表只有 20 个有效条目,type≥0x14 会越界读到指针碎片,这是个真实的内存安全缺陷代码模式,但我们在管家婆辉煌ⅡTOP+15所需的scktsrvr.exe 7.0 上实测它被 Delphi 异常机制中和、不构成可利用溢出。这部分会在后面体现。

到这一步,静态逆向已经告诉我们:

  1. 无认证InterceptGUID 空);
  2. 握手后可自由调用 AppServer 任意方法sub_48C050 无鉴权);
  3. 变体的线编码格式已经清楚。

但还有一个关键的"差一步":这些 action、变体、方法,到底是怎么打包成一个完整数据包的?数据包的包头长什么样?光靠 IDA 推断,我们反复构造测试包都失败了(一直报 Invalid variant type / Invalid class string)。这一步必须靠真实流量来证实。

四、MITM 抓真实流量:拿到地面真相

4.1 为什么必须抓包

逆向告诉我们"协议的零件长什么样",但没告诉我们"零件怎么组装成一个真实的数据包"。我们在 IDA 里推断的包头格式、action 位置,和服务器实际期望的对不上,连续碰壁:

早期构造的测试包,服务器返回Invalid variant type 01 db 00 00(错误签名+错误变体串)的终端截图

这种时候最稳的办法,不是继续猜,而是让一个真实的管家婆客户端从我们的"中间人"上过一遍,把真实的数据包字节抓下来对照

4.2 搭建中间人(MITM)

我们的测试机(Linux,IP 192.168.115.18)和管家婆客户端(Windows,IP 192.168.115.22)在同一个局域网。方案很直接:

  1. 在 Linux 上跑一个 TCP 中继脚本 midas_mitm.py监听 0.0.0.0:211,把所有数据原样转发到云端靶机 115.xxx.xx.xx:211,同时把双向的每一个字节都记录到日志文件。
  2. 在管家婆客户端的登录界面,把"服务器地址"从云端 IP 改成我们 Linux 的 IP 192.168.115.18(端口仍是 211)。
  3. 客户端照常登录账套。对客户端来说,它以为自己连的是服务器;实际上数据先经过我们的中继,被完整记录后再转给真服务器。

midas_mitm.py 的核心逻辑:

# 监听 211, 转发到 115.xxx.xx.xx:211, 双向逐字节记录
LISTEN = ("0.0.0.0", 211)
UPSTREAM = ("115.xxx.xx.xx", 211)
LOG = "midas_mitm.log"   # ← 抓到的所有字节都落在这里
# 每条消息记录: 方向(C2S/S2C) + 字节数 + hex + ascii

客户端登录界面,服务器地址改为 192.168.115.18 的截图。

Linux终端运行python3 midas_mitm.py,显示"MITM已启动 0.0.0.0:211 -> 115.xxx.xxx.xx:211"的画面

4.3 抓到的真实流量:脚本运行结果

客户端登录一次(进入账套、查询数据),我们在脚本运行结果里抓到了 880 条消息的完整双向字节流。这是后面还原协议的"地面真相"。

查看结果,能看到第一条客户端发来的握手包(92 字节):

[客户端→服务器 C2S] 92字节:
  hex: 04 da 00 00 54 00 00 00 08 00 00 00 26 00 00 00
       7b 00 36 00 39 00 43 00 34 00 ...(CLSID 的 UTF-16LE)...
  ascii: ....T.......&...{.6.9.C.4.1.9.F.0.-.1.E.D.F.-.4.D.C.A.-.B.1.2.9.-.A.3.F.8.4.1.E.9.A.9.2.A.}.

midas_mitm.py运行后日志文件的开头部分

把这个包逐字节拆开,协议的"组装方式"终于现出原形

04 da 00 00     ← 签名(前2字节是"动作+方向", 04=握手, da=客户端发)
54 00 00 00     ← 载荷长度 = 0x54 = 84 字节
08 00 00 00     ← record_id = 8 (连接请求)
26 00 00 00     ← 字符串字符数 = 0x26 = 38
{69C419F0-1EDF-4DCA-B129-A3F841E9A92A}  ← 管家婆 GraspSvr 的 CLSID(UTF-16LE, 38字符)

4.4 抓包纠正的关键错误:动作类型在"签名"里,不在载荷里

继续往下看脚本运行结果,我们看到了握手之后的下一条客户端消息,它的开头是:

03 da 00 00 ...   ← 注意! 签名第一个字节变成了 03 (不再是 04)

再下一条又是:

02 da 00 00 ...   ← 又变成了 02

midas_mitm.py解析输出中连续三条消息,签名首字节依次为04 da(握手)、03 da(查方法名)、02 da(方法调用),展示"动作在签名首字节"的铁证

这就是我们在前面末尾踩的坑的答案!数据包的"动作类型"不在载荷里,而是藏在签名(前4字节)的第一个字节里。

签名首字节 含义
04 da 握手/连接(载荷 = record_id=8 + CLSID)
03 da GetIDsOfNames(按方法名查 dispid)
02 da Invoke(载荷 = 6 个变体 + 参数,无前导字节)
04/01 db 服务器响应(04 db=正常,01 db=错误)

我们之前一直把 02(Invoke 动作)放进载荷里发,服务器却把它当成了握手(action 4)来解析,所以一直报 Invalid class string差一个字节的位置,卡了好久。抓包一对照,豁然开朗。

4.5 抓包还原出的完整方法调用序列

用配套的解析脚本 parse_mitm.pymidas_mitm.log 整个解一遍,真实客户端登录一次的过程清清楚楚:

[0] 握手 CLSID={69C419F0-...}                      → 服务器回 cookie=0
[2] ACT3 查方法名 "ConnectDb"                        → 回 dispid 0x60030000
[4] INVOKE dispid=0x60030000 (ConnectDb)             → 注册客户端
[6] ACT3 "ChangeDb"                                  → 回 dispid 6
[8] INVOKE dispid=6 (ChangeDb) arg="hherpmaster"     → 选择账套(主账套)
...
[34] INVOKE dispid=5 (OpenSQL) arg="Select SubValue From Sysdata ..."  → 设置SQL
[36] INVOKE dispid=0x1312d01 (AS_GetRecords) arg="pv_qry_Open"         → 执行SQL并取数


parse_mitm.py的解析输出,展示上述方法调用序列(每条含dispid、参数)

这条序列给了我们最后两块拼图:

  1. 方法名 → dispid 是动态查的(ACT3,按名字解析),所以 dispid 跨版本都一样能用;
  2. 真正把 SQL 发到数据库执行的,是 OpenSQL + AS_GetRecords 这一对组合,OpenSQL 只是"设置 SQL 文本",AS_GetRecords 才"执行 + 取数"。

至此,逆向 + 抓包两条路汇合,协议的每一个细节都清楚了。下面,我们用它构造完整的攻击 PoC,在靶场上跑通。

五、构造 PoC:从无凭据握手到 SYSTEM

把前面还原出的协议,写成最小可用 Python 脚本(仅标准库),就是我们的 PoC。完整脚本见配套文件,下面把关键步骤一步步拆开。

5.1 第一步:无凭据握手

按前面字节布局,构造握手包发出去:


PoC脚本运行,输出"握手成功"——无任何凭据,直接建立连接

5.2 第二步:踩中"必须先初始化会话"的坑

握手成功后,我们最初直接发 OpenSQL(SQL) + AS_GetRecords,结果服务器一直回 Provider not exported(provider 未导出),SQL 根本发不到数据库(计划缓存里查无此 SQL)。

对照 脚本结果里真实客户端的序列才明白:客户端在查询前,必然先调用 ConnectDb 注册客户端、再调用 ChangeDb 选择一个账套。不选账套,provider 就不会被"导出"。 这是协议状态机的一个隐藏前提。

补上这两步:

INVOKE(dispid=ConnectDb, args=[客户端IP, 主机名])    # 注册客户端
INVOKE(dispid=ChangeDb,  args=["hherpmaster"])      # 选择账套 ← 关键!

这里有个让攻击"零先验知识"的关键点ChangeDb 选的账套名 hherpmaster,是管家婆默认的系统/主账套,每一套管家婆装好就有、名字固定,攻击者根本不需要事先知道受害者的业务账套叫什么。而且 xp_cmdshell 是 SQL Server 服务器级的功能,不依赖具体业务账套,从 hherpmaster 一样能执行。

如果攻击者还想精准打击某个业务账套,甚至可以连上后直接 select name from master.sys.databases 自己把所有账套列出来,我们在靶场上实测,一条 SQL 就把 gjp20260702 / hherpmaster / ... 全列出来了。

5.3 第三步:执行任意 SQL,进而执行 OS 命令

会话初始化完成后,方法调用链就和真实客户端一模一样了:

INVOKE(dispid=OpenSQL,      args=["exec master..xp_cmdshell 'echo PWNED > c:\\pwned.txt'"])
INVOKE(dispid=AS_GetRecords, args=[..., "pv_qry_Open"])   # 触发执行


完整PoC执行后的终端输出与靶场验证截图

AS_GetRecords 一旦触发,GraspSvr 就以 sa 身份把这条 SQL 送进了 SQL Server;xp_cmdshell 把命令交给了操作系统。


完整PoC执行后的终端输出与靶场验证截图

六、落地验证:SYSTEM 权限 + 取证自证

光说"能执行"不够,必须在靶场上把"执行结果"落成看得见、摸得着的物证。

6.1 物证一:操作系统命令落地,且是 SYSTEM 权限

我们用PoC 经 xp_cmdshell 在靶机 C:\ 写下标记文件 solar.txt。我们接着去验证:


靶机C:\solar.txt文件存在性验证截图

攻击者拿到的是 Windows 上最高权限的本地账户之一(NT AUTHORITY\SYSTEM,权限等同甚至略高于本地 Administrator,属用户态最高)。这是因为 xp_cmdshell 以 SQL Server 服务进程的身份执行命令,在我们这台靶机上,SQL Server 服务实测以 LocalSystem 运行,于是命令落地文件的 权限 就是 NT AUTHORITY\SYSTEM,攻击者由此掌控本机几乎所有资源:装勒索、停服务、清日志都可以直接做;横向移动到其它主机则通常还需进一步窃取/复用凭据(非一步到位)。

注意:并非所有管家婆部署的 SQL 服务都是 LocalSystem,不同版本/安装方式可能使用更低权限的服务账户(如 NETWORK SERVICE、虚拟服务账户 NT SERVICE\MSSQLSERVER 或域账号),那种情况下 xp_cmdshell 的本机权限会相应降低;但无论服务账户是什么,"未授权以 sa 执行任意 SQL"这个核心漏洞都成立,区别只在于落地 OS 命令时的权限高低。

6.2 物证二:未授权链路以 sa 执行 DDL(持久表)

为了进一步证明"未授权就能以 sa 执行任意 SQL",我们经同一条无凭据链路建了一张表:

create table grasp_rce_poc_211_4(id int, src varchar(80), ts varchar(30));
insert grasp_rce_poc_211 values(1,'PWNED-VIA-UNAUTH-MIDAS-211_4', cast(getdate() as varchar(30)));
select * from grasp_rce_poc_211_4;

执行后去查:


查询grasp_rce_poc_211_4表,确认记录存在

这张表持久存在于受害者数据库里,是"未授权 → sa 任意 SQL"的铁证。

6.3 回写一句话:为什么受害者环境没有痕迹

到这里,最开始的悖论彻底解开:

  • 不产生登录日志:MIDAS 握手无认证、不记录"谁连进来了";
  • 不触发登录失败告警:根本没用口令,没有"失败"可言;
  • 不经过 Web:WAF / Web 日志完全看不到;
  • 借用应用自身的 sa 连接:1433 没开也无所谓,命令是从应用内部发出去的;
  • 唯一的痕迹只在 SQL 默认 trace 里(且默认 trace 并非所有部署都会保留/被检查)。

所以受害者环境看起来没有痕迹,不是没被打,是这条管道天生不留常规脚印

七、这不是管家婆一个产品的问题:一类漏洞

研究过程中,有两个发现让我们意识到:把这事局限在"管家婆"上,是低估了。

7.1 案例 A 的 XXXServer,用的是同一套 scktsrvr

回到第一章案例 A(.sorry,制造业务系统),我们解压它的服务端包 XXXserver.rar,里面赫然也有一个 ScktSrvr.exe(680960 字节,版本 11.0.2804.9245,"CodeGear Socket Server"),和管家婆同源、同一个 Borland Socket Server


XXXserver\ScktSrvr.exe的文件属性:产品名"CodeGear Socket Server",版本11.0.2804.9245

进一步分析它的业务程序 ,XXXServerZz.exe发现它实现的是标准的 Delphi IAppServer(二进制里含标准 IAppServer 类型库 GUID {1AEFCC20-...}AS_ExecuteAS_GetProviderNames,以及一个关键属性 poAllowCommandText,允许 provider 直接执行调用方传入的 SQL)。

差别在于:管家婆用的是自己定制的 AppServer(有"账套"概念,方法名 ConnectDb/ChangeDb/OpenSQL);而 XXXServer 用的是标准 IAppServer,没有账套,攻击路径是 AS_Execute(provider, CommandText=任意SQL) 直接执行 SQL。但底层入口完全一样:都是 211 端口、都是 scktsrvr、都无认证。

7.2 一次同网段扫描,4 台不同应用,全是 scktsrvr

我们对靶机所在网段做了 211 端口扫描,一次就扫到 4 台开放 211 的主机。用我们的工具去连,全部返回同一个错误:

Object not available: {69XXX-XXX-XXX-XXX-A92A}

这个错误的意思是:这些机器上没注册管家婆的 GraspSvr 这个类:它们是别的 Delphi MIDAS 应用(不同的 AppServer、不同的 CLSID)。我们手里这把"管家婆钥匙"开不了它们的锁。

7.3 攻击者为什么"这么准":CLSID 字典

这就回答了一个读者可能早就想问的问题:既然每个应用 CLSID 不同,攻击者怎么知道该用哪个 CLSID 去连?

答案是:攻击者手里有一本 CLSID 字典。他们逆向了多种 Delphi MIDAS 应用的客户端,提取出各自 AppServer 的 CLSID(管家婆 {69C419F0-...}、XXXServer 的、还有别的……),攒成字典。扫描器先用 04 da 探针确认"这是台 scktsrvr/MIDAS",然后逐个试字典里的 CLSID,命中哪个,就知道对方是什么应用,再套对应的方法集打。

结论:这是一类漏洞,不是一个产品的漏洞。 任何"Delphi MIDAS socket 应用 + scktsrvr 无认证 + AppServer 有 SQL/命令执行能力"的组合都中招。管家婆只是其中最常见、暴露量最大的一类。

八、顺藤摸瓜:有没有"绕过数据库"的缓冲区溢出

既然 211 上跑的是个解析私有协议的程序,那除了"经数据库 RCE"这条应用层路径,协议解析层本身有没有内存破坏漏洞(缓冲区溢出),能不经过数据库直接打? 我们也深入挖了一下。

8.1 受害者日志里的铁证:scktsrvr 确实崩过

我们在案例 A 受害者的 Application.evtx 里发现:ScktSrvr.exe 有大量崩溃记录(30 条 Application Error,两组不同 WER 哈希),其中一条:


受害者Application.evtxScktSrvr.exe的AppCrash 1000事件详情

受害者崩溃点 0x4041d9 经反汇编确认落在 Delphi 运行时的 SEH 链链代码(mov fs:[0], eax),这是 SEH 链被破坏的强签名(栈溢出覆盖 SEH 后,异常处理二次崩溃的典型落点)。严格来讲,要坐实"可控 SEH 覆盖(可改写 handler 跳转 → RCE)"还需要一份崩溃转储来观察被覆盖的 SEH 记录;目前受害者侧只留存了崩溃日志,未取得 dump,故此处为"高度可疑的栈/SEH 破坏",而非"已证实的可控覆盖"。补全这一步需在 Wine 上复现 11.0 崩溃并抓 dump(后续工作)。

8.2 管家婆 7.0:有缺陷代码,但被异常机制中和

我们把目光转回管家婆辉煌ⅡTOP+15所用的 7.0(靶场同款),重点审计前面3.4 节那个变体读取器的 default 分支:

default:
    read(v4, a4+4, word_491354[v16 & 0xFFF]);   // 按大小表读 N 字节到栈缓冲

大小表 word_491354(IDA 地址 0x491354)只有 20 个有效条目(type 0–19);当变体类型 ≥0x14(越界)时,会读到表后面的指针碎片(如 type 0x14 → 31180),然后往只有 12 字节的栈缓冲里写 31180 字节,这是确凿的栈溢出代码模式

IDA查看word_491354大小表的内容,展示20个有效条目后的越界风险

但在 7.0 上实测打不死:发 type 0x14(带各种长度 payload),服务器全部优雅返回 Invalid variant type,进程始终存活(PID 不变)。

原因:所有能触发 default 分支的 type 都是"非法 type",Delphi 在用到被破坏的返回地址/SEH 之前,就先抛了 EVariantError,被外层分发器(sub_48B820,其栈帧未被破坏)的 SEH 兜住,清除了。内存写确实发生了,但崩不出来、控不了执行流。


Fuzz脚本对7.0发多种畸形变体,全部未成功,PID始终不变的终端截图

8.3 溢出研究的结论

版本 结论
管家婆辉煌ⅡTOP+15 有内存安全缺陷代码模式(latent),但不可利用(Delphi 异常机制中和,不崩、不可控执行)
scktsrvr 11.0(XXXServer) 有崩溃证明(c0000005 @ SEH、c00000fd),属另一版本的独立问题

所以诚实地讲:真正被勒索利用的、我们已经闭环的,是前面那条"未授权 MIDAS → SQL → RCE"的应用层路径,不是缓冲区溢出。

九、防御建议(按优先级)

  1. 立即收敛暴露面:211 端口禁止对公网开放。安全组 / 防火墙仅放行业务必需的可信 IP。这是性价比最高的一步,这个漏洞再严重,连不上就打不了,但缺点也很明显,一旦客户端IP有变化,就需要再加白新的客户端IP。
  2. 应用层鉴权:为 scktsrvr 配置 InterceptGUID 鉴权拦截器(若产品支持),或在 211 前置 VPN / 反向代理做强制认证,目前管家婆官方已在内部发布修复版本的Scktsrvr.exe程序。
  3. 收紧数据库:禁用或严格限制 xp_cmdshellOle Automation ProceduresAppServer 不应以 sa 连库,改用最小权限账号,即使被未授权调用,也执行不了 OS 命令。
  4. 监控盲区补盲:在 211 流量上部署协议级监测(识别 04 da / 04 db MIDAS 握手、异常 ACT3/Invoke),因为传统 Web/登录日志对它无效;SQL 默认 trace / 扩展事件重点盯 sp_configuresp_OACreatexp_cmdshell
  5. 多波防御:一旦被加密,恢复前务必清除持久化(计划任务 / 服务 / Run 键 / WMI 订阅 / 启动项 / System32 下可疑 exe)并封堵 211,否则恢复业务即等于重新把靶子立起来(攻击者可二次、三次再来)。

附录 :样本指纹(用于比对 / 鉴定 / 复现)

样本 大小 版本 厂商 / 产品 MD5 SHA256
scktsrvr.exe(管家婆辉煌ⅡTOP+15版本 scktsrvr.exe所用7.0,逆向主样本/靶场同款) 680448 7.0.4.453 Borland Software Corporation / Borland Socket Server bd48bdba38bc4d925e080183cb2dc8c1 f2b1ab212b0f02ab1da4b1e93a6d54e19f853b99b5aa4fa7b56d8d3946362978
ScktSrvr.exe(XXXServer 11.0,sorry 受害者同款,有崩溃铁证) 680960 11.0.2804.9245 CodeGear / CodeGear Socket Server 3aa210de0269010e6169f8736471ecf0 9a736ac6c1f359be7e2e44287581974ea84982d4416e72b84e3104f4dea7367e
GraspSvr.exe(管家婆 AppServer,COM 组件) 4430336 15.0.0.68 任我行软件有限公司 / 管家婆辉煌服务器 424317c15260bf7ebc92516cb90a7de4 8d169f57a7ab9399ea46a1cc30687b2bd23d3ee153d0a71a78f78b98e8b37808
midas.dll(Delphi MIDAS 客户端库) 264192 Borland / MIDAS 7f63290fddec173fd009bf3e7ffe6d6b b0d8a36a5cd4923517186b2f7a7e8820ff21691b8896c554eeb6764bbaaf3e4b

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - 52pojie.cn ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2026-7-7 03:00

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表