Notepad++ v8.9.6.2 — 通过 HMAC 竞争条件实现攻击者控制的 UserCommand 执行
| GHSA | GHSA-qm4c-qg8p-qfcr |
| CVE | CVE-2026-52885 |
| 严重程度 | 高 |
| 受影响版本 | v8.9.6.2 |
| 已修复版本 | v8.9.6.4 |
| 补丁提交 | 4f7563c |
| 受影响文件 | PowerEditor/src/NppCommands.cpp :4328–4359 |
| 平台 | Windows 11 Pro x64 |
| 所需权限 | 标准用户(对 shortcuts.xml 有写入权限) |
| 研究者 | v3s9er |
| 披露日期 | 2026-06-05 |
Notepad++ v8.9.6.2 引入了对 shortcuts.xml 的 HMAC-SHA256 完整性检查,以防止注入任意 UserCommand 条目。
该检查存在 TOCTOU 缺陷:HMAC 在触发时针对磁盘上的文件进行验证,但执行的命令是从启动时加载的内存中的 _userCommands 向量读取的。这两个来源从未重新同步。
能够写入 shortcuts.xml 的攻击者可以在启动前植入恶意命令,让 Notepad++ 将其加载到内存中,恢复磁盘上的合法文件,然后触发命令。HMAC 检查通过(磁盘是干净的),但恶意载荷执行(内存是过时的)。
完整性机制验证当前磁盘上的 shortcuts.xml,而命令执行使用之前加载的内存表示。由于这两个数据源是独立的,攻击者可以在启动前修改文件,使其被解析到内存中,然后在命令执行前恢复原始文件——导致 HMAC 检查通过,而恶意载荷运行。
NppCommands.cpp 第 4328–4359 行:
// 检查时机 — 读取磁盘文件
std::string currentHMAC = computeHMAC(
getMachineGUID(),
getFileContent(nppParams.getShortcutsPath().c_str())
);
if (currentHMAC != nppGUI._shortcutsXmlHmacInConfig) { return; }
// 针对磁盘验证
// 使用时机 — 读取内存向量(启动时加载,从未刷新)
int i = id - ID_USER_CMD;
const vector<UserCommand>& cmds = nppParams.getUserCommandList();
UserCommand ucmd = cmds[i];
Command cmd(string2wstring(ucmd.getCmd(), CP_UTF8));
cmd.run(_pPublicInterface->getHSelf()); // 从内存执行载荷
| 操作 | 来源 | 时机 |
|---|---|---|
| 检查(HMAC) | 磁盘文件 | 触发时 |
| 使用(执行) | 内存向量 | 启动时加载 |
| 时间差 | 攻击者可控 | 启动 → 触发 |
前提条件: 对 shortcuts.xml 有写入权限
notepad++.exe 同一目录%AppData%\Notepad++\步骤:
shortcuts.xml 覆盖为恶意版本(cmd=calc.exe)_userCommands 从恶意文件填充shortcuts.xml 恢复到磁盘 — HMAC 现在与 config.xml 匹配WM_COMMAND(21000)(ID_USER_CMD+0)或按下 Ctrl+Alt+P_userCommands[0] → calc.exe 运行结果: 在当前用户权限下执行攻击者控制的命令。无需管理员、无需UAC、无用户警告。
要求:
doLocalConf.xml 存在于 Notepad++ 目录中(便携模式)config.xml 中包含合法 shortcuts.xml 的有效 HMAC.\poc_8962_toctou.ps1 -NppPath "C:\path\to\notepad++.exe"
已验证: calc.exe 生成(PID 35932),磁盘全程显示合法的 shortcuts.xml。
修复(提交 4f7563c)将 HMAC 验证移至启动时,与初始文件解析同时进行。检查和使用现在同时操作同一对象,消除了 TOCTOU 窗口。
| v8.9.6.2(易受攻击) | v8.9.6.4(已修复) | |
|---|---|---|
| HMAC 检查 | 触发时,重新读取磁盘 | 启动时,与解析使用相同字节 |
| 命令来源 | 启动时加载的内存 | 启动时验证的内存 |
| TOCTOU 窗口 | 启动 → 触发 | 已消除 |
| 时间 | 事件 | 状态 |
|---|
| T+0.0秒 | 攻击者写入恶意 shortcuts.xml | 磁盘=恶意 |
| T+0.0秒 | 攻击者启动 Notepad++ | 磁盘=恶意 |
| T+0.1秒 | Notepad++ 读取 shortcuts.xml → _userCommands[0]="calc.exe" | 内存=恶意 |
| T+4.2秒 | Notepad++ 完全加载 | 内存=恶意 |
| T+4.2秒 | 攻击者恢复合法 shortcuts.xml | 磁盘=合法 / 内存=恶意 |
| T+4.7秒 | 攻击者发送 WM_COMMAND(21000) | |
| T+4.7秒 | HMAC 检查:从磁盘读取合法文件 → 通过 | 检查通过 |
| T+4.7秒 | 执行:从内存读取 _userCommands[0] → calc.exe | 利用成功 |
| T+4.7秒 | calc.exe 生成(PID 35932) | 已确认 |
| 类型 |
| 任意命令执行 |
| 是否需要管理员 | 否 |
| UAC 提示 | 否 |
| 可检测性 | 低 — 磁盘全程显示合法文件;载荷仅存在于内存 |
| CVSS(估计) | AV:L / AC:H / PR:L / UI:N / S:U / C:H / I:H / A:H |