Notepad++ Plugin Autoload Persistence & Execution
{{#include ../../banners/hacktricks-training.md}}
Notepad++ will autoload every plugin DLL found under its plugins subfolders on launch. Dropping a malicious plugin into any writable Notepad++ installation gives code execution inside notepad++.exe every time the editor starts, which can be abused for persistence, stealthy initial execution, or as an in-process loader if the editor is launched elevated.
Writable plugin locations
- Standard install:
C:\Program Files\Notepad++\plugins\<PluginName>\<PluginName>.dll(usually requires admin to write). - Writable options for low-privileged operators:
- Use the portable Notepad++ build in a user-writable folder.
- Copy
C:\Program Files\Notepad++to a user-controlled path (e.g.,%LOCALAPPDATA%\npp\) and runnotepad++.exefrom there. - Each plugin gets its own subfolder under
pluginsand is loaded automatically at startup; menu entries appear under Plugins.
Plugin load points (execution primitives)
Notepad++ expects specific exported functions. These are all called during initialization, giving multiple execution surfaces:
- DllMain β runs immediately on DLL load (first execution point).
- setInfo(NppData) β called once on load to provide Notepad++ handles; typical place to register menu items.
- getName() β returns the plugin name shown in the menu.
- getFuncsArray(int *nbF) β returns menu commands; even if empty, it is called during startup.
- beNotified(SCNotification*) β receives editor events (file open/change, UI events) for ongoing triggers.
- messageProc(UINT, WPARAM, LPARAM) β message handler, useful for larger data exchanges.
- isUnicode() β compatibility flag checked at load.
Most exports can be implemented as stubs; execution can occur from DllMain or any callback above during autoload.
Minimal malicious plugin skeleton
Compile a DLL with the expected exports and place it in plugins\\MyNewPlugin\\MyNewPlugin.dll under a writable Notepad++ folder:
BOOL APIENTRY DllMain(HMODULE h, DWORD r, LPVOID) { if (r == DLL_PROCESS_ATTACH) MessageBox(NULL, TEXT("Hello from Notepad++"), TEXT("MyNewPlugin"), MB_OK); return TRUE; }
extern "C" __declspec(dllexport) void setInfo(NppData) {}
extern "C" __declspec(dllexport) const TCHAR *getName() { return TEXT("MyNewPlugin"); }
extern "C" __declspec(dllexport) FuncItem *getFuncsArray(int *nbF) { *nbF = 0; return NULL; }
extern "C" __declspec(dllexport) void beNotified(SCNotification *) {}
extern "C" __declspec(dllexport) LRESULT messageProc(UINT, WPARAM, LPARAM) { return TRUE; }
extern "C" __declspec(dllexport) BOOL isUnicode() { return TRUE; }
- Build the DLL (Visual Studio/MinGW).
- Create the plugin subfolder under
pluginsand drop the DLL inside. - Restart Notepad++; the DLL is loaded automatically, executing
DllMainand subsequent callbacks.
Reflective loader plugin pattern
A weaponized plugin can turn Notepad++ into a reflective DLL loader:
- Present a minimal UI/menu entry (e.g., "LoadDLL").
- Accept a file path or URL to fetch a payload DLL.
- Reflectively map the DLL into the current process and invoke an exported entry point (e.g., a loader function inside the fetched DLL).
- Benefit: reuse a benign-looking GUI process instead of spawning a new loader; payload inherits the integrity of notepad++.exe (including elevated contexts).
- Trade-offs: dropping an unsigned plugin DLL to disk is noisy; consider piggybacking on existing trusted plugins if present.
Detection and hardening notes
- Block or monitor writes to Notepad++ plugin directories (including portable copies in user profiles); enable controlled folder access or application allowlisting.
- Alert on new unsigned DLLs under
pluginsand unusual child processes/network activity fromnotepad++.exe. - Enforce plugin installation via Plugins Admin only, and restrict execution of portable copies from untrusted paths.
References
{{#include ../../banners/hacktricks-training.md}}