Refactor config -> opts

Puts all the evasion stuff in the same place as regular HTTP options to
make it easier to deal with.
bug/bundler_fix
James Lee 2013-02-27 17:29:26 -06:00
parent 5606db3f9c
commit 4edd46216f
4 changed files with 103 additions and 135 deletions

View File

@ -23,7 +23,6 @@ module Http
###
class Client
DefaultUserAgent = Rex::Proto::Http::ClientRequest::DefaultUserAgent
#
# Creates a new client instance
@ -41,8 +40,6 @@ class Client
self.config = {
'read_max_data' => (1024*1024*1),
'vhost' => self.hostname,
'version' => '1.1',
'agent' => DefaultUserAgent,
}.merge(Http::ClientRequest::DefaultConfig)
self.config_types = {

View File

@ -14,6 +14,25 @@ class ClientRequest
DefaultUserAgent = "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)"
DefaultConfig = {
#
# Regular HTTP stuff
#
'agent' => DefaultUserAgent,
'cgi' => true,
'cookie' => nil,
'data' => '',
'headers' => {},
'raw_headers' => '',
'method' => 'GET',
'path_info' => '',
'port' => 80,
'proto' => 'HTTP',
'ssl' => false,
'uri' => '/',
'vars_get' => {},
'vars_post' => {},
'version' => '1.1',
#
# Evasion options
#
@ -45,6 +64,7 @@ class ClientRequest
'uri_fake_params_start' => false, # bool
'header_folding' => false, # bool
'chunked_size' => 0, # integer
#
# NTLM Options
#
@ -61,68 +81,25 @@ class ClientRequest
'DigestAuthIIS' => true
}
attr_accessor :authorization
attr_accessor :cgi
attr_accessor :config
attr_accessor :connection
attr_accessor :content_type
attr_accessor :cookie
attr_accessor :data
attr_accessor :headers
attr_accessor :host
attr_accessor :method
attr_accessor :path
attr_accessor :port
attr_accessor :protocol
attr_accessor :query
attr_accessor :raw_headers
attr_accessor :ssl
attr_accessor :uri
attr_accessor :user_agent
attr_accessor :vars_get
attr_accessor :vars_post
attr_accessor :version
attr_reader :opts
def initialize(opts={})
@cgi = (opts['cgi'].nil? ? true : opts['cgi'])
@config = DefaultConfig.merge(opts['client_config'] || {})
@connection = opts['connection']
@content_type = opts['ctype']
@cookie = opts['cookie']
@data = opts['data'] || ""
@headers = opts['headers'] || {}
@host = opts['vhost']
@method = opts['method'] || "GET"
@path = opts['path_info']
@port = opts['port'] || 80
@protocol = opts['proto'] || "HTTP"
@query = opts['query'] || ""
@ssl = opts['ssl']
@raw_headers = opts['raw_headers'] || ""
@uri = opts['uri']
@user_agent = opts['agent']
@vars_get = opts['vars_get'] || {}
@vars_post = opts['vars_post'] || {}
@version = opts['version'] || "1.1"
@opts = opts
@opts = DefaultConfig.merge(opts)
end
def to_s
# Start GET query string
qstr = query.dup
qstr = opts['query'] ? opts['query'].dup : ""
# Start POST data string
pstr = data.dup
pstr = opts['data'] ? opts['data'].dup : ""
if cgi
if opts['cgi']
uri_str= set_cgi
if (config['pad_get_params'])
1.upto(config['pad_get_params_count'].to_i) do |i|
if (opts['pad_get_params'])
1.upto(opts['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 << '='
@ -130,33 +107,33 @@ class ClientRequest
end
end
vars_get.each_pair do |var,val|
opts['vars_get'].each_pair do |var,val|
qstr << '&' if qstr.length > 0
qstr << (config['encode_params'] ? set_encode_uri(var) : var)
qstr << (opts['encode_params'] ? set_encode_uri(var) : var)
qstr << '='
qstr << (config['encode_params'] ? set_encode_uri(val) : val)
qstr << (opts['encode_params'] ? set_encode_uri(val) : val)
end
if (config['pad_post_params'])
1.upto(config['pad_post_params_count'].to_i) do |i|
if (opts['pad_post_params'])
1.upto(opts['pad_post_params_count'].to_i) do |i|
rand_var = Rex::Text.rand_text_alphanumeric(rand(32)+1)
rand_val = Rex::Text.rand_text_alphanumeric(rand(32)+1)
pstr << '&' if pstr.length > 0
pstr << (config['encode_params'] ? set_encode_uri(rand_var) : rand_var)
pstr << (opts['encode_params'] ? set_encode_uri(rand_var) : rand_var)
pstr << '='
pstr << (config['encode_params'] ? set_encode_uri(rand_val) : rand_val)
pstr << (opts['encode_params'] ? set_encode_uri(rand_val) : rand_val)
end
end
vars_post.each_pair do |var,val|
opts['vars_post'].each_pair do |var,val|
pstr << '&' if pstr.length > 0
pstr << (config['encode_params'] ? set_encode_uri(var) : var)
pstr << (opts['encode_params'] ? set_encode_uri(var) : var)
pstr << '='
pstr << (config['encode_params'] ? set_encode_uri(val) : val)
pstr << (opts['encode_params'] ? set_encode_uri(val) : val)
end
else
uri_str = set_uri
if config['encode']
if opts['encode']
qstr = set_encode_uri(qstr)
end
end
@ -166,7 +143,7 @@ class ClientRequest
req << set_method_uri_spacer()
req << set_uri_prepend()
if config['encode']
if opts['encode']
req << set_encode_uri(uri_str)
else
req << uri_str
@ -185,7 +162,7 @@ class ClientRequest
req << set_host_header
# If an explicit User-Agent header is set, then use that instead of the value of user_agent
unless headers.keys.map{|x| x.downcase }.include?('user-agent')
unless opts['headers'].keys.map{|x| x.downcase }.include?('user-agent')
req << set_agent_header
end
@ -197,19 +174,19 @@ class ClientRequest
req << set_content_type_header
req << set_content_len_header(pstr.length)
req << set_chunked_header()
req << raw_headers
req << opts['raw_headers']
req << set_body(pstr)
end
protected
def set_uri
uri_str = uri.dup
if (config['uri_dir_self_reference'])
uri_str = opts['uri'].dup
if (opts['uri_dir_self_reference'])
uri_str.gsub!('/', '/./')
end
if (config['uri_dir_fake_relative'])
if (opts['uri_dir_fake_relative'])
buf = ""
uri_str.split('/').each do |part|
cnt = rand(8)+2
@ -222,10 +199,10 @@ class ClientRequest
uri_str = buf
end
if (config['uri_full_url'])
url = self.ssl ? "https://" : "http://"
url << self.config['vhost']
url << ((self.port == 80) ? "" : ":#{self.port}")
if (opts['uri_full_url'])
url = opts['ssl'] ? "https://" : "http://"
url << opts['vhost']
url << ((opts['port'] == 80) ? "" : ":#{opts['port']}")
url << uri_str
url
else
@ -234,12 +211,12 @@ class ClientRequest
end
def set_cgi
uri_str = uri.dup
if (config['uri_dir_self_reference'])
uri_str = opts['uri'].dup
if (opts['uri_dir_self_reference'])
uri_str.gsub!('/', '/./')
end
if (config['uri_dir_fake_relative'])
if (opts['uri_dir_fake_relative'])
buf = ""
uri_str.split('/').each do |part|
cnt = rand(8)+2
@ -254,10 +231,10 @@ class ClientRequest
url = uri_str
if (config['uri_full_url'])
url = self.ssl ? "https" : "http"
url << self.config['vhost']
url << (self.port == 80) ? "" : ":#{self.port}"
if (opts['uri_full_url'])
url = opts['ssl'] ? "https" : "http"
url << opts['vhost']
url << (opts['port'] == 80) ? "" : ":#{opts['port']}"
url << uri_str
end
@ -266,24 +243,24 @@ class ClientRequest
def set_encode_uri(str)
a = str.dup
config['uri_encode_count'].times {
a = Rex::Text.uri_encode(a, config['uri_encode_mode'])
opts['uri_encode_count'].times {
a = Rex::Text.uri_encode(a, opts['uri_encode_mode'])
}
return a
end
def set_method
ret = method.dup
ret = opts['method'].dup
if (config['method_random_valid'])
if (opts['method_random_valid'])
ret = ['GET', 'POST', 'HEAD'][rand(3)]
end
if (config['method_random_invalid'])
if (opts['method_random_invalid'])
ret = Rex::Text.rand_text_alpha(rand(20)+1)
end
if (config['method_random_case'])
if (opts['method_random_case'])
ret = Rex::Text.to_rand_case(ret)
end
@ -291,11 +268,11 @@ class ClientRequest
end
def set_method_uri_spacer
len = config['pad_method_uri_count'].to_i
len = opts['pad_method_uri_count'].to_i
set = " "
buf = ""
case config['pad_method_uri_type']
case opts['pad_method_uri_type']
when 'tab'
set = "\t"
when 'apache'
@ -315,11 +292,11 @@ class ClientRequest
def set_uri_prepend
prefix = ""
if (config['uri_fake_params_start'])
if (opts['uri_fake_params_start'])
prefix << '/%3fa=b/../'
end
if (config['uri_fake_end'])
if (opts['uri_fake_end'])
prefix << '/%20HTTP/1.0/../../'
end
@ -331,7 +308,7 @@ class ClientRequest
# TODO:
# * Encode path information
def set_path_info
path ? path : ''
opts['path_info'] ? opts['path_info'] : ''
end
#
@ -347,11 +324,11 @@ class ClientRequest
# Return the spacing between the uri and the version
#
def set_uri_version_spacer
len = config['pad_uri_version_count'].to_i
len = opts['pad_uri_version_count'].to_i
set = " "
buf = ""
case config['pad_uri_version_type']
case opts['pad_uri_version_type']
when 'tab'
set = "\t"
when 'apache'
@ -369,17 +346,17 @@ class ClientRequest
# Return the HTTP version string
#
def set_version
ret = protocol + "/" + version
ret = opts['proto'] + "/" + opts['version']
if (config['version_random_valid'])
ret = protocol + "/" + ['1.0', '1.1'][rand(2)]
if (opts['version_random_valid'])
ret = opts['proto'] + "/" + ['1.0', '1.1'][rand(2)]
end
if (config['version_random_invalid'])
if (opts['version_random_invalid'])
ret = Rex::Text.rand_text_alphanumeric(rand(20)+1)
end
if (config['version_random_case'])
if (opts['version_random_case'])
ret = Rex::Text.to_rand_case(ret)
end
@ -390,7 +367,7 @@ class ClientRequest
# Return a formatted header string
#
def set_formatted_header(var, val)
if (self.config['header_folding'])
if (self.opts['header_folding'])
"#{var}:\r\n\t#{val}\r\n"
else
"#{var}: #{val}\r\n"
@ -401,38 +378,38 @@ class ClientRequest
# Return the HTTP agent header
#
def set_agent_header
user_agent ? set_formatted_header("User-Agent", user_agent) : ""
opts['agent'] ? set_formatted_header("User-Agent", opts['agent']) : ""
end
def set_auth_header
authorization ? set_formatted_header("Authorization", authorization) : ""
opts['authorization'] ? set_formatted_header("Authorization", opts['authorization']) : ""
end
#
# Return the HTTP cookie header
#
def set_cookie_header
cookie ? set_formatted_header("Cookie", cookie) : ""
opts['cookie'] ? set_formatted_header("Cookie", opts['cookie']) : ""
end
#
# Return the HTTP connection header
#
def set_connection_header
connection ? set_formatted_header("Connection", connection) : ""
opts['connection'] ? set_formatted_header("Connection", opts['connection']) : ""
end
#
# Return the content type header
#
def set_content_type_header
set_formatted_header("Content-Type", content_type)
opts['ctype'] ? set_formatted_header("Content-Type", opts['ctype']) : ""
end
#
# Return the content length header
def set_content_len_header(clen)
return "" if config['chunked_size'] > 0
return "" if opts['chunked_size'] > 0
set_formatted_header("Content-Length", clen)
end
@ -440,8 +417,8 @@ class ClientRequest
# Return the HTTP Host header
#
def set_host_header
return "" if config['uri_full_url']
host ||= config['vhost']
return "" if opts['uri_full_url']
host ||= opts['vhost']
# IPv6 addresses must be placed in brackets
if Rex::Socket.is_ipv6?(host)
@ -449,8 +426,8 @@ class ClientRequest
end
# The port should be appended if non-standard
if not [80,443].include?(port)
host = host + ":#{port}"
if not [80,443].include?(opts['port'])
host = host + ":#{opts['port']}"
end
set_formatted_header("Host", host)
@ -462,8 +439,8 @@ class ClientRequest
def set_extra_headers
buf = ''
if (config['pad_fake_headers'])
1.upto(config['pad_fake_headers_count'].to_i) do |i|
if (opts['pad_fake_headers'])
1.upto(opts['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)
@ -471,7 +448,7 @@ class ClientRequest
end
end
headers.each_pair do |var,val|
opts['headers'].each_pair do |var,val|
buf << set_formatted_header(var, val)
end
@ -479,7 +456,7 @@ class ClientRequest
end
def set_chunked_header
return "" if config['chunked_size'] == 0
return "" if opts['chunked_size'] == 0
set_formatted_header('Transfer-Encoding', 'chunked')
end
@ -487,11 +464,11 @@ class ClientRequest
# Return the HTTP seperator and body string
#
def set_body(bdata)
return "\r\n" + bdata if config['chunked_size'] == 0
return "\r\n" + bdata if opts['chunked_size'] == 0
str = bdata.dup
chunked = ''
while str.size > 0
chunk = str.slice!(0,rand(config['chunked_size']) + 1)
chunk = str.slice!(0,rand(opts['chunked_size']) + 1)
chunked << sprintf("%x", chunk.size) + "\r\n" + chunk + "\r\n"
end
"\r\n" + chunked + "0\r\n\r\n"

View File

@ -4,7 +4,7 @@ require 'rex/proto/http/client_request'
shared_context "with 'uri_dir_self_reference'" do
before(:all) do
client_request.config['uri_dir_self_reference'] = true
client_request.opts['uri_dir_self_reference'] = true
end
it "should return the unmodified uri" do
@ -14,9 +14,9 @@ end
shared_context "with no evasions" do
before(:all) do
client_request.config['uri_dir_self_reference'] = false
client_request.config['uri_fake_params_start'] = false
client_request.config['uri_full_url'] = false
client_request.opts['uri_dir_self_reference'] = false
client_request.opts['uri_fake_params_start'] = false
client_request.opts['uri_full_url'] = false
end
it "should return the unmodified uri" do
@ -27,11 +27,11 @@ end
shared_context "with 'uri_full_url'" do
before(:all) do
client_request.config['uri_full_url'] = true
client_request.opts['uri_full_url'] = true
end
before(:each) do
client_request.config['vhost'] = host
client_request.opts['vhost'] = host
end
context "with ipv4 host" do
@ -43,7 +43,7 @@ shared_context "with 'uri_full_url'" do
context "with ipv6 host" do
let(:host) { '2001:DB8::1' }
#before(:each) do
# client_request.config['vhost'] = "[#{host}]"
# client_request.opts['vhost'] = "[#{host}]"
#end
it_behaves_like "uri_full_url"
@ -83,9 +83,7 @@ describe Rex::Proto::Http::ClientRequest do
[ "with reasonable default options",
default_options.merge({
'agent' => "Mozilla/4.0 (compatible; Metasploit RSPEC)",
# Yes, vhost is in the config. There is no godly reason why this
# should be so.
'client_config' => { 'vhost' => 'www.example.com', },
'vhost' => 'www.example.com',
}),
{
:set_cgi => { :result => "/" },
@ -106,7 +104,7 @@ describe Rex::Proto::Http::ClientRequest do
[ "with header folding",
default_options.merge({
'agent' => "Mozilla/4.0 (compatible; Metasploit RSPEC)",
'client_config' => { 'header_folding' => true, }
'header_folding' => true,
}),
{
:set_uri => { :result => "/" },
@ -124,7 +122,7 @@ describe Rex::Proto::Http::ClientRequest do
[ "with ipv6 host",
default_options.merge({
'client_config' => { 'vhost' => "2001:DB8::1" },
'vhost' => "2001:DB8::1",
}),
{
:set_host_header => { :result => "Host: [2001:DB8::1]\r\n" },
@ -134,7 +132,7 @@ describe Rex::Proto::Http::ClientRequest do
[ "with ipv6 host and non-default port",
default_options.merge({
'port' => 1234,
'client_config' => { 'vhost' => "2001:DB8::1" },
'vhost' => "2001:DB8::1",
}),
{
:set_host_header => { :result => "Host: [2001:DB8::1]:1234\r\n" },
@ -162,11 +160,9 @@ describe Rex::Proto::Http::ClientRequest do
context "with GET paramaters" do
subject(:client_request) {
options_with_params = default_options.merge({
'client_config' => {
'uri_encode_mode' => encode_mode,
'encode_params' => encode_params,
'encode' => false,
},
'vars_get' => vars_get,
})
Rex::Proto::Http::ClientRequest.new(options_with_params)

View File

@ -56,8 +56,6 @@ describe Rex::Proto::Http::Client do
req = cli.request_cgi
req.should be_a_kind_of Rex::Proto::Http::ClientRequest
req.port.should == 80
req.ssl.should be_false
end
it "should attempt to connect to a server" do