@@ -8,64 +8,10 @@ def initialize(info = {})
8
8
9
9
register_advanced_options (
10
10
[
11
- OptInt . new ( 'FileDropperDelay' , [ false , 'Delay in seconds before attempting file cleanup' ] )
11
+ OptInt . new ( 'FileDropperDelay' , [ false , 'Delay in seconds before attempting file cleanup' ] )
12
12
] , self . class )
13
13
end
14
14
15
- #
16
- # When a new session is created, attempt to delete any files that the
17
- # exploit created.
18
- #
19
- # @param (see Msf::Exploit#on_new_session)
20
- # @return [void]
21
- #
22
- def on_new_session ( session )
23
- super
24
-
25
- if session . type == "meterpreter"
26
- session . core . use ( "stdapi" ) unless session . ext . aliases . include? ( "stdapi" )
27
- end
28
-
29
- if not @dropped_files or @dropped_files . empty?
30
- return true
31
- end
32
-
33
- @dropped_files . delete_if do |file |
34
- win_file = file . gsub ( "/" , "\\ \\ " )
35
- if session . type == "meterpreter"
36
- begin
37
- # Meterpreter should do this automatically as part of
38
- # fs.file.rm(). Until that has been implemented, remove the
39
- # read-only flag with a command.
40
- if session . platform =~ /win/
41
- session . shell_command_token ( %Q|attrib.exe -r #{ win_file } | )
42
- end
43
- session . fs . file . rm ( file )
44
- print_good ( "Deleted #{ file } " )
45
- true
46
- rescue ::Rex ::Post ::Meterpreter ::RequestError
47
- false
48
- end
49
- else
50
- win_cmds = [
51
- %Q|attrib.exe -r "#{ win_file } "| ,
52
- %Q|del.exe /f /q "#{ win_file } "|
53
- ]
54
- # We need to be platform-independent here. Since we can't be
55
- # certain that {#target} is accurate because exploits with
56
- # automatic targets frequently change it, we just go ahead and
57
- # run both a windows and a unix command in the same line. One
58
- # of them will definitely fail and the other will probably
59
- # succeed. Doing it this way saves us an extra round-trip.
60
- # Trick shared by @mihi42
61
- session . shell_command_token ( "rm -f \" #{ file } \" >/dev/null ; echo ' & #{ win_cmds . join ( " & " ) } & echo \" ' >/dev/null" )
62
- print_good ( "Deleted #{ file } " )
63
- true
64
- end
65
- end
66
- end
67
-
68
- #
69
15
# Record file as needing to be cleaned up
70
16
#
71
17
# @param files [Array<String>] List of paths on the target that should
@@ -84,7 +30,32 @@ def register_files_for_cleanup(*files)
84
30
# Singular version
85
31
alias register_file_for_cleanup register_files_for_cleanup
86
32
33
+ # When a new session is created, attempt to delete any files that the
34
+ # exploit created.
87
35
#
36
+ # @param (see Msf::Exploit#on_new_session)
37
+ # @return [void]
38
+ def on_new_session ( session )
39
+ super
40
+
41
+ if session . type == 'meterpreter'
42
+ session . core . use ( 'stdapi' ) unless session . ext . aliases . include? ( 'stdapi' )
43
+ end
44
+
45
+ unless @dropped_files && @dropped_files . length > 0
46
+ return
47
+ end
48
+
49
+ @dropped_files . delete_if do |file |
50
+ exists_before = file_dropper_file_exist? ( session , file )
51
+ if file_dropper_delete ( session , file )
52
+ file_dropper_deleted? ( session , file , exists_before )
53
+ else
54
+ false
55
+ end
56
+ end
57
+ end
58
+
88
59
# While the exploit cleanup do a last attempt to delete any files created
89
60
# if there is a file_rm method available. Warn the user if any files were
90
61
# not cleaned up.
@@ -109,9 +80,8 @@ def cleanup
109
80
@dropped_files . delete_if do |file |
110
81
begin
111
82
file_rm ( file )
112
- print_good ( "Deleted #{ file } " )
113
- true
114
- #rescue ::Rex::SocketError, ::EOFError, ::IOError, ::Errno::EPIPE, ::Rex::Post::Meterpreter::RequestError => e
83
+ # We don't know for sure if file has been deleted, so always warn about it to the user
84
+ false
115
85
rescue ::Exception => e
116
86
vprint_error ( "Failed to delete #{ file } : #{ e } " )
117
87
elog ( "Failed to delete #{ file } : #{ e . class } : #{ e } " )
@@ -126,5 +96,101 @@ def cleanup
126
96
end
127
97
128
98
end
99
+
100
+ private
101
+
102
+ # See if +path+ exists on the remote system and is a regular file
103
+ #
104
+ # @param path [String] Remote filename to check
105
+ # @return [Boolean] True if the file exists, otherwise false.
106
+ def file_dropper_file_exist? ( session , path )
107
+ if session . platform =~ /win/
108
+ normalized = file_dropper_win_file ( path )
109
+ else
110
+ normalized = path
111
+ end
112
+
113
+ if session . type == 'meterpreter'
114
+ stat = session . fs . file . stat ( normalized ) rescue nil
115
+ return false unless stat
116
+ stat . file?
117
+ else
118
+ if session . platform =~ /win/
119
+ f = shell_command_token ( "cmd.exe /C IF exist \" #{ normalized } \" ( echo true )" )
120
+ if f =~ /true/
121
+ f = shell_command_token ( "cmd.exe /C IF exist \" #{ normalized } \\ \\ \" ( echo false ) ELSE ( echo true )" )
122
+ end
123
+ else
124
+ f = session . shell_command_token ( "test -f \" #{ normalized } \" && echo true" )
125
+ end
126
+
127
+ return false if f . nil? || f . empty?
128
+ return false unless f =~ /true/
129
+ true
130
+ end
131
+ end
132
+
133
+ # Sends a file deletion command to the remote +session+
134
+ #
135
+ # @param [String] file The file to delete
136
+ # @return [Boolean] True if the delete command has been executed in the remote machine, otherwise false.
137
+ def file_dropper_delete ( session , file )
138
+ win_file = file_dropper_win_file ( file )
139
+
140
+ if session . type == 'meterpreter'
141
+ begin
142
+ # Meterpreter should do this automatically as part of
143
+ # fs.file.rm(). Until that has been implemented, remove the
144
+ # read-only flag with a command.
145
+ if session . platform =~ /win/
146
+ session . shell_command_token ( %Q|attrib.exe -r #{ win_file } | )
147
+ end
148
+ session . fs . file . rm ( file )
149
+ true
150
+ rescue ::Rex ::Post ::Meterpreter ::RequestError
151
+ false
152
+ end
153
+ else
154
+ win_cmds = [
155
+ %Q|attrib.exe -r "#{ win_file } "| ,
156
+ %Q|del.exe /f /q "#{ win_file } "|
157
+ ]
158
+ # We need to be platform-independent here. Since we can't be
159
+ # certain that {#target} is accurate because exploits with
160
+ # automatic targets frequently change it, we just go ahead and
161
+ # run both a windows and a unix command in the same line. One
162
+ # of them will definitely fail and the other will probably
163
+ # succeed. Doing it this way saves us an extra round-trip.
164
+ # Trick shared by @mihi42
165
+ session . shell_command_token ( "rm -f \" #{ file } \" >/dev/null ; echo ' & #{ win_cmds . join ( " & " ) } & echo \" ' >/dev/null" )
166
+ true
167
+ end
168
+ end
169
+
170
+ # Checks if a file has been deleted by the current job
171
+ #
172
+ # @param [String] file The file to check
173
+ # @return [Boolean] If the file has been deleted, otherwise false.
174
+ def file_dropper_deleted? ( session , file , exists_before )
175
+ if exists_before && file_dropper_file_exist? ( session , file )
176
+ print_error ( "Unable to delete #{ file } " )
177
+ false
178
+ elsif exists_before
179
+ print_good ( "Deleted #{ file } " )
180
+ true
181
+ else
182
+ print_warning ( "Tried to delete #{ file } , unknown result" )
183
+ true
184
+ end
185
+ end
186
+
187
+ # Converts a file path to use the windows separator '\'
188
+ #
189
+ # @param [String] file The file path to convert
190
+ # @return [String] The file path converted
191
+ def file_dropper_win_file ( file )
192
+ file . gsub ( '/' , '\\\\' )
193
+ end
194
+
129
195
end
130
196
end
0 commit comments