Skip to content

Commit b1dd2a6

Browse files
committed
On new session, check if file has been REALLY deleted
1 parent 3cba27e commit b1dd2a6

File tree

1 file changed

+62
-63
lines changed

1 file changed

+62
-63
lines changed

lib/msf/core/exploit/file_dropper.rb

Lines changed: 62 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,12 @@
33
module Msf
44
module Exploit::FileDropper
55

6-
include Msf::Post::Common
7-
include Msf::Post::File
8-
96
def initialize(info = {})
107
super
118

129
register_advanced_options(
1310
[
14-
OptInt.new( 'FileDropperDelay', [ false, 'Delay in seconds before attempting file cleanup' ])
11+
OptInt.new('FileDropperDelay', [false, 'Delay in seconds before attempting file cleanup'])
1512
], self.class)
1613
end
1714

@@ -23,49 +20,22 @@ def initialize(info = {})
2320
# @return [void]
2421
#
2522
def on_new_session(session)
26-
super
27-
28-
@session = session
23+
super(session)
2924

30-
if session.type == "meterpreter"
31-
session.core.use("stdapi") unless session.ext.aliases.include?("stdapi")
25+
if session.type == 'meterpreter'
26+
session.core.use('stdapi') unless session.ext.aliases.include?('stdapi')
3227
end
3328

3429
if not @dropped_files or @dropped_files.empty?
3530
return true
3631
end
3732

3833
@dropped_files.delete_if do |file|
39-
win_file = file.gsub("/", "\\\\")
40-
exists_before = check_file(file, win_file)
41-
42-
if session.type == "meterpreter"
43-
begin
44-
# Meterpreter should do this automatically as part of
45-
# fs.file.rm(). Until that has been implemented, remove the
46-
# read-only flag with a command.
47-
if session.platform =~ /win/
48-
session.shell_command_token(%Q|attrib.exe -r #{win_file}|)
49-
end
50-
session.fs.file.rm(file)
51-
file_deleted?(file, win_file, exists_before)
52-
rescue ::Rex::Post::Meterpreter::RequestError
53-
return false
54-
end
34+
exists_before = file_dropper_check_file(file)
35+
if file_dropper_delete(file)
36+
file_dropper_deleted?(file, exists_before)
5537
else
56-
win_cmds = [
57-
%Q|attrib.exe -r "#{win_file}"|,
58-
%Q|del.exe /f /q "#{win_file}"|
59-
]
60-
# We need to be platform-independent here. Since we can't be
61-
# certain that {#target} is accurate because exploits with
62-
# automatic targets frequently change it, we just go ahead and
63-
# run both a windows and a unix command in the same line. One
64-
# of them will definitely fail and the other will probably
65-
# succeed. Doing it this way saves us an extra round-trip.
66-
# Trick shared by @mihi42
67-
session.shell_command_token("rm -f \"#{file}\" >/dev/null ; echo ' & #{win_cmds.join(" & ")} & echo \" ' >/dev/null")
68-
file_deleted?(file, win_file, exists_before)
38+
false
6939
end
7040
end
7141
end
@@ -114,9 +84,8 @@ def cleanup
11484
@dropped_files.delete_if do |file|
11585
begin
11686
file_rm(file)
117-
print_good("Deleted #{file}")
118-
true
119-
#rescue ::Rex::SocketError, ::EOFError, ::IOError, ::Errno::EPIPE, ::Rex::Post::Meterpreter::RequestError => e
87+
# We don't know for sure if file has been deleted, so always warn about it to the user
88+
false
12089
rescue ::Exception => e
12190
vprint_error("Failed to delete #{file}: #{e}")
12291
elog("Failed to delete #{file}: #{e.class}: #{e}")
@@ -130,39 +99,69 @@ def cleanup
13099
print_warning("This exploit may require manual cleanup of '#{f}' on the target")
131100
end
132101

133-
private
102+
end
134103

135-
def session
136-
@session
137-
end
104+
private
138105

139-
alias :client :session
106+
def file_dropper_win_file(file)
107+
file.gsub('/', "\\\\")
108+
end
140109

141-
def check_file(file, win_file)
142-
if session.platform =~ /win/
143-
res = file_exist?(win_file)
144-
else
145-
res = file_exist?(file)
110+
def file_dropper_delete(file)
111+
win_file = file_dropper_win_file(file)
112+
113+
if session.type == 'meterpreter'
114+
begin
115+
# Meterpreter should do this automatically as part of
116+
# fs.file.rm(). Until that has been implemented, remove the
117+
# read-only flag with a command.
118+
if session.platform =~ /win/
119+
session.shell_command_token(%Q|attrib.exe -r #{win_file}|)
120+
end
121+
session.fs.file.rm(file)
122+
true
123+
rescue ::Rex::Post::Meterpreter::RequestError
124+
false
146125
end
126+
else
127+
win_cmds = [
128+
%Q|attrib.exe -r "#{win_file}"|,
129+
%Q|del.exe /f /q "#{win_file}"|
130+
]
131+
# We need to be platform-independent here. Since we can't be
132+
# certain that {#target} is accurate because exploits with
133+
# automatic targets frequently change it, we just go ahead and
134+
# run both a windows and a unix command in the same line. One
135+
# of them will definitely fail and the other will probably
136+
# succeed. Doing it this way saves us an extra round-trip.
137+
# Trick shared by @mihi42
138+
session.shell_command_token("rm -f \"#{file}\" >/dev/null ; echo ' & #{win_cmds.join(" & ")} & echo \" ' >/dev/null")
139+
true
140+
end
141+
end
147142

148-
res
143+
def file_dropper_check_file(file)
144+
if session.platform =~ /win/
145+
normalized = file_dropper_win_file(file)
146+
else
147+
normalized = file
149148
end
150149

151-
def file_deleted?(file, win_file, exists_before)
152-
if exists_before
153-
if check_file(file, win_file)
154-
print_error("Unable to delete #{file}")
155-
false
156-
else
157-
print_good("Deleted #{file}")
158-
true
159-
end
160-
end
150+
Msf::Post::File.file_exist?(normalized)
151+
end
161152

153+
def file_dropper_deleted?(file, exists_before)
154+
if exists_before && file_dropper_check_file(file)
155+
print_error("Unable to delete #{file}")
156+
false
157+
elsif exists_before
158+
print_good("Deleted #{file}")
159+
true
160+
else
162161
print_warning("Tried to delete #{file}, unknown result")
163162
true
164163
end
165-
166164
end
165+
167166
end
168167
end

0 commit comments

Comments
 (0)