The Exploit
An attacker only needs local access to the user's Notepad++ profile files so they can corrupt the File Browser command interpreter setting.
## Set Notepad++ FileBrowser command interpreter to a malicious executable
powershell -Command "
\$cfg = '$env:APPDATA\\Notepad++\\config.xml'
(Get-Content -Raw \$cfg) -replace '<GUIConfig name=\"CommandLineInterpreter\">.*?</GUIConfig>', '<GUIConfig name=\"CommandLineInterpreter\">C:\\Windows\\System32\\calc.exe</GUIConfig>' |
Set-Content -Path \$cfg -Encoding utf8
"
## Then open Notepad++, open File Browser, choose any existing folder, and click "Cmd Here"
## The vulnerable code uses the attacker-controlled _commandLineInterpreter and runs it with `path`.
When the request lands, Notepad++ spawns the attacker-chosen executable instead of a safe shell. The app passes the selected path directory to Command::run(), which means any valid folder selection triggers execution of the malicious interpreter.
What the Patch Did
Before:
if (doesPathExist(path.c_str()))
{
Command cmd(NppParameters::getInstance().getNppGUI()._commandLineInterpreter.c_str());
cmd.run(nullptr, path.c_str());
}
After:
if (doesPathExist(path.c_str()))
{
if (cmdID == IDM_FILEBROWSER_CMDHERE)
{
Command cmd(L"%COMSPEC%");
cmd.run(nullptr, path.c_str());
}
else
{
static wchar_t psPath[512] = { L'\0' };
if (psPath[0] == L'\0')
{
const wchar_t* subkey = L"SOFTWARE\\Microsoft\\PowerShell\\1\\ShellIds\\Microsoft.PowerShell";
const wchar_t* valueName = L"Path";
HKEY hKey = nullptr;
LONG status = ::RegOpenKeyExW(HKEY_LOCAL_MACHINE, subkey, 0, KEY_READ, &hKey);
if (status != ERROR_SUCCESS) return;
DWORD bufSize = sizeof(psPath);
status = ::RegGetValueW(hKey, nullptr, valueName, RRF_RT_REG_SZ, nullptr, psPath, &bufSize);
::RegCloseKey(hKey);
if (status != ERROR_SUCCESS) return;
}
Command powerShell(psPath);
powerShell.run(nullptr, path.c_str());
}
}
The fix removes the user-configurable NppParameters::getInstance().getNppGUI()._commandLineInterpreter from the command execution path and replaces it with system-controlled values: %COMSPEC% for the CMD action and a registry-resolved PowerShell path for the PowerShell action.
Root Cause
This is a command execution / untrusted interpreter path bug (CWE-78). The attacker-controlled configuration value NppParameters::getInstance().getNppGUI()._commandLineInterpreter flowed directly into Command cmd(...), crossing the boundary from user-editable settings into an OS process-launch call. The selected file browser path was only checked for existence, not used to limit what interpreter binary could run, so any existing directory triggered execution of the malicious interpreter.
Why It Works
The single load-bearing change is replacing the interpreter constructor argument. In the vulnerable code, Command cmd(NppParameters::getInstance().getNppGUI()._commandLineInterpreter.c_str()); handed control of the executable path to a configuration setting. Once that line is replaced with either Command cmd(L"%COMSPEC%"); or Command powerShell(psPath);, attacker input no longer determines which binary is launched. The added registry lookup and cmdID branch maintain the intended behavior for both CMD and PowerShell variants while hardening the interpreter selection.
If the patch had left the original interpreter selection in place and only added the registry lookup, the bug would still be exploitable. The registry code protects only the PowerShell branch, and the cmdID == IDM_FILEBROWSER_CMDHERE branch is the actual source of the vulnerability for the "Cmd Here" action.
Hardening Checklist
- Do not use user-editable configuration values as the executable path for process creation; use fixed system values like
%COMSPEC%or a known Windows registry path. - When launching a shell, resolve the interpreter via trusted system APIs (
RegGetValueW,GetEnvironmentVariableW) rather than reading a config string. - Whitelist only approved binaries for execution and avoid passing user-controlled strings directly into
CreateProcessWor equivalent APIs. - Use explicit command interpreter paths and avoid shelling out through user-supplied command-line interpreter settings.
- Validate that any path used for execution is a real system binary before calling
Command::run().
References
- https://nvd.nist.gov/vuln/detail/CVE-2026-48800