Skip to content

Commit 43fe219

Browse files
author
HD Moore
committed
This improves handling of 100-continue responses
1 parent 19920b3 commit 43fe219

File tree

3 files changed

+68
-43
lines changed

3 files changed

+68
-43
lines changed

lib/rex/proto/http/client.rb

Lines changed: 45 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -195,8 +195,7 @@ def request_raw(opts={})
195195
# - cookie: Cookie header value
196196
# - ctype: Content-Type header value, default: +application/x-www-form-urlencoded+
197197
# - data: HTTP data (only useful with some methods, see rfc2616)
198-
# - encode: URI encode the supplied URI, default: false
199-
# - encode_params: URI encode the GET or POST variables (names and values), default: true
198+
# - encode: URI encode the supplied URI
200199
# - headers: HTTP headers as a hash, e.g. <code>{ "X-MyHeader" => "value" }</code>
201200
# - method: HTTP method to use in the request, not limited to standard methods defined by rfc2616, default: GET
202201
# - proto: protocol, default: HTTP
@@ -209,28 +208,28 @@ def request_raw(opts={})
209208
# - vhost: Host header value
210209
#
211210
def request_cgi(opts={})
212-
c_enc = opts['encode'] || false
213-
c_enc_p = (opts['encode_params'] == true or opts['encode_params'].nil? ? true : false)
214-
c_cgi = opts['uri'] || '/'
215-
c_body = opts['data'] || ''
216-
c_meth = opts['method'] || 'GET'
217-
c_prot = opts['proto'] || 'HTTP'
218-
c_vers = opts['version'] || config['version'] || '1.1'
219-
c_qs = opts['query'] || ''
220-
c_varg = opts['vars_get'] || {}
221-
c_varp = opts['vars_post'] || {}
222-
c_head = opts['headers'] || config['headers'] || {}
223-
c_rawh = opts['raw_headers'] || config['raw_headers'] || ''
224-
c_type = opts['ctype'] || 'application/x-www-form-urlencoded'
225-
c_ag = opts['agent'] || config['agent']
226-
c_cook = opts['cookie'] || config['cookie']
227-
c_host = opts['vhost'] || config['vhost']
228-
c_conn = opts['connection']
229-
c_path = opts['path_info']
230-
c_auth = opts['basic_auth'] || config['basic_auth'] || ''
231-
uri = set_cgi(c_cgi)
232-
qstr = c_qs
233-
pstr = c_body
211+
c_enc = opts['encode'] || false
212+
c_cgi = opts['uri'] || '/'
213+
c_body = opts['data'] || ''
214+
c_meth = opts['method'] || 'GET'
215+
c_prot = opts['proto'] || 'HTTP'
216+
c_vers = opts['version'] || config['version'] || '1.1'
217+
c_qs = opts['query'] || ''
218+
c_varg = opts['vars_get'] || {}
219+
c_varp = opts['vars_post'] || {}
220+
c_head = opts['headers'] || config['headers'] || {}
221+
c_rawh = opts['raw_headers']|| config['raw_headers'] || ''
222+
c_type = opts['ctype'] || 'application/x-www-form-urlencoded'
223+
c_ag = opts['agent'] || config['agent']
224+
c_cook = opts['cookie'] || config['cookie']
225+
c_host = opts['vhost'] || config['vhost']
226+
c_conn = opts['connection']
227+
c_path = opts['path_info']
228+
c_auth = opts['basic_auth'] || config['basic_auth'] || ''
229+
230+
uri = set_cgi(c_cgi)
231+
qstr = c_qs
232+
pstr = c_body
234233

235234
if (config['pad_get_params'])
236235
1.upto(config['pad_get_params_count'].to_i) do |i|
@@ -243,27 +242,25 @@ def request_cgi(opts={})
243242

244243
c_varg.each_pair do |var,val|
245244
qstr << '&' if qstr.length > 0
246-
qstr << (c_enc_p ? set_encode_uri(var) : var)
245+
qstr << set_encode_uri(var)
247246
qstr << '='
248-
qstr << (c_enc_p ? set_encode_uri(val) : val)
247+
qstr << set_encode_uri(val)
249248
end
250249

251250
if (config['pad_post_params'])
252251
1.upto(config['pad_post_params_count'].to_i) do |i|
253-
rand_var = Rex::Text.rand_text_alphanumeric(rand(32)+1)
254-
rand_val = Rex::Text.rand_text_alphanumeric(rand(32)+1)
255252
pstr << '&' if pstr.length > 0
256-
pstr << (c_enc_p ? set_encode_uri(rand_var) : rand_var)
253+
pstr << set_encode_uri(Rex::Text.rand_text_alphanumeric(rand(32)+1))
257254
pstr << '='
258-
pstr << (c_enc_p ? set_encode_uri(rand_val) : rand_val)
255+
pstr << set_encode_uri(Rex::Text.rand_text_alphanumeric(rand(32)+1))
259256
end
260257
end
261258

262259
c_varp.each_pair do |var,val|
263260
pstr << '&' if pstr.length > 0
264-
pstr << (c_enc_p ? set_encode_uri(var) : var)
261+
pstr << set_encode_uri(var)
265262
pstr << '='
266-
pstr << (c_enc_p ? set_encode_uri(val) : val)
263+
pstr << set_encode_uri(val)
267264
end
268265

269266
req = ''
@@ -297,7 +294,6 @@ def request_cgi(opts={})
297294
req << set_chunked_header()
298295
req << set_raw_headers(c_rawh)
299296
req << set_body(pstr)
300-
301297
req
302298
end
303299

@@ -365,7 +361,7 @@ def send_request(req, t = -1)
365361
#
366362
# Read a response from the server
367363
#
368-
def read_response(t = -1)
364+
def read_response(t = -1, opts = {})
369365

370366
resp = Response.new
371367
resp.max_data = config['read_max_data']
@@ -392,7 +388,7 @@ def read_response(t = -1)
392388

393389
##########################################################################
394390
# XXX: NOTE: BUG: get_once currently (as of r10042) rescues "Exception"
395-
# As such, the following rescue block will ever be reached. -jjd
391+
# As such, the following rescue block will never be reached. -jjd
396392
##########################################################################
397393

398394
# Handle unexpected disconnects
@@ -434,14 +430,20 @@ def read_response(t = -1)
434430
return resp if not resp
435431

436432
# As a last minute hack, we check to see if we're dealing with a 100 Continue here.
437-
if resp.proto == '1.1' and resp.code == 100
438-
# If so, our real response becaome the body, so we re-parse it.
439-
body = resp.body
440-
resp = Response.new
441-
resp.max_data = config['read_max_data']
442-
rv = resp.parse(body)
443-
# XXX: At some point, this may benefit from processing post-completion code
444-
# as seen above.
433+
# Most of the time this is handled by the parser via check_100()
434+
if resp.proto == '1.1' and resp.code == 100 and not opts[:skip_100]
435+
# Read the real response from the body if we found one
436+
# If so, our real response became the body, so we re-parse it.
437+
if resp.body.to_s =~ /^HTTP/
438+
body = resp.body
439+
resp = Response.new
440+
resp.max_data = config['read_max_data']
441+
rv = resp.parse(body)
442+
# We found a 100 Continue but didn't read the real reply yet
443+
# Otherwise reread the reply, but don't try this hack again
444+
else
445+
resp = read_response(t, :skip_100 => true)
446+
end
445447
end
446448

447449
resp

lib/rex/proto/http/packet.rb

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -367,6 +367,7 @@ def parse_body
367367
if (self.body_bytes_left == 0)
368368
self.bufq.sub!(/^\r?\n/s,'')
369369
self.state = ParseState::Completed
370+
self.check_100
370371
return
371372
end
372373

@@ -396,10 +397,15 @@ def parse_body
396397
# ready to go.
397398
if (not self.transfer_chunked and self.body_bytes_left == 0)
398399
self.state = ParseState::Completed
400+
self.check_100
399401
return
400402
end
401403
end
402404

405+
# Override this as needed
406+
def check_100
407+
end
408+
403409
end
404410

405411
end

lib/rex/proto/http/response.rb

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,9 @@ def initialize(code = 200, message = 'OK', proto = DefaultProtocol)
5353
# default chunk sizes (if chunked is used)
5454
self.chunk_min_size = 1
5555
self.chunk_max_size = 10
56+
57+
# 100 continue counter
58+
self.count_100 = 0
5659
end
5760

5861
#
@@ -66,6 +69,19 @@ def update_cmd_parts(str)
6669
else
6770
raise RuntimeError, "Invalid response command string", caller
6871
end
72+
73+
check_100()
74+
end
75+
76+
#
77+
# Allow 100 Continues to be ignored by the caller
78+
#
79+
def check_100
80+
# If this was a 100 continue with no data, reset
81+
if self.code == 100 and (self.body_bytes_left == -1 or self.body_bytes_left == 0) and self.count_100 < 5
82+
self.reset_except_queue
83+
self.count_100 += 1
84+
end
6985
end
7086

7187
#
@@ -84,6 +100,7 @@ def cmd_string
84100
attr_accessor :code
85101
attr_accessor :message
86102
attr_accessor :proto
103+
attr_accessor :count_100
87104
end
88105

89106
end

0 commit comments

Comments
 (0)