Skip to content

Commit b3bb20f

Browse files
committed
Land rapid7#3629, @wchen-r7's HTTP traversal fixes
2 parents 93174a8 + c35dc4d commit b3bb20f

File tree

1 file changed

+30
-25
lines changed

1 file changed

+30
-25
lines changed

modules/auxiliary/scanner/http/http_traversal.rb

Lines changed: 30 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,18 @@ def initialize(info = {})
8080
deregister_options('RHOST')
8181
end
8282

83+
84+
# Avoids writing to datastore['METHOD'] directly
85+
def method
86+
@method || datastore['METHOD']
87+
end
88+
89+
# Avoids writing to datastore['DATA'] directly
90+
def data
91+
@data || datastore['DATA']
92+
end
93+
94+
8395
#
8496
# The fuzz() function serves as the engine for the module. It can intelligently mutate
8597
# a trigger, and find potential bugs with it.
@@ -101,7 +113,7 @@ def fuzz
101113

102114
# Each possible trigger, we try to traverse multiple levels down depending
103115
# on datastore['DEPATH']
104-
depth = datastore['DEPTH']
116+
depth = datastore['DEPTH']
105117
triggers.each do |base|
106118
1.upto(depth) do |d|
107119
file_to_read.each do |f|
@@ -124,19 +136,15 @@ def fuzz
124136
def ini_request(uri)
125137
req = {}
126138

127-
# If the user is using some rare-to-use method, we probably have not fully tested,
128-
# so we will not support it for now.
129-
method = datastore['METHOD']
130-
data = datastore['DATA']
131139
case method
132140
when 'GET'
133141
# Example: Say we have the following datastore['PATH']
134142
# '/test.php?page=1&id=3&note=whatever'
135143
# We expect it to regex the GET parameters:
136144
# 'page=1&id=3&note=whatever'
137145
# And then let queryparse() to handle the rest
138-
data = uri.match(/\?(\w+=.+&*)$/)
139-
req['vars_get'] = queryparse(data[1]) if not data.nil?
146+
query_params = uri.match(/\?(\w+=.+&*)$/)
147+
req['vars_get'] = queryparse(query_params[1]) if query_params
140148
when 'POST'
141149
req['vars_post'] = queryparse(data) if not data.empty?
142150
when 'PUT'
@@ -154,10 +162,10 @@ def ini_request(uri)
154162
this_path = uri
155163
end
156164

157-
req['method'] = datastore['METHOD']
165+
req['method'] = method
158166
req['uri'] = this_path
159167
req['headers'] = {'Cookie'=>datastore['COOKIE']} if not datastore['COOKIE'].empty?
160-
req['data'] = datastore['DATA'] if not datastore['DATA'].empty?
168+
req['data'] = data if not data.empty?
161169
req['authorization'] = basic_auth(datastore['USERNAME'], datastore['PASSWORD'])
162170

163171
return req
@@ -217,7 +225,7 @@ def check(trigger)
217225
:proof => trigger,
218226
:name => self.fullname,
219227
:category => "web",
220-
:method => datastore['METHOD']
228+
:method => method
221229
})
222230

223231
else
@@ -281,15 +289,15 @@ def php_download(files)
281289
#
282290
def is_writable(trigger)
283291
# Modify some registered options for the PUT method
284-
tmp_method = datastore['METHOD']
285-
tmp_data = datastore['DATA']
286-
datastore['METHOD'] = 'PUT'
292+
tmp_method = method
293+
tmp_data = data
294+
@method = 'PUT'
287295

288-
if datastore['DATA'].empty?
296+
if data.empty?
289297
unique_str = Rex::Text.rand_text_alpha(4) * 4
290-
datastore['DATA'] = unique_str
298+
@data = unique_str
291299
else
292-
unique_str = datastore['DATA']
300+
unique_str = data
293301
end
294302

295303
# Form the PUT request
@@ -302,8 +310,8 @@ def is_writable(trigger)
302310
send_request_cgi(req, 25)
303311

304312
# Prepare request to read our file
305-
datastore['METHOD'] = 'GET'
306-
datastore['DATA'] = tmp_data
313+
@method = 'GET'
314+
@data = tmp_data
307315
req = ini_request(uri)
308316
vprint_status("Verifying upload...")
309317
res = send_request_cgi(req, 25)
@@ -316,24 +324,21 @@ def is_writable(trigger)
316324
end
317325

318326
# Ah, don't forget to restore our method
319-
datastore['METHOD'] = tmp_method
327+
@method = tmp_method
320328
end
321329

322330
#
323331
# Load the whole file list
324332
# This is used in the lfi_download() function
325333
#
326334
def load_filelist
327-
f = File.open(datastore['FILELIST'], 'rb')
328-
buf = f.read
329-
f.close
330-
return buf
335+
File.open(datastore['FILELIST'], 'rb') {|f| f.read}
331336
end
332337

333338
def run_host(ip)
334339
# Warn if it's not a well-formed UPPERCASE method
335-
if datastore['METHOD'] !~ /^[A-Z]+$/
336-
print_warning("HTTP method #{datastore['METHOD']} is not Apache-compliant. Try only UPPERCASE letters.")
340+
if method !~ /^[A-Z]+$/
341+
print_warning("HTTP method #{method} is not Apache-compliant. Try only UPPERCASE letters.")
337342
end
338343
print_status("Running action: #{action.name}...")
339344

0 commit comments

Comments
 (0)