Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/lib/rex/proto/http/request.rb
Views: 11704
# -*- coding: binary -*-1require 'uri'234module Rex5module Proto6module Http78###9#10# HTTP request class.11#12###13class Request < Packet1415PostRequests = ['POST', 'SEARCH']1617##18#19# Some individual request types.20#21##2223#24# HTTP GET request class wrapper.25#26class Get < Request27def initialize(uri = '/', proto = DefaultProtocol)28super('GET', uri, proto)29end30end3132#33# HTTP POST request class wrapper.34#35class Post < Request36def initialize(uri = '/', proto = DefaultProtocol)37super('POST', uri, proto)38end39end4041#42# HTTP PUT request class wrapper.43#44class Put < Request45def initialize(uri = '/', proto = DefaultProtocol)46super('PUT', uri, proto)47end48end4950#51# Initializes an instance of an HTTP request with the supplied method, URI,52# and protocol.53#54def initialize(method = 'GET', uri = '/', proto = DefaultProtocol)55super()5657self.method = method58self.raw_uri = uri59self.uri_parts = {}60self.proto = proto || DefaultProtocol61self.chunk_min_size = 162self.chunk_max_size = 1063self.uri_encode_mode = 'hex-normal'6465if self.method == 'GET' || self.method == 'CONNECT'66self.auto_cl = false67end6869update_uri_parts70end7172#73# Updates the command parts for this specific packet type.74#75def update_cmd_parts(str)76if (md = str.match(/^(.+?)\s+(.+?)\s+HTTP\/(.+?)\r?\n?$/i))77self.method = md[1]78self.raw_uri = CGI.unescape(md[2])79self.proto = md[3]8081update_uri_parts82else83raise RuntimeError, "Invalid request command string", caller84end85end8687#88# Split the URI into the resource being requested and its query string.89#90def update_uri_parts91# If it has a query string, get the parts.92if ((self.raw_uri) and (md = self.raw_uri.match(/(.+?)\?(.*)$/)))93self.uri_parts['QueryString'] = parse_cgi_qstring(md[2])94self.uri_parts['Resource'] = md[1]95# Otherwise, just assume that the URI is equal to the resource being96# requested.97else98self.uri_parts['QueryString'] = {}99self.uri_parts['Resource'] = self.raw_uri100end101102self.normalize!(resource)103# Set the relative resource to the actual resource.104self.relative_resource = resource105end106107# normalize out multiple slashes, directory traversal, and self referrential directories108def normalize!(str)109i = 0110while (str.gsub!(/(\/\.\/|\/\w+\/\.\.\/|\/\/)/,'/')); i += 1; end111i112end113114# Puts a URI back together based on the URI parts115def uri116str = self.uri_parts['Resource'].dup || '/'117118# /././././119if self.junk_self_referring_directories120str.gsub!(/\//) {121'/.' * (rand(3) + 1) + '/'122}123end124125# /%3faaa=bbbbb126# which could possibly decode to "/?aaa=bbbbb", which if the IDS normalizes first, then splits the URI on ?, then it can be bypassed127if self.junk_param_start128str.sub!(/\//, '/%3f' + Rex::Text.rand_text_alpha(rand(5) + 1) + '=' + Rex::Text.rand_text_alpha(rand(10) + 1) + '/../')129end130131# /RAND/../RAND../132if self.junk_directories133str.gsub!(/\//) {134dirs = ''135(rand(5)+5).times {136dirs << '/' + Rex::Text.rand_text_alpha(rand(5) + 1) + '/..'137}138dirs + '/'139}140end141142# ////143#144# 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 place145if self.junk_slashes146str.gsub!(/\//) {147'/' * (rand(3) + 2)148}149str.sub!(/^[\/]+/, '/') # only one beginning slash!150end151152# /%20HTTP/1.0%0d%0a/../../153# which decodes to "/ HTTP/1.0\r\n"154if self.junk_end_of_uri155str.sub!(/^\//, '/%20HTTP/1.0%0d%0a/../../')156end157158Rex::Text.uri_encode(str, self.uri_encode_mode)159160if !PostRequests.include?(self.method)161if param_string.size > 0162str << '?' + param_string163end164end165str166end167168def param_string169params=[]170self.uri_parts['QueryString'].each_pair { |param, value|171# inject a random number of params in between each param172if self.junk_params173rand(10)+5.times {174params.push(Rex::Text.rand_text_alpha(rand(16) + 5) + '=' + Rex::Text.rand_text_alpha(rand(10) + 1))175}176end177if value.kind_of?(Array)178value.each { |subvalue|179params.push(Rex::Text.uri_encode(param, self.uri_encode_mode) + '=' + Rex::Text.uri_encode(subvalue, self.uri_encode_mode))180}181else182if !value.nil?183params.push(Rex::Text.uri_encode(param, self.uri_encode_mode) + '=' + Rex::Text.uri_encode(value, self.uri_encode_mode))184else185params.push(Rex::Text.uri_encode(param, self.uri_encode_mode))186end187end188}189190# inject some junk params at the end of the param list, just to be sure :P191if self.junk_params192rand(10)+5.times {193params.push(Rex::Text.rand_text_alpha(rand(32) + 5) + '=' + Rex::Text.rand_text_alpha(rand(64) + 5))194}195end196params.join('&')197end198199# Updates the underlying URI structure200def uri=(str)201self.raw_uri = str202update_uri_parts203end204205# Returns a request packet206def to_s207str = ''208if self.junk_pipeline209host = ''210if self.headers['Host']211host = "Host: #{self.headers['Host']}\r\n"212end213str << "GET / HTTP/1.1\r\n#{host}Connection: Keep-Alive\r\n\r\n" * self.junk_pipeline214self.headers['Connection'] = 'Closed'215end216str + super217end218219def body220str = super || ''221if str.length > 0222return str223end224225if PostRequests.include?(self.method)226return param_string227end228''229end230231#232# Returns the command string derived from the three values.233#234def cmd_string235proto_str = (self.proto =~ /^\d/) ? "HTTP/#{self.proto}" : self.proto236237"#{self.method} #{self.uri} #{proto_str}\r\n"238end239240#241# Returns the resource that is being requested.242#243def resource244self.uri_parts['Resource']245end246247#248# Changes the resource URI. This is used when making a request relative to249# a given mount point.250#251def resource=(rsrc)252self.uri_parts['Resource'] = rsrc253end254255#256# If there were CGI parameters in the URI, this will hold a hash of each257# variable to value. If there is more than one value for a given variable,258# an array of each value is returned.259#260def qstring261self.uri_parts['QueryString']262end263264#265# Returns a hash of variables that contain information about the request,266# such as the remote host information.267#268# TODO269#270def meta_vars271end272273#274# The method being used for the request (e.g. GET).275#276attr_accessor :method277#278# The raw URI being requested, before any mucking gets to it279#280attr_accessor :raw_uri281282#283# The split up parts of the URI.284#285attr_accessor :uri_parts286#287# The protocol to be sent with the request.288#289attr_accessor :proto290291#292# The resource path relative to the root of a server mount point.293#294attr_accessor :relative_resource295296# add junk directories297attr_accessor :junk_directories298299# add junk slashes300attr_accessor :junk_slashes301302# add junk self referring directories (aka /././././)303attr_accessor :junk_self_referring_directories304305# add junk params306attr_accessor :junk_params307308# add junk pipeline requests309attr_accessor :junk_pipeline310311# add junk start of params312attr_accessor :junk_param_start313314# add junk end of URI315attr_accessor :junk_end_of_uri316317# encoding uri318attr_accessor :uri_encode_mode319320#321# Parses a CGI query string into the var/val combinations.322#323def parse_cgi_qstring(str)324qstring = {}325326# Delimit on each variable327str.split(/[;&]/).each { |vv|328var = vv329val = ''330331if (md = vv.match(/(.+?)=(.*)/))332var = md[1]333val = md[2]334end335336# Add the item to the hash with logic to convert values to an array337# if so desired.338if (qstring.include?(var))339if (qstring[var].kind_of?(Array))340qstring[var] << val341else342curr = self.qstring[var]343qstring[var] = [ curr, val ]344end345else346qstring[var] = val347end348}349350return qstring351end352353end354355end356end357end358359360