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

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, #bufq, #chunk_max_size, #chunk_min_size, #compress, #error, #headers, #incomplete, #max_data, #state, #transfer_chunked

Instance Method Summary collapse

Methods inherited from Packet

#[], #[]=, #chunk, #completed?, #from_s, #parse, #reset, #reset_except_queue

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
# 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'

  update_uri_parts
end

Instance Attribute Details

#junk_directoriesObject

add junk directories



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

def junk_directories
  @junk_directories
end

#junk_end_of_uriObject

add junk end of URI



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

def junk_end_of_uri
  @junk_end_of_uri
end

#junk_param_startObject

add junk start of params



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

def junk_param_start
  @junk_param_start
end

#junk_paramsObject

add junk params



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

def junk_params
  @junk_params
end

#junk_pipelineObject

add junk pipeline requests



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

def junk_pipeline
  @junk_pipeline
end

#junk_self_referring_directoriesObject

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



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

def junk_self_referring_directories
  @junk_self_referring_directories
end

#junk_slashesObject

add junk slashes



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

def junk_slashes
  @junk_slashes
end

#methodObject

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



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

def method
  @method
end

#protoObject

The protocol to be sent with the request.



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

def proto
  @proto
end

#raw_uriObject

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



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

def raw_uri
  @raw_uri
end

#relative_resourceObject

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



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

def relative_resource
  @relative_resource
end

#uri_encode_modeObject

encoding uri



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

def uri_encode_mode
  @uri_encode_mode
end

#uri_partsObject

The split up parts of the URI.



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

def uri_parts
  @uri_parts
end

Instance Method Details

#bodyObject



216
217
218
219
220
221
222
223
224
225
226
# File 'lib/rex/proto/http/request.rb', line 216

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.



231
232
233
234
235
# File 'lib/rex/proto/http/request.rb', line 231

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



267
268
# File 'lib/rex/proto/http/request.rb', line 267

def meta_vars
end

#normalize!(str) ⇒ Object

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



105
106
107
108
109
# File 'lib/rex/proto/http/request.rb', line 105

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

#param_stringObject



165
166
167
168
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
# File 'lib/rex/proto/http/request.rb', line 165

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.



320
321
322
323
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
# File 'lib/rex/proto/http/request.rb', line 320

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.



257
258
259
# File 'lib/rex/proto/http/request.rb', line 257

def qstring
  self.uri_parts['QueryString']
end

#resourceObject

Returns the resource that is being requested.



240
241
242
# File 'lib/rex/proto/http/request.rb', line 240

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.



248
249
250
# File 'lib/rex/proto/http/request.rb', line 248

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

#to_sObject

Returns a request packet



203
204
205
206
207
208
209
210
211
212
213
214
# File 'lib/rex/proto/http/request.rb', line 203

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.



72
73
74
75
76
77
78
79
80
81
82
# File 'lib/rex/proto/http/request.rb', line 72

def update_cmd_parts(str)
  if (md = str.match(/^(.+?)\s+(.+?)\s+HTTP\/(.+?)\r?\n?$/))
    self.method  = md[1]
    self.raw_uri = URI.decode(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.



87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
# File 'lib/rex/proto/http/request.rb', line 87

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



112
113
114
115
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
# File 'lib/rex/proto/http/request.rb', line 112

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



197
198
199
200
# File 'lib/rex/proto/http/request.rb', line 197

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