Windows Gather Deleted Files Enumeration and Recovering - Metasploit
This page contains detailed information about how to use the post/windows/gather/forensics/recovery_files metasploit module. For list of all metasploit modules, visit the Metasploit Module Library.
Module Overview
Name: Windows Gather Deleted Files Enumeration and Recovering
Module: post/windows/gather/forensics/recovery_files
Source code: modules/post/windows/gather/forensics/recovery_files.rb
Disclosure date: -
Last modification time: 2021-10-06 13:43:31 +0000
Supported architecture(s): -
Supported platform(s): Windows
Target service / protocol: -
Target network port(s): -
List of CVEs: -
This module lists and attempts to recover deleted files from NTFS file systems. Use the FILES option to guide recovery. Leave this option empty to enumerate deleted files in the DRIVE. Set FILES to an extension (e.g., "pdf") to recover deleted files with that extension, or set FILES to a comma separated list of IDs (from enumeration) to recover those files. The user must have account file enumeration. Recovery may take a long time; use the TIMEOUT option to abort enumeration or recovery by extension after a specified period (in seconds).
Module Ranking and Traits
Module Ranking:
- normal: The exploit is otherwise reliable, but depends on a specific version and can't (or doesn't) reliably autodetect. More information about ranking can be found here.
Basic Usage
There are two ways to execute this post module.
From the Meterpreter prompt
The first is by using the "run" command at the Meterpreter prompt. It allows you to run the post module against that specific session:
meterpreter > run post/windows/gather/forensics/recovery_files
From the msf prompt
The second is by using the "use" command at the msf prompt. You will have to figure out which session ID to set manually. To list all session IDs, you can use the "sessions" command.
msf > use post/windows/gather/forensics/recovery_files
msf post(recovery_files) > show options
... show and set options ...
msf post(recovery_files) > set SESSION session-id
msf post(recovery_files) > exploit
If you wish to run the post against all sessions from framework, here is how:
1 - Create the following resource script:
framework.sessions.each_pair do |sid, session|
run_single("use post/windows/gather/forensics/recovery_files")
run_single("set SESSION #{sid}")
run_single("run")
end
2 - At the msf prompt, execute the above resource script:
msf > resource path-to-resource-script
Required Options
- SESSION: The session to run this module on.
Go back to menu.
Msfconsole Usage
Here is how the windows/gather/forensics/recovery_files post exploitation module looks in the msfconsole:
msf6 > use post/windows/gather/forensics/recovery_files
msf6 post(windows/gather/forensics/recovery_files) > show info
Name: Windows Gather Deleted Files Enumeration and Recovering
Module: post/windows/gather/forensics/recovery_files
Platform: Windows
Arch:
Rank: Normal
Provided by:
Borja Merino <[email protected]>
Compatible session types:
Meterpreter
Basic options:
Name Current Setting Required Description
---- --------------- -------- -----------
DRIVE C: yes Drive you want to recover files from.
FILES no ID or extensions of the files to recover in a comma separated way. Let empty to enumerate deleted files.
SESSION yes The session to run this module on.
TIMEOUT 3600 yes Search timeout. If 0 the module will go through the entire $MFT.
Description:
This module lists and attempts to recover deleted files from NTFS
file systems. Use the FILES option to guide recovery. Leave this
option empty to enumerate deleted files in the DRIVE. Set FILES to
an extension (e.g., "pdf") to recover deleted files with that
extension, or set FILES to a comma separated list of IDs (from
enumeration) to recover those files. The user must have account file
enumeration. Recovery may take a long time; use the TIMEOUT option
to abort enumeration or recovery by extension after a specified
period (in seconds).
References:
http://www.youtube.com/watch?v=9yzCf360ujY&hd=1
Module Options
This is a complete list of options available in the windows/gather/forensics/recovery_files post exploitation module:
msf6 post(windows/gather/forensics/recovery_files) > show options
Module options (post/windows/gather/forensics/recovery_files):
Name Current Setting Required Description
---- --------------- -------- -----------
DRIVE C: yes Drive you want to recover files from.
FILES no ID or extensions of the files to recover in a comma separated way. Let empty to enumerate deleted files.
SESSION yes The session to run this module on.
TIMEOUT 3600 yes Search timeout. If 0 the module will go through the entire $MFT.
Advanced Options
Here is a complete list of advanced options supported by the windows/gather/forensics/recovery_files post exploitation module:
msf6 post(windows/gather/forensics/recovery_files) > show advanced
Module advanced options (post/windows/gather/forensics/recovery_files):
Name Current Setting Required Description
---- --------------- -------- -----------
VERBOSE false no Enable detailed status messages
WORKSPACE no Specify the workspace for this module
Post Actions
This is a list of all post exploitation actions which the windows/gather/forensics/recovery_files module can do:
msf6 post(windows/gather/forensics/recovery_files) > show actions
Post actions:
Name Description
---- -----------
Evasion Options
Here is the full list of possible evasion options supported by the windows/gather/forensics/recovery_files post exploitation module in order to evade defenses (e.g. Antivirus, EDR, Firewall, NIDS etc.):
msf6 post(windows/gather/forensics/recovery_files) > show evasion
Module evasion options:
Name Current Setting Required Description
---- --------------- -------- -----------
Go back to menu.
Error Messages
This module may fail with the following error messages:
- Module not valid for Windows 2000
- The file system is not NTFS
- You don't have enough privileges. Try getsystem.
- GetLastError
- Error opening <DRIVE> GetLastError=<GETLASTERROR>
- Timed out after <TO> seconds. Skipping...
- There were problems to recover the file: <NAME>
- The file is not resident. Saving <NAME> ... (<SIZE> bytes)
- (No last datarun) Save 1 chunk of <VALUE>, there are <SIZE> left
- GetLastError
- Error opening <DRIVE> GetLastError=<GETLASTERROR>
- Try to get SYSTEM Privilege
- File compressed/encrypted/sparse. Ignore this file if you get errors
- Attribute not found
Check for the possible causes from the code snippets below found in the module source code. This can often times help in identifying the root cause of the problem.
Module not valid for Windows 2000
Here is a relevant code snippet related to the "Module not valid for Windows 2000" error message:
40:
41: def run
42: winver = sysinfo["OS"]
43:
44: if winver =~ /2000/i
45: print_error("Module not valid for Windows 2000")
46: return
47: end
48:
49: drive = datastore['DRIVE']
50: fs = file_system(drive)
The file system is not NTFS
Here is a relevant code snippet related to the "The file system is not NTFS" error message:
48:
49: drive = datastore['DRIVE']
50: fs = file_system(drive)
51:
52: if fs !~ /ntfs/i
53: print_error("The file system is not NTFS")
54: return
55: end
56:
57: if not is_admin?
58: print_error("You don't have enough privileges. Try getsystem.")
You don't have enough privileges. Try getsystem.
Here is a relevant code snippet related to the "You don't have enough privileges. Try getsystem." error message:
53: print_error("The file system is not NTFS")
54: return
55: end
56:
57: if not is_admin?
58: print_error("You don't have enough privileges. Try getsystem.")
59: return
60: end
61:
62: print_status("System Info - OS: #{winver}, Drive: #{drive}")
63: type = datastore['FILES']
GetLastError
Here is a relevant code snippet related to the "GetLastError" error message:
63: type = datastore['FILES']
64: files = type.split(',')
65: # To extract files from its IDs
66: if datastore['FILES'] != "" and is_numeric(files[0])
67: r = client.railgun.kernel32.CreateFileA("\\\\.\\#{drive}", "GENERIC_READ", "FILE_SHARE_DELETE|FILE_SHARE_READ|FILE_SHARE_WRITE", nil, "OPEN_EXISTING", "FILE_FLAG_WRITE_THROUGH", 0)
68: if r['GetLastError'] == 0
69: recover_file(files, r['return'])
70: client.railgun.kernel32.CloseHandle(r['return'])
71: else
72: print_error("Error opening #{drive} GetLastError=#{r['GetLastError']}")
73: end
Error opening <DRIVE> GetLastError=<GETLASTERROR>
Here is a relevant code snippet related to the "Error opening <DRIVE> GetLastError=<GETLASTERROR>" error message:
67: r = client.railgun.kernel32.CreateFileA("\\\\.\\#{drive}", "GENERIC_READ", "FILE_SHARE_DELETE|FILE_SHARE_READ|FILE_SHARE_WRITE", nil, "OPEN_EXISTING", "FILE_FLAG_WRITE_THROUGH", 0)
68: if r['GetLastError'] == 0
69: recover_file(files, r['return'])
70: client.railgun.kernel32.CloseHandle(r['return'])
71: else
72: print_error("Error opening #{drive} GetLastError=#{r['GetLastError']}")
73: end
74: # To show deleted files (FILE="") or extract the type of file specified by extension
75: else
76: handle = get_mft_info(drive)
77: if handle != nil
Timed out after <TO> seconds. Skipping...
Here is a relevant code snippet related to the "Timed out after <TO> seconds. Skipping..." error message:
81: begin
82: ::Timeout.timeout(to) do
83: deleted_files(data_runs[1..-1], handle, files)
84: end
85: rescue ::Timeout::Error
86: print_error("Timed out after #{to} seconds. Skipping...")
87: end
88: end
89: end
90: end
91:
There were problems to recover the file: <NAME>
Here is a relevant code snippet related to the "There were problems to recover the file: <NAME>" error message:
109: name = get_name(attributes)
110: print_status("File to download: #{name}")
111: vprint_status("Getting Data Runs ...")
112: data = get_data_runs(attributes)
113: if data == nil or name == nil
114: print_error("There were problems to recover the file: #{name}")
115: next
116: end
117:
118: # If file is resident
119: if data[0] == 0
The file is not resident. Saving <NAME> ... (<SIZE> bytes)
Here is a relevant code snippet related to the "The file is not resident. Saving <NAME> ... (<SIZE> bytes)" error message:
124: # If file no resident
125: else
126: # Due to the size of the non-resident files we have to store small chunks of data as we go through each of the data runs
127: # that make up the file (save_file function).
128: size = get_size(rf['lpBuffer'][56..-1])
129: print_status("The file is not resident. Saving #{name} ... (#{size} bytes)")
130: base = 0
131: # Go through each of the data runs to save the file
132: file_data = ""
133: 1.upto(data.count - 1) { |i|
134: datarun = get_datarun_location(data[i])
(No last datarun) Save 1 chunk of <VALUE>, there are <SIZE> left
Here is a relevant code snippet related to the "(No last datarun) Save 1 chunk of <VALUE>, there are <SIZE> left" error message:
178: vprint_status("(Last datarun) Save 1 chunk of #{size}")
179: else
180: rf = client.railgun.kernel32.ReadFile(handle, bytes_per_cluster * rest, bytes_per_cluster * rest, 4, nil)
181: file_data << rf['lpBuffer']
182: size = size - bytes_per_cluster * rest
183: vprint_status("(No last datarun) Save 1 chunk of #{rest * bytes_per_cluster}, there are #{size} left")
184: end
185: end
186: return size
187: end
188:
GetLastError
Here is a relevant code snippet related to the "GetLastError" error message:
285:
286: # Gets the NTFS information and return a pointer to the beginning of the MFT
287: def get_mft_info(drive)
288: r = client.railgun.kernel32.CreateFileA("\\\\.\\#{drive}", "GENERIC_READ", "FILE_SHARE_DELETE|FILE_SHARE_READ|FILE_SHARE_WRITE", nil, "OPEN_EXISTING", "FILE_FLAG_WRITE_THROUGH", 0)
289:
290: if r['GetLastError'] != 0
291: print_error("Error opening #{drive} GetLastError=#{r['GetLastError']}")
292: print_error("Try to get SYSTEM Privilege") if r['GetLastError'] == 5
293: return nil
294: else
295: ra = file_system_features(r['return'])
Error opening <DRIVE> GetLastError=<GETLASTERROR>
Here is a relevant code snippet related to the "Error opening <DRIVE> GetLastError=<GETLASTERROR>" error message:
286: # Gets the NTFS information and return a pointer to the beginning of the MFT
287: def get_mft_info(drive)
288: r = client.railgun.kernel32.CreateFileA("\\\\.\\#{drive}", "GENERIC_READ", "FILE_SHARE_DELETE|FILE_SHARE_READ|FILE_SHARE_WRITE", nil, "OPEN_EXISTING", "FILE_FLAG_WRITE_THROUGH", 0)
289:
290: if r['GetLastError'] != 0
291: print_error("Error opening #{drive} GetLastError=#{r['GetLastError']}")
292: print_error("Try to get SYSTEM Privilege") if r['GetLastError'] == 5
293: return nil
294: else
295: ra = file_system_features(r['return'])
296: bytes_per_cluster = ra['lpOutBuffer'][44, 4].unpack("V*")[0]
Try to get SYSTEM Privilege
Here is a relevant code snippet related to the "Try to get SYSTEM Privilege" error message:
287: def get_mft_info(drive)
288: r = client.railgun.kernel32.CreateFileA("\\\\.\\#{drive}", "GENERIC_READ", "FILE_SHARE_DELETE|FILE_SHARE_READ|FILE_SHARE_WRITE", nil, "OPEN_EXISTING", "FILE_FLAG_WRITE_THROUGH", 0)
289:
290: if r['GetLastError'] != 0
291: print_error("Error opening #{drive} GetLastError=#{r['GetLastError']}")
292: print_error("Try to get SYSTEM Privilege") if r['GetLastError'] == 5
293: return nil
294: else
295: ra = file_system_features(r['return'])
296: bytes_per_cluster = ra['lpOutBuffer'][44, 4].unpack("V*")[0]
297: mft_logical_offset = ra['lpOutBuffer'][64, 8].unpack("V*")[0]
File compressed/encrypted/sparse. Ignore this file if you get errors
Here is a relevant code snippet related to the "File compressed/encrypted/sparse. Ignore this file if you get errors" error message:
327: def get_data_runs(data)
328: # We reach de DATA attribute
329: data_runs = get_attribute(data, "\x80\x00\x00\x00")
330: return nil if data_runs == nil
331:
332: print_status("File compressed/encrypted/sparse. Ignore this file if you get errors") if ["\x01\x00", "\x00\x40", "\x00\x80"].include? data_runs[12, 2]
333: # Check if the file is resident or not
334: resident = data_runs[8, 1]
335: if resident == "\x00"
336: inf = [0]
337: inf << get_resident(data_runs)
Attribute not found
Here is a relevant code snippet related to the "Attribute not found" error message:
376: return nil if (size_att > 1024) or header == "\xff\xff\xff\xff"
377:
378: str = str[size_att..-1]
379: end
380: end
381: print_status("Attribute not found")
382: return nil
383: end
384:
385: # Get the type of file system
386: def file_system(drive)
Go back to menu.
Related Pull Requests
- #12407 Merged Pull Request: begining to fix spelling errors
- #8716 Merged Pull Request: Print_Status -> Print_Good (And OCD bits 'n bobs)
- #8338 Merged Pull Request: Fix msf/core and self.class msftidy warnings
- #6655 Merged Pull Request: use MetasploitModule as a class name
- #6648 Merged Pull Request: Change metasploit class names
- #6395 Merged Pull Request: Fix recovery_files.rb Description grammar errors
- #3484 Merged Pull Request: Fix multiple occurrences of bad pack/unpack specifiers
- #2525 Merged Pull Request: Change module boilerplate
References
See Also
Check also the following modules related to this module:
- post/windows/gather/forensics/browser_history
- post/windows/gather/forensics/duqu_check
- post/windows/gather/forensics/enum_drives
- post/windows/gather/forensics/fanny_bmp_check
- post/windows/gather/forensics/imager
- post/windows/gather/forensics/nbd_server
Authors
- Borja Merino <bmerinofe[at]gmail.com>
Version
This page has been produced using Metasploit Framework version 6.1.24-dev. For more modules, visit the Metasploit Module Library.
Go back to menu.