Notepad++ Zero-Click RCE via Path Traversal (CVE-2026-52884)

ringzeropirate1 pts0 comments

CVE-2026-48800 Bypass · Advisory · notepad-plus-plus/notepad-plus-plus · GitHub

//repos/advisories/show" data-turbo-transient="true" />

Skip to content

Search or jump to...

Search code, repositories, users, issues, pull requests...

-->

Search

Clear

Search syntax tips

Provide feedback

--><br>We read every piece of feedback, and take your input very seriously.

Include my email address so I can be contacted

Cancel

Submit feedback

Saved searches

Use saved searches to filter your results more quickly

-->

Name

Query

To see all available qualifiers, see our documentation.

Cancel

Create saved search

Sign in

//repos/advisories/show;ref_cta:Sign up;ref_loc:header logged out"}"<br>Sign up

Appearance settings

Resetting focus

You signed in with another tab or window. Reload to refresh your session.<br>You signed out in another tab or window. Reload to refresh your session.<br>You switched accounts on another tab or window. Reload to refresh your session.

Dismiss alert

{{ message }}

notepad-plus-plus

notepad-plus-plus

Public

Notifications<br>You must be signed in to change notification settings

Fork<br>5.3k

Star<br>28.3k

CVE-2026-48800 Bypass

High

donho<br>published<br>GHSA-p58x-r3c9-x9p6<br>May 31, 2026

Package

No package listed

Affected versions

v8.9.6.1

Patched versions

v8.9.6.2

Description

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<br>Resolved Path<br>Warning?<br>Result

C:\Users\[USERNAME]\Downloads\mimikatz.exe<br>Untrusted path<br>✅ Yes — warning shown<br>Blocked

C:\Windows\System32\..\..\Users\[USERNAME]\Downloads\mimikatz.exe<br>Same mimikatz.exe<br>❌ No warning<br>Executes silently

C:\Program Files\..\..\Users\[USERNAME]\Downloads\payload.exe<br>Same payload<br>❌ No warning<br>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<br>Resolved Executable<br>Warning?<br>Result

cmd.exe /c calc.exe<br>C:\Windows\System32\cmd.exe (trusted)<br>❌ None<br>Executes silently

powershell.exe -ExecutionPolicy Bypass -Command calc.exe<br>C:\Windows\System32\...\powershell.exe (trusted)<br>❌ None<br>Executes silently

rundll32.exe javascript:...\mshtml,RunHTMLApplication<br>C:\Windows\System32\rundll32.exe (trusted)<br>❌ None<br>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) {<br>wchar_t trustedDirs[][MAX_PATH] = {<br>L"C:\\Program Files\\",<br>L"C:\\Program Files (x86)\\",<br>L"C:\\Windows\\System32\\",<br>L"C:\\Windows\\",<br>};<br>for (auto& trusted : trustedDirs) {<br>if (PathIsPrefix(trusted, path)) // BUG: checks prefix without canonicalization<br>return true;<br>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) {<br>wchar_t canonicalPath[MAX_PATH] = {};<br>// Resolve .., ., and redundant separators<br>if (!PathCanonicalize(canonicalPath, path))<br>return false;<br>// NOW check against trusted directories<br>for (auto& trusted : trustedDirs) {<br>if (PathIsPrefix(trusted, canonicalPath))<br>return true;<br>return false;

Attack Scenarios

Scenario 1: Direct config file write

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

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

When the user presses Alt+F1 (or...

path windows trusted system32 users plus

Related Articles