Class: Rex::Proto::Http::Request

Inherits:
Packet
  • Object
show all
Defined in:
lib/rex/proto/http/request.rb

Overview

HTTP request class.

Direct Known Subclasses

Msf::Exploit::Git::SmartHttp::Request, Get, Post, Put

Defined Under Namespace

Classes: Get, Post, Put

Constant Summary collapse

PostRequests =
['POST', 'SEARCH']

Instance Attribute Summary collapse

Attributes inherited from Packet

#auto_cl, #body_bytes_left, #bufq, #chunk_max_size, #chunk_min_size, #compress, #error, #headers, #incomplete, #inside_chunk, #keepalive, #max_data, #state, #transfer_chunked

Instance Method Summary collapse

Methods inherited from Packet

#[], #[]=, #body=, #check_100, #chunk, #completed?, #from_s, #output_packet, #parse, #parse_body, #parse_header, #reset, #reset_except_queue, #to_terminal_output

Constructor Details

#initialize(method = 'GET', uri = '/', proto = DefaultProtocol) ⇒ Request

Initializes an instance of an HTTP request with the supplied method, URI, and protocol.



57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
# File 'lib/rex/proto/http/request.rb', line 57

def initialize(method = 'GET', uri = '/', proto = DefaultProtocol)
  super()

  self.method    = method
  self.raw_uri   = uri
  self.uri_parts = {}
  self.proto     = proto || DefaultProtocol
  self.chunk_min_size = 1
  self.chunk_max_size = 10
  self.uri_encode_mode = 'hex-normal'

  if self.method == 'GET' || self.method == 'CONNECT'
    self.auto_cl = false
  end

  update_uri_parts
end

Instance Attribute Details

#conn_idObject

An identifier associated with the incoming request, can be used to match requests with sessions.



279
280
281
# File 'lib/rex/proto/http/request.rb', line 279

def conn_id
  @conn_id
end

#junk_directoriesObject

add junk directories



305
306
307
# File 'lib/rex/proto/http/request.rb', line 305

def junk_directories
  @junk_directories
end

#junk_end_of_uriObject

add junk end of URI



323
324
325
# File 'lib/rex/proto/http/request.rb', line 323

def junk_end_of_uri
  @junk_end_of_uri
end

#junk_param_startObject

add junk start of params



320
321
322
# File 'lib/rex/proto/http/request.rb', line 320

def junk_param_start
  @junk_param_start
end

#junk_paramsObject

add junk params



314
315
316
# File 'lib/rex/proto/http/request.rb', line 314

def junk_params
  @junk_params
end

#junk_pipelineObject

add junk pipeline requests



317
318
319
# File 'lib/rex/proto/http/request.rb', line 317

def junk_pipeline
  @junk_pipeline
end

#junk_self_referring_directoriesObject

add junk self referring directories (aka /././././)



311
312
313
# File 'lib/rex/proto/http/request.rb', line 311

def junk_self_referring_directories
  @junk_self_referring_directories
end

#junk_slashesObject

add junk slashes



308
309
310
# File 'lib/rex/proto/http/request.rb', line 308

def junk_slashes
  @junk_slashes
end

#methodObject

The method being used for the request (e.g. GET).



284
285
286
# File 'lib/rex/proto/http/request.rb', line 284

def method
  @method
end

#protoObject

The protocol to be sent with the request.



297
298
299
# File 'lib/rex/proto/http/request.rb', line 297

def proto
  @proto
end

#raw_uriObject

The raw URI being requested, before any mucking gets to it



288
289
290
# File 'lib/rex/proto/http/request.rb', line 288

def raw_uri
  @raw_uri
end

#relative_resourceObject

The resource path relative to the root of a server mount point.



302
303
304
# File 'lib/rex/proto/http/request.rb', line 302

def relative_resource
  @relative_resource
end

#uri_encode_modeObject

encoding uri



326
327
328
# File 'lib/rex/proto/http/request.rb', line 326

def uri_encode_mode
  @uri_encode_mode
end

#uri_partsObject

The split up parts of the URI.



293
294
295
# File 'lib/rex/proto/http/request.rb', line 293

def uri_parts
  @uri_parts
end

Instance Method Details

#bodyObject

Returns a hijacked version of the body that shoves the request’s query string in as a replacement in cases where there is no body. YOLO! (shrug)



226
227
228
229
230
231
232
# File 'lib/rex/proto/http/request.rb', line 226

def body
  str = super || ''
  if str.length == 0 && PostRequests.include?(self.method)
    str = param_string
  end
  str
end

#cmd_stringObject

Returns the command string derived from the three values.



237
238
239
240
241
# File 'lib/rex/proto/http/request.rb', line 237

def cmd_string
  proto_str = (self.proto =~ /^\d/) ? "HTTP/#{self.proto}" : self.proto

  "#{self.method} #{self.uri} #{proto_str}\r\n"
end

#meta_varsObject

Returns a hash of variables that contain information about the request, such as the remote host information.

TODO



273
274
# File 'lib/rex/proto/http/request.rb', line 273

def meta_vars
end

#normalize!(str) ⇒ Object

normalize out multiple slashes, directory traversal, and self referrential directories



111
112
113
114
115
# File 'lib/rex/proto/http/request.rb', line 111

def normalize!(str)
  i = 0
  while (str.gsub!(/(\/\.\/|\/\w+\/\.\.\/|\/\/)/,'/')); i += 1; end
  i
end

#param_stringObject



171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
# File 'lib/rex/proto/http/request.rb', line 171

def param_string
  params=[]
  self.uri_parts['QueryString'].each_pair { |param, value|
    # inject a random number of params in between each param
    if self.junk_params
      rand(10)+5.times {
        params.push(Rex::Text.rand_text_alpha(rand(16) + 5) + '=' + Rex::Text.rand_text_alpha(rand(10) + 1))
      }
    end
    if value.kind_of?(Array)
      value.each { |subvalue|
  				params.push(Rex::Text.uri_encode(param, self.uri_encode_mode) + '=' + Rex::Text.uri_encode(subvalue, self.uri_encode_mode))
      }
    else
      if !value.nil?
        params.push(Rex::Text.uri_encode(param, self.uri_encode_mode) + '=' + Rex::Text.uri_encode(value, self.uri_encode_mode))
      else
        params.push(Rex::Text.uri_encode(param, self.uri_encode_mode))
      end
    end
  }

  # inject some junk params at the end of the param list, just to be sure :P
  if self.junk_params
    rand(10)+5.times {
      params.push(Rex::Text.rand_text_alpha(rand(32) + 5) + '=' + Rex::Text.rand_text_alpha(rand(64) + 5))
    }
  end
  params.join('&')
end

#parse_cgi_qstring(str) ⇒ Object

Parses a CGI query string into the var/val combinations.



331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
# File 'lib/rex/proto/http/request.rb', line 331

def parse_cgi_qstring(str)
  qstring = {}

  # Delimit on each variable
  str.split(/[;&]/).each { |vv|
    var = vv
    val = ''

    if (md = vv.match(/(.+?)=(.*)/))
      var = md[1]
      val = md[2]
    end

    # Add the item to the hash with logic to convert values to an array
    # if so desired.
    if (qstring.include?(var))
      if (qstring[var].kind_of?(Array))
        qstring[var] << val
      else
        curr = self.qstring[var]
        qstring[var] = [ curr, val ]
      end
    else
      qstring[var] = val
    end
  }

  return qstring
end

#qstringObject

If there were CGI parameters in the URI, this will hold a hash of each variable to value. If there is more than one value for a given variable, an array of each value is returned.



263
264
265
# File 'lib/rex/proto/http/request.rb', line 263

def qstring
  self.uri_parts['QueryString']
end

#resourceObject

Returns the resource that is being requested.



246
247
248
# File 'lib/rex/proto/http/request.rb', line 246

def resource
  self.uri_parts['Resource']
end

#resource=(rsrc) ⇒ Object

Changes the resource URI. This is used when making a request relative to a given mount point.



254
255
256
# File 'lib/rex/proto/http/request.rb', line 254

def resource=(rsrc)
  self.uri_parts['Resource'] = rsrc
end

#to_sObject

Returns a request packet



209
210
211
212
213
214
215
216
217
218
219
220
# File 'lib/rex/proto/http/request.rb', line 209

def to_s
  str = ''
  if self.junk_pipeline
    host = ''
    if self.headers['Host']
      host = "Host: #{self.headers['Host']}\r\n"
    end
    str << "GET / HTTP/1.1\r\n#{host}Connection: Keep-Alive\r\n\r\n" * self.junk_pipeline
    self.headers['Connection'] = 'Closed'
  end
  str + super
end

#update_cmd_parts(str) ⇒ Object

Updates the command parts for this specific packet type.



78
79
80
81
82
83
84
85
86
87
88
# File 'lib/rex/proto/http/request.rb', line 78

def update_cmd_parts(str)
  if (md = str.match(/^(.+?)\s+(.+?)\s+HTTP\/(.+?)\r?\n?$/i))
    self.method  = md[1]
    self.raw_uri = CGI.unescape(md[2])
    self.proto   = md[3]

    update_uri_parts
  else
    raise RuntimeError, "Invalid request command string", caller
  end
end

#update_uri_partsObject

Split the URI into the resource being requested and its query string.



93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
# File 'lib/rex/proto/http/request.rb', line 93

def update_uri_parts
  # If it has a query string, get the parts.
  if ((self.raw_uri) and (md = self.raw_uri.match(/(.+?)\?(.*)$/)))
    self.uri_parts['QueryString'] = parse_cgi_qstring(md[2])
    self.uri_parts['Resource']    = md[1]
  # Otherwise, just assume that the URI is equal to the resource being
  # requested.
  else
    self.uri_parts['QueryString'] = {}
    self.uri_parts['Resource']    = self.raw_uri
  end

  self.normalize!(resource)
  # Set the relative resource to the actual resource.
  self.relative_resource = resource
end

#uriObject

Puts a URI back together based on the URI parts



118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
# File 'lib/rex/proto/http/request.rb', line 118

def uri
  str = self.uri_parts['Resource'].dup || '/'

  # /././././
  if self.junk_self_referring_directories
    str.gsub!('/') {
      '/.' * (rand(3) + 1) + '/'
    }
  end

  # /%3faaa=bbbbb
  # which could possibly decode to "/?aaa=bbbbb", which if the IDS normalizes first, then splits the URI on ?, then it can be bypassed
  if self.junk_param_start
    str.sub!('/', '/%3f' + Rex::Text.rand_text_alpha(rand(5) + 1) + '=' + Rex::Text.rand_text_alpha(rand(10) + 1) + '/../')
  end

  # /RAND/../RAND../
  if self.junk_directories
    str.gsub!('/') {
      dirs = ''
      (rand(5)+5).times {
        dirs << '/' + Rex::Text.rand_text_alpha(rand(5) + 1) + '/..'
      }
      dirs + '/'
    }
  end

  # ////
  #
  # NOTE: this must be done after all other odd directory junk, since they would cancel this out, except junk_end_of_uri, since that a specific slash in a specific place
  if self.junk_slashes
    str.gsub!('/') {
      '/' * (rand(3) + 2)
    }
    str.sub!(/^[\/]+/, '/') # only one beginning slash!
  end

  # /%20HTTP/1.0%0d%0a/../../
  # which decodes to "/ HTTP/1.0\r\n"
  if self.junk_end_of_uri
    str.sub!(/^\//, '/%20HTTP/1.0%0d%0a/../../')
  end

  Rex::Text.uri_encode(str, self.uri_encode_mode)

  if !PostRequests.include?(self.method)
    if param_string.size > 0
      str << '?' + param_string
    end
  end
  str
end

#uri=(str) ⇒ Object

Updates the underlying URI structure



203
204
205
206
# File 'lib/rex/proto/http/request.rb', line 203

def uri=(str)
  self.raw_uri = str
  update_uri_parts
end