7ea5f87960
At some point in the distant past, the datastore was all strings and the various option types got parsed out in the appropriate places. Then, in the somewhat more recent past, the options started getting converted to regular ruby types (such as TrueClass for a BOOL options, etc) earlier in their life. Apparently, that change broke boolean http evasions. This commit fixes them by ensuring that +true+ is just as acceptable as "true". Fixes #6198, thanks Ashish for the report
844 lines
19 KiB
Ruby
844 lines
19 KiB
Ruby
require 'rex/socket'
|
|
require 'rex/proto/http'
|
|
require 'rex/text'
|
|
|
|
module Rex
|
|
module Proto
|
|
module Http
|
|
|
|
###
|
|
#
|
|
# Acts as a client to an HTTP server, sending requests and receiving responses.
|
|
#
|
|
# See the RFC: http://www.w3.org/Protocols/rfc2616/rfc2616.html
|
|
#
|
|
###
|
|
class Client
|
|
|
|
DefaultUserAgent = "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)"
|
|
|
|
#
|
|
# Creates a new client instance
|
|
#
|
|
def initialize(host, port = 80, context = {}, ssl = nil, ssl_version = nil, proxies = nil)
|
|
self.hostname = host
|
|
self.port = port.to_i
|
|
self.context = context
|
|
self.ssl = ssl
|
|
self.ssl_version = ssl_version
|
|
self.proxies = proxies
|
|
self.config = {
|
|
'read_max_data' => (1024*1024*1),
|
|
'vhost' => self.hostname,
|
|
'version' => '1.1',
|
|
'agent' => DefaultUserAgent,
|
|
#
|
|
# Evasion options
|
|
#
|
|
'uri_encode_mode' => 'hex-normal', # hex-all, hex-random, u-normal, u-random, u-all
|
|
'uri_encode_count' => 1, # integer
|
|
'uri_full_url' => false, # bool
|
|
'pad_method_uri_count' => 1, # integer
|
|
'pad_uri_version_count' => 1, # integer
|
|
'pad_method_uri_type' => 'space', # space, tab, apache
|
|
'pad_uri_version_type' => 'space', # space, tab, apache
|
|
'method_random_valid' => false, # bool
|
|
'method_random_invalid' => false, # bool
|
|
'method_random_case' => false, # bool
|
|
'version_random_valid' => false, # bool
|
|
'version_random_invalid' => false, # bool
|
|
'version_random_case' => false, # bool
|
|
'uri_dir_self_reference' => false, # bool
|
|
'uri_dir_fake_relative' => false, # bool
|
|
'uri_use_backslashes' => false, # bool
|
|
'pad_fake_headers' => false, # bool
|
|
'pad_fake_headers_count' => 16, # integer
|
|
'pad_get_params' => false, # bool
|
|
'pad_get_params_count' => 8, # integer
|
|
'pad_post_params' => false, # bool
|
|
'pad_post_params_count' => 8, # integer
|
|
'uri_fake_end' => false, # bool
|
|
'uri_fake_params_start' => false, # bool
|
|
'header_folding' => false, # bool
|
|
'chunked_size' => 0 # integer
|
|
}
|
|
|
|
# This is not used right now...
|
|
self.config_types = {
|
|
'uri_encode_mode' => ['hex-normal', 'hex-all', 'hex-random', 'u-normal', 'u-random', 'u-all'],
|
|
'uri_encode_count' => 'integer',
|
|
'uri_full_url' => 'bool',
|
|
'pad_method_uri_count' => 'integer',
|
|
'pad_uri_version_count' => 'integer',
|
|
'pad_method_uri_type' => ['space', 'tab', 'apache'],
|
|
'pad_uri_version_type' => ['space', 'tab', 'apache'],
|
|
'method_random_valid' => 'bool',
|
|
'method_random_invalid' => 'bool',
|
|
'method_random_case' => 'bool',
|
|
'version_random_valid' => 'bool',
|
|
'version_random_invalid' => 'bool',
|
|
'version_random_case' => 'bool',
|
|
'uri_dir_self_reference' => 'bool',
|
|
'uri_dir_fake_relative' => 'bool',
|
|
'uri_use_backslashes' => 'bool',
|
|
'pad_fake_headers' => 'bool',
|
|
'pad_fake_headers_count' => 'integer',
|
|
'pad_get_params' => 'bool',
|
|
'pad_get_params_count' => 'integer',
|
|
'pad_post_params' => 'bool',
|
|
'pad_post_params_count' => 'integer',
|
|
'uri_fake_end' => 'bool',
|
|
'uri_fake_params_start' => 'bool',
|
|
'header_folding' => 'bool',
|
|
'chunked_size' => 'integer'
|
|
}
|
|
end
|
|
|
|
#
|
|
# Set configuration options
|
|
#
|
|
def set_config(opts = {})
|
|
opts.each_pair do |var,val|
|
|
# Default type is string
|
|
typ = self.config_types[var] || 'string'
|
|
|
|
# These are enum types
|
|
if(typ.class.to_s == 'Array')
|
|
if not typ.include?(val)
|
|
raise RuntimeError, "The specified value for #{var} is not one of the valid choices"
|
|
end
|
|
end
|
|
|
|
# The caller should have converted these to proper ruby types, but
|
|
# take care of the case where they didn't before setting the
|
|
# config.
|
|
|
|
if(typ == 'bool')
|
|
val = (val =~ /^(t|y|1)$/i ? true : false || val === true)
|
|
end
|
|
|
|
if(typ == 'integer')
|
|
val = val.to_i
|
|
end
|
|
|
|
self.config[var]=val
|
|
end
|
|
|
|
end
|
|
|
|
#
|
|
# Create an arbitrary HTTP request
|
|
#
|
|
def request_raw(opts={})
|
|
c_enc = opts['encode'] || false
|
|
c_uri = opts['uri'] || '/'
|
|
c_body = opts['data'] || ''
|
|
c_meth = opts['method'] || 'GET'
|
|
c_prot = opts['proto'] || 'HTTP'
|
|
c_vers = opts['version'] || config['version'] || '1.1'
|
|
c_qs = opts['query']
|
|
c_ag = opts['agent'] || config['agent']
|
|
c_cook = opts['cookie'] || config['cookie']
|
|
c_host = opts['vhost'] || config['vhost'] || self.hostname
|
|
c_head = opts['headers'] || config['headers'] || {}
|
|
c_rawh = opts['raw_headers']|| config['raw_headers'] || ''
|
|
c_conn = opts['connection']
|
|
c_auth = opts['basic_auth'] || config['basic_auth'] || ''
|
|
|
|
uri = set_uri(c_uri)
|
|
|
|
req = ''
|
|
req << set_method(c_meth)
|
|
req << set_method_uri_spacer()
|
|
req << set_uri_prepend()
|
|
req << (c_enc ? set_encode_uri(uri) : uri)
|
|
|
|
if (c_qs)
|
|
req << '?'
|
|
req << (c_enc ? set_encode_qs(c_qs) : c_qs)
|
|
end
|
|
|
|
req << set_uri_append()
|
|
req << set_uri_version_spacer()
|
|
req << set_version(c_prot, c_vers)
|
|
req << set_host_header(c_host)
|
|
req << set_agent_header(c_ag)
|
|
|
|
if (c_auth.length > 0)
|
|
req << set_basic_auth_header(c_auth)
|
|
end
|
|
|
|
req << set_cookie_header(c_cook)
|
|
req << set_connection_header(c_conn)
|
|
req << set_extra_headers(c_head)
|
|
req << set_raw_headers(c_rawh)
|
|
req << set_body(c_body)
|
|
|
|
req
|
|
end
|
|
|
|
|
|
#
|
|
# Create a CGI compatible request
|
|
#
|
|
# Options:
|
|
# - agent: User-Agent header value
|
|
# - basic_auth: Basic-Auth header value
|
|
# - connection: Connection header value
|
|
# - cookie: Cookie header value
|
|
# - ctype: Content-Type header value, default: +application/x-www-form-urlencoded+
|
|
# - data: HTTP data (only useful with some methods, see rfc2616)
|
|
# - encode: URI encode the supplied URI
|
|
# - headers: HTTP headers as a hash, e.g. <code>{ "X-MyHeader" => "value" }</code>
|
|
# - method: HTTP method to use in the request, not limited to standard methods defined by rfc2616, default: GET
|
|
# - proto: protocol, default: HTTP
|
|
# - query: raw query string
|
|
# - raw_headers: HTTP headers as a hash
|
|
# - uri: the URI to request
|
|
# - vars_get: GET variables as a hash to be translated into a query string
|
|
# - vars_post: POST variables as a hash to be translated into POST data
|
|
# - version: version of the protocol, default: 1.1
|
|
# - vhost: Host header value
|
|
#
|
|
def request_cgi(opts={})
|
|
c_enc = opts['encode'] || false
|
|
c_cgi = opts['uri'] || '/'
|
|
c_body = opts['data'] || ''
|
|
c_meth = opts['method'] || 'GET'
|
|
c_prot = opts['proto'] || 'HTTP'
|
|
c_vers = opts['version'] || config['version'] || '1.1'
|
|
c_qs = opts['query'] || ''
|
|
c_varg = opts['vars_get'] || {}
|
|
c_varp = opts['vars_post'] || {}
|
|
c_head = opts['headers'] || config['headers'] || {}
|
|
c_rawh = opts['raw_headers']|| config['raw_headers'] || ''
|
|
c_type = opts['ctype'] || 'application/x-www-form-urlencoded'
|
|
c_ag = opts['agent'] || config['agent']
|
|
c_cook = opts['cookie'] || config['cookie']
|
|
c_host = opts['vhost'] || config['vhost']
|
|
c_conn = opts['connection']
|
|
c_path = opts['path_info']
|
|
c_auth = opts['basic_auth'] || config['basic_auth'] || ''
|
|
|
|
uri = set_cgi(c_cgi)
|
|
qstr = c_qs
|
|
pstr = c_body
|
|
|
|
if (config['pad_get_params'])
|
|
1.upto(config['pad_get_params_count'].to_i) do |i|
|
|
qstr << '&' if qstr.length > 0
|
|
qstr << set_encode_uri(Rex::Text.rand_text_alphanumeric(rand(32)+1))
|
|
qstr << '='
|
|
qstr << set_encode_uri(Rex::Text.rand_text_alphanumeric(rand(32)+1))
|
|
end
|
|
end
|
|
|
|
c_varg.each_pair do |var,val|
|
|
qstr << '&' if qstr.length > 0
|
|
qstr << set_encode_uri(var)
|
|
qstr << '='
|
|
qstr << set_encode_uri(val)
|
|
end
|
|
|
|
if (config['pad_post_params'])
|
|
1.upto(config['pad_post_params_count'].to_i) do |i|
|
|
pstr << '&' if qstr.length > 0
|
|
pstr << set_encode_uri(Rex::Text.rand_text_alphanumeric(rand(32)+1))
|
|
pstr << '='
|
|
pstr << set_encode_uri(Rex::Text.rand_text_alphanumeric(rand(32)+1))
|
|
end
|
|
end
|
|
|
|
c_varp.each_pair do |var,val|
|
|
pstr << '&' if pstr.length > 0
|
|
pstr << set_encode_uri(var)
|
|
pstr << '='
|
|
pstr << set_encode_uri(val)
|
|
end
|
|
|
|
req = ''
|
|
req << set_method(c_meth)
|
|
req << set_method_uri_spacer()
|
|
req << set_uri_prepend()
|
|
req << (c_enc ? set_encode_uri(uri):uri)
|
|
|
|
if (qstr.length > 0)
|
|
req << '?'
|
|
req << qstr
|
|
end
|
|
|
|
req << set_path_info(c_path)
|
|
req << set_uri_append()
|
|
req << set_uri_version_spacer()
|
|
req << set_version(c_prot, c_vers)
|
|
req << set_host_header(c_host)
|
|
req << set_agent_header(c_ag)
|
|
|
|
if (c_auth.length > 0)
|
|
req << set_basic_auth_header(c_auth)
|
|
end
|
|
|
|
req << set_cookie_header(c_cook)
|
|
req << set_connection_header(c_conn)
|
|
req << set_extra_headers(c_head)
|
|
|
|
req << set_content_type_header(c_type)
|
|
req << set_content_len_header(pstr.length)
|
|
req << set_chunked_header()
|
|
req << set_raw_headers(c_rawh)
|
|
req << set_body(pstr)
|
|
|
|
req
|
|
end
|
|
|
|
#
|
|
# Connects to the remote server if possible.
|
|
#
|
|
def connect(t = -1)
|
|
# If we already have a connection and we aren't pipelining, close it.
|
|
if (self.conn)
|
|
if !pipelining?
|
|
close
|
|
else
|
|
return self.conn
|
|
end
|
|
end
|
|
|
|
timeout = (t.nil? or t == -1) ? 0 : t
|
|
|
|
self.conn = Rex::Socket::Tcp.create(
|
|
'PeerHost' => self.hostname,
|
|
'PeerPort' => self.port.to_i,
|
|
'LocalHost' => self.local_host,
|
|
'LocalPort' => self.local_port,
|
|
'Context' => self.context,
|
|
'SSL' => self.ssl,
|
|
'SSLVersion'=> self.ssl_version,
|
|
'Proxies' => self.proxies,
|
|
'Timeout' => timeout
|
|
)
|
|
end
|
|
|
|
#
|
|
# Closes the connection to the remote server.
|
|
#
|
|
def close
|
|
if (self.conn)
|
|
self.conn.shutdown
|
|
self.conn.close
|
|
end
|
|
|
|
self.conn = nil
|
|
end
|
|
|
|
#
|
|
# Transmit an HTTP request and receive the response
|
|
# If persist is set, then the request will attempt
|
|
# to reuse an existing connection.
|
|
#
|
|
def send_recv(req, t = -1, persist=false)
|
|
@pipeline = persist
|
|
send_request(req, t)
|
|
res = read_response(t)
|
|
res.request = req.to_s if res
|
|
res
|
|
end
|
|
|
|
#
|
|
# Send an HTTP request to the server
|
|
#
|
|
def send_request(req, t = -1)
|
|
connect(t)
|
|
conn.put(req.to_s)
|
|
end
|
|
|
|
#
|
|
# Read a response from the server
|
|
#
|
|
def read_response(t = -1)
|
|
|
|
resp = Response.new
|
|
resp.max_data = config['read_max_data']
|
|
|
|
# Wait at most t seconds for the full response to be read in. We only
|
|
# do this if t was specified as a negative value indicating an infinite
|
|
# wait cycle. If t were specified as nil it would indicate that no
|
|
# response parsing is required.
|
|
|
|
return resp if not t
|
|
|
|
Timeout.timeout((t < 0) ? nil : t) do
|
|
|
|
rv = nil
|
|
while (
|
|
rv != Packet::ParseCode::Completed and
|
|
rv != Packet::ParseCode::Error
|
|
)
|
|
|
|
begin
|
|
|
|
buff = conn.get_once(-1, 1)
|
|
rv = resp.parse( buff || '' )
|
|
|
|
##########################################################################
|
|
# XXX: NOTE: BUG: get_once currently (as of r10042) rescues "Exception"
|
|
# As such, the following rescue block will ever be reached. -jjd
|
|
##########################################################################
|
|
|
|
# Handle unexpected disconnects
|
|
rescue ::Errno::EPIPE, ::EOFError, ::IOError
|
|
case resp.state
|
|
when Packet::ParseState::ProcessingHeader
|
|
resp = nil
|
|
when Packet::ParseState::ProcessingBody
|
|
# truncated request, good enough
|
|
resp.error = :truncated
|
|
end
|
|
break
|
|
end
|
|
|
|
# This is a dirty hack for broken HTTP servers
|
|
if rv == Packet::ParseCode::Completed
|
|
rbody = resp.body
|
|
rbufq = resp.bufq
|
|
|
|
rblob = rbody.to_s + rbufq.to_s
|
|
tries = 0
|
|
begin
|
|
# XXX: This doesn't deal with chunked encoding or "Content-type: text/html; charset=..."
|
|
while tries < 1000 and resp.headers["Content-Type"]== "text/html" and rblob !~ /<\/html>/i
|
|
buff = conn.get_once(-1, 0.05)
|
|
break if not buff
|
|
rblob += buff
|
|
tries += 1
|
|
end
|
|
rescue ::Errno::EPIPE, ::EOFError, ::IOError
|
|
end
|
|
|
|
resp.bufq = ""
|
|
resp.body = rblob
|
|
end
|
|
end
|
|
end
|
|
|
|
return resp if not resp
|
|
|
|
# As a last minute hack, we check to see if we're dealing with a 100 Continue here.
|
|
if resp.proto == '1.1' and resp.code == 100
|
|
# If so, our real response becaome the body, so we re-parse it.
|
|
body = resp.body
|
|
resp = Response.new
|
|
resp.max_data = config['read_max_data']
|
|
rv = resp.parse(body)
|
|
# XXX: At some point, this may benefit from processing post-completion code
|
|
# as seen above.
|
|
end
|
|
|
|
resp
|
|
end
|
|
|
|
#
|
|
# Cleans up any outstanding connections and other resources.
|
|
#
|
|
def stop
|
|
close
|
|
end
|
|
|
|
#
|
|
# Returns whether or not the conn is valid.
|
|
#
|
|
def conn?
|
|
conn != nil
|
|
end
|
|
|
|
#
|
|
# Whether or not connections should be pipelined.
|
|
#
|
|
def pipelining?
|
|
pipeline
|
|
end
|
|
|
|
#
|
|
# Return the encoded URI
|
|
# ['none','hex-normal', 'hex-all', 'u-normal', 'u-all']
|
|
def set_encode_uri(uri)
|
|
a = uri
|
|
self.config['uri_encode_count'].times {
|
|
a = Rex::Text.uri_encode(a, self.config['uri_encode_mode'])
|
|
}
|
|
return a
|
|
end
|
|
|
|
#
|
|
# Return the encoded query string
|
|
#
|
|
def set_encode_qs(qs)
|
|
a = qs
|
|
self.config['uri_encode_count'].times {
|
|
a = Rex::Text.uri_encode(a, self.config['uri_encode_mode'])
|
|
}
|
|
return a
|
|
end
|
|
|
|
#
|
|
# Return the uri
|
|
#
|
|
def set_uri(uri)
|
|
|
|
if (self.config['uri_dir_self_reference'])
|
|
uri.gsub!('/', '/./')
|
|
end
|
|
|
|
if (self.config['uri_dir_fake_relative'])
|
|
buf = ""
|
|
uri.split('/').each do |part|
|
|
cnt = rand(8)+2
|
|
1.upto(cnt) { |idx|
|
|
buf << "/" + Rex::Text.rand_text_alphanumeric(rand(32)+1)
|
|
}
|
|
buf << ("/.." * cnt)
|
|
buf << "/" + part
|
|
end
|
|
uri = buf
|
|
end
|
|
|
|
if (self.config['uri_full_url'])
|
|
url = self.ssl ? "https" : "http"
|
|
url << self.config['vhost']
|
|
url << ((self.port == 80) ? "" : ":#{self.port}")
|
|
url << uri
|
|
url
|
|
else
|
|
uri
|
|
end
|
|
end
|
|
|
|
#
|
|
# Return the cgi
|
|
#
|
|
def set_cgi(uri)
|
|
|
|
if (self.config['uri_dir_self_reference'])
|
|
uri.gsub!('/', '/./')
|
|
end
|
|
|
|
if (self.config['uri_dir_fake_relative'])
|
|
buf = ""
|
|
uri.split('/').each do |part|
|
|
cnt = rand(8)+2
|
|
1.upto(cnt) { |idx|
|
|
buf << "/" + Rex::Text.rand_text_alphanumeric(rand(32)+1)
|
|
}
|
|
buf << ("/.." * cnt)
|
|
buf << "/" + part
|
|
end
|
|
uri = buf
|
|
end
|
|
|
|
url = uri
|
|
|
|
if (self.config['uri_full_url'])
|
|
url = self.ssl ? "https" : "http"
|
|
url << self.config['vhost']
|
|
url << (self.port == 80) ? "" : ":#{self.port}"
|
|
url << uri
|
|
end
|
|
|
|
url
|
|
end
|
|
|
|
#
|
|
# Return the HTTP method string
|
|
#
|
|
def set_method(method)
|
|
ret = method
|
|
|
|
if (self.config['method_random_valid'])
|
|
ret = ['GET', 'POST', 'HEAD'][rand(3)]
|
|
end
|
|
|
|
if (self.config['method_random_invalid'])
|
|
ret = Rex::Text.rand_text_alpha(rand(20)+1)
|
|
end
|
|
|
|
if (self.config['method_random_case'])
|
|
ret = Rex::Text.to_rand_case(ret)
|
|
end
|
|
|
|
ret
|
|
end
|
|
|
|
#
|
|
# Return the HTTP version string
|
|
#
|
|
def set_version(protocol, version)
|
|
ret = protocol + "/" + version
|
|
|
|
if (self.config['version_random_valid'])
|
|
ret = protocol + "/" + ['1.0', '1.1'][rand(2)]
|
|
end
|
|
|
|
if (self.config['version_random_invalid'])
|
|
ret = Rex::Text.rand_text_alphanumeric(rand(20)+1)
|
|
end
|
|
|
|
if (self.config['version_random_case'])
|
|
ret = Rex::Text.to_rand_case(ret)
|
|
end
|
|
|
|
ret << "\r\n"
|
|
end
|
|
|
|
#
|
|
# Return the HTTP seperator and body string
|
|
#
|
|
def set_body(data)
|
|
return "\r\n" + data if self.config['chunked_size'] == 0
|
|
str = data.dup
|
|
chunked = ''
|
|
while str.size > 0
|
|
chunk = str.slice!(0,rand(self.config['chunked_size']) + 1)
|
|
chunked << sprintf("%x", chunk.size) + "\r\n" + chunk + "\r\n"
|
|
end
|
|
"\r\n" + chunked + "0\r\n\r\n"
|
|
end
|
|
|
|
#
|
|
# Return the HTTP path info
|
|
# TODO:
|
|
# * Encode path information
|
|
def set_path_info(path)
|
|
path ? path : ''
|
|
end
|
|
|
|
#
|
|
# Return the spacing between the method and uri
|
|
#
|
|
def set_method_uri_spacer
|
|
len = self.config['pad_method_uri_count'].to_i
|
|
set = " "
|
|
buf = ""
|
|
|
|
case self.config['pad_method_uri_type']
|
|
when 'tab'
|
|
set = "\t"
|
|
when 'apache'
|
|
set = "\t \x0b\x0c\x0d"
|
|
end
|
|
|
|
while(buf.length < len)
|
|
buf << set[ rand(set.length) ]
|
|
end
|
|
|
|
return buf
|
|
end
|
|
|
|
#
|
|
# Return the spacing between the uri and the version
|
|
#
|
|
def set_uri_version_spacer
|
|
len = self.config['pad_uri_version_count'].to_i
|
|
set = " "
|
|
buf = ""
|
|
|
|
case self.config['pad_uri_version_type']
|
|
when 'tab'
|
|
set = "\t"
|
|
when 'apache'
|
|
set = "\t \x0b\x0c\x0d"
|
|
end
|
|
|
|
while(buf.length < len)
|
|
buf << set[ rand(set.length) ]
|
|
end
|
|
|
|
return buf
|
|
end
|
|
|
|
#
|
|
# Return the padding to place before the uri
|
|
#
|
|
def set_uri_prepend
|
|
prefix = ""
|
|
|
|
if (self.config['uri_fake_params_start'])
|
|
prefix << '/%3fa=b/../'
|
|
end
|
|
|
|
if (self.config['uri_fake_end'])
|
|
prefix << '/%20HTTP/1.0/../../'
|
|
end
|
|
|
|
prefix
|
|
end
|
|
|
|
#
|
|
# Return the padding to place before the uri
|
|
#
|
|
def set_uri_append
|
|
# TODO:
|
|
# * Support different padding types
|
|
""
|
|
end
|
|
|
|
#
|
|
# Return the HTTP Host header
|
|
#
|
|
def set_host_header(host=nil)
|
|
return "" if self.config['uri_full_url']
|
|
host ||= self.config['vhost']
|
|
|
|
# IPv6 addresses must be placed in brackets
|
|
if Rex::Socket.is_ipv6?(host)
|
|
host = "[#{host}]"
|
|
end
|
|
|
|
# The port should be appended if non-standard
|
|
if not [80,443].include?(self.port)
|
|
host = host + ":#{port}"
|
|
end
|
|
|
|
set_formatted_header("Host", host)
|
|
end
|
|
|
|
#
|
|
# Return the HTTP agent header
|
|
#
|
|
def set_agent_header(agent)
|
|
agent ? set_formatted_header("User-Agent", agent) : ""
|
|
end
|
|
|
|
#
|
|
# Return the HTTP cookie header
|
|
#
|
|
def set_cookie_header(cookie)
|
|
cookie ? set_formatted_header("Cookie", cookie) : ""
|
|
end
|
|
|
|
#
|
|
# Return the HTTP connection header
|
|
#
|
|
def set_connection_header(conn)
|
|
conn ? set_formatted_header("Connection", conn) : ""
|
|
end
|
|
|
|
#
|
|
# Return the content type header
|
|
#
|
|
def set_content_type_header(ctype)
|
|
set_formatted_header("Content-Type", ctype)
|
|
end
|
|
|
|
#
|
|
# Return the content length header
|
|
def set_content_len_header(clen)
|
|
return "" if self.config['chunked_size'] > 0
|
|
set_formatted_header("Content-Length", clen)
|
|
end
|
|
|
|
#
|
|
# Return the Authorization basic-auth header
|
|
#
|
|
def set_basic_auth_header(auth)
|
|
auth ? set_formatted_header("Authorization", "Basic " + Rex::Text.encode_base64(auth)) : ""
|
|
end
|
|
|
|
#
|
|
# Return a string of formatted extra headers
|
|
#
|
|
def set_extra_headers(headers)
|
|
buf = ''
|
|
|
|
if (self.config['pad_fake_headers'])
|
|
1.upto(self.config['pad_fake_headers_count'].to_i) do |i|
|
|
buf << set_formatted_header(
|
|
Rex::Text.rand_text_alphanumeric(rand(32)+1),
|
|
Rex::Text.rand_text_alphanumeric(rand(32)+1)
|
|
)
|
|
end
|
|
end
|
|
|
|
headers.each_pair do |var,val|
|
|
buf << set_formatted_header(var, val)
|
|
end
|
|
|
|
buf
|
|
end
|
|
|
|
def set_chunked_header()
|
|
return "" if self.config['chunked_size'] == 0
|
|
set_formatted_header('Transfer-Encoding', 'chunked')
|
|
end
|
|
|
|
#
|
|
# Return a string of raw header data
|
|
#
|
|
def set_raw_headers(data)
|
|
data
|
|
end
|
|
|
|
#
|
|
# Return a formatted header string
|
|
#
|
|
def set_formatted_header(var, val)
|
|
if (self.config['header_folding'])
|
|
"#{var}:\r\n\t#{val}\r\n"
|
|
else
|
|
"#{var}: #{val}\r\n"
|
|
end
|
|
end
|
|
|
|
|
|
|
|
#
|
|
# The client request configuration
|
|
#
|
|
attr_accessor :config
|
|
#
|
|
# The client request configuration classes
|
|
#
|
|
attr_accessor :config_types
|
|
#
|
|
# Whether or not pipelining is in use.
|
|
#
|
|
attr_accessor :pipeline
|
|
#
|
|
# The local host of the client.
|
|
#
|
|
attr_accessor :local_host
|
|
#
|
|
# The local port of the client.
|
|
#
|
|
attr_accessor :local_port
|
|
#
|
|
# The underlying connection.
|
|
#
|
|
attr_accessor :conn
|
|
#
|
|
# The calling context to pass to the socket
|
|
#
|
|
attr_accessor :context
|
|
#
|
|
# The proxy list
|
|
#
|
|
attr_accessor :proxies
|
|
|
|
|
|
# When parsing the request, thunk off the first response from the server, since junk
|
|
attr_accessor :junk_pipeline
|
|
|
|
protected
|
|
|
|
# https
|
|
attr_accessor :ssl, :ssl_version # :nodoc:
|
|
|
|
attr_accessor :hostname, :port # :nodoc:
|
|
|
|
|
|
end
|
|
|
|
end
|
|
end
|
|
end
|
|
|