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

#[], #[]=, #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.



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

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

#junk_directoriesObject

add junk directories



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

def junk_directories
  @junk_directories
end

#junk_end_of_uriObject

add junk end of URI



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

def junk_end_of_uri
  @junk_end_of_uri
end

#junk_param_startObject

add junk start of params



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

def junk_param_start
  @junk_param_start
end

#junk_paramsObject

add junk params



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

def junk_params
  @junk_params
end

#junk_pipelineObject

add junk pipeline requests



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

def junk_pipeline
  @junk_pipeline
end

#junk_self_referring_directoriesObject

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



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

def junk_self_referring_directories
  @junk_self_referring_directories
end

#junk_slashesObject

add junk slashes



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

def junk_slashes
  @junk_slashes
end

#methodObject

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



277
278
279
# File 'lib/rex/proto/http/request.rb', line 277

def method
  @method
end

#protoObject

The protocol to be sent with the request.



290
291
292
# File 'lib/rex/proto/http/request.rb', line 290

def proto
  @proto
end

#raw_uriObject

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



281
282
283
# File 'lib/rex/proto/http/request.rb', line 281

def raw_uri
  @raw_uri
end

#relative_resourceObject

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



295
296
297
# File 'lib/rex/proto/http/request.rb', line 295

def relative_resource
  @relative_resource
end

#uri_encode_modeObject

encoding uri



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

def uri_encode_mode
  @uri_encode_mode
end

#uri_partsObject

The split up parts of the URI.



286
287
288
# File 'lib/rex/proto/http/request.rb', line 286

def uri_parts
  @uri_parts
end

Instance Method Details

#bodyObject



220
221
222
223
224
225
226
227
228
229
230
# File 'lib/rex/proto/http/request.rb', line 220

def body
  str = super || ''
  if str.length > 0
    return str
  end

  if PostRequests.include?(self.method)
    return param_string
  end
  ''
end

#cmd_stringObject

Returns the command string derived from the three values.



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

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



271
272
# File 'lib/rex/proto/http/request.rb', line 271

def meta_vars
end

#normalize!(str) ⇒ Object

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



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

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

#param_stringObject



169
170
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
# File 'lib/rex/proto/http/request.rb', line 169

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.



324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
# File 'lib/rex/proto/http/request.rb', line 324

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.



261
262
263
# File 'lib/rex/proto/http/request.rb', line 261

def qstring
  self.uri_parts['QueryString']
end

#resourceObject

Returns the resource that is being requested.



244
245
246
# File 'lib/rex/proto/http/request.rb', line 244

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.



252
253
254
# File 'lib/rex/proto/http/request.rb', line 252

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

#to_sObject

Returns a request packet



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

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.



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

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.



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

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



116
117
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
# File 'lib/rex/proto/http/request.rb', line 116

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



201
202
203
204
# File 'lib/rex/proto/http/request.rb', line 201

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