Skip to content

使用 Rex Proto Http Client 发送 HTTP 请求

L edited this page May 5, 2022 · 1 revision

**注意: 此文档可能需要经过审查. **

使用 Rex::Proto::Http::Client 发送 HTTP 请求

Rex 库 (Ruby 扩展库) 是 Metasploit 框架架构中最基本的部分. 模块通常不直接使用 Rex, 而是依赖于框架核心及其 mixin 以实现更好的代码共享. 如果你是 Metasploit 模块开发人员, 那么 lib/msf/core 目录应该足以满足你的大部分需求. 如果你正在编写一个使用 HTTP 的模块, 那么 Msf::Exploit::Remote::HttpClient mixin (位于 lib/msf/core/exploit/http/client 中) 很可能是你想要的.

但是, 在某些情况下, 你实际上不能使用 HttpClient mixin. 最常见的实际上是在使用 LoginScanner API 编写基于表单的登录模块时. 如果你发现自己处于这种情况, 请使用 Rex::Proto::Http::Client.

初始化 Rex::Proto::Http::Client

Rex::Proto::Http::Client 初始化程序创建一个新的 HTTP 客户端实例, 最重要的部分是:

def initialize(host, port = 80, context = {}, ssl = nil, ssl_version = nil, proxies = nil, username = '', password = '')

正如你所见, 只有主机参数是必需的, 其余的都是可选的. 但是让我们快速回顾一下所有这些:

参数名 数据类型 描述
host String 目标主机 IP
port Fixnum 目标主机端口
context Hash SSL Socket 请求时的上下文
ssl Boolean True 则启用 SSL
ssl_version String SSL2、SSL3 或 TLS1
proxies String 配置代理
username String 用于自动认证的用户名
password String 用于自动认证的密码

初始化 Rex::Proto::Http::Client 的代码示例:

cli = Rex::Proto::Http::Client.new(rhost, rport, {}, true, 8181, proxies, 'username', 'password')

发出 HTTP 请求

尽管我们本文档的主要主题是关于 Rex::Proto::Http::Client, 但它并不知道如何发出 HTTP 尽管我们本文档的主要主题是关于 Rex::Proto::Http::Client, 但它并不知道如何发出 HTTP 请求. 相反, Rex::Proto::Http::ClientRequest 实际上是所有 Metasploit 的 HTTP 请求的母亲. 实际上是所有 Metasploit 的 HTTP 请求的模块.

那么 Rex::Proto::Http::ClientRequest 是如何产生 HTTP 请求的呢? 使用 #request_cgi 或 #request_raw 方法即可使用 Rex::Proto::Http::Client 发起 HTTP 请求. 不同之处在于, 如果使用#request_cgi, 则请求意味着与 CGI 兼容, 并且在大多数情况下, 这就是你想要的. 如果使用#request_raw, 从技术上讲, 这意味着更少的选项, 更少的 CGI 兼容性.

原始 HTTP 请求支持以下选项:

参数名 数据类型 描述
query String 原始 GET 请求参数字符串
data String 原始 POST body 数据字符串
uri String 原始 URI 字符串
ssl Boolean True 则使用 https:// 替代 http://
agent String User-Agent. 默认是 Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)
method String HTTP 请求方法
proto String 协议
version String 版本
vhost String Host 头
port Fixnum Host 头的端口号
authorization String 认证头
cookie String cookie 头
connection String connection 头
headers Hash 自定义 headers 的 Hash, 比 raw_headers 安全
raw_headers String 字符串指定 headers
ctype String Content-type 头

使用 #request_raw 选项的示例:

# cli 是 Rex::Proto::Http::Client 的对象实例
req = cli.request_raw({
	'uri'    =>'/test.php',
	'method' => 'POST',
	'data'   => 'A=B'
})

#request_cgi 继承了上述所有参数, 以及包含下面参数:

参数名 数据类型 描述
pad_get_params Boolean 为 GET 参数启用填充随机字符串
pad_get_params_count Fixnum 随机 GET 参数的数量. 为此, 你还需要 pad_get_params
vars_get Hash Hash 指定 GET 的请求参数
encode_params Boolean 为 GET 或 POST 参数启用 URI 编码
pad_post_params Boolean 为 POST 参数启用填充
pad_post_params_count Fixnum 随机 POST 参数的数量. 为此, 你还需要 pad_post_params

使用 #request_cgi 选项之一的示例:

# cli 是 Rex::Proto::Http::Client 的对象实例
req = cli.request_cgi({
	'uri'      =>'/test.php',
	'vars_get' => {
		'param1' => 'value',
		'param2' => 'value'
	}
})

发送 HTTP 请求

以下是如何使用 #request_cgi 或 #request_raw 与 HTTP 服务器发送请求的示例:

#request_cgi

cli = Rex::Proto::Http::Client.new(rhost),
cli.connect
req = cli.request_cgi({'uri'=>'/'})
res = cli.send_recv(req)
cli.close

#request_raw

cli = Rex::Proto::Http::Client.new(rhost),
cli.connect
req = cli.request_raw({'uri'=>'/'})
res = cli.send_recv(req)
cli.close

配置高级选项

绕 WAF 选项

Rex::Proto::Http::Client 还带有自己的绕 WAF 选项参数. 你可以在请求 Rex::Proto::Http::ClientRequest 发出 HTTP 请求时设置它们, 也可以使用 #set_config 方法设置它们. 主要区别在于, 如果你使用#set_config, 则应使这些选项可由用户配置.

选项名 数据类型 默认值 已知的可配置选项
encode_params Boolean true N/A
encode Boolean false N/A
uri_encode_mode String hex-normal HTTP::uri_encode_mode
uri_encode_count Fixnum 1 N/A
uri_full_url Boolean false HTTP::uri_full_url
pad_method_uri_count Fixnum 1 HTTP::pad_method_uri_count
pad_uri_version_count Fixnum 1 HTTP::pad_uri_version_count
pad_method_uri_type String space HTTP::pad_method_uri_type
pad_uri_version_type String space HTTP::pad_uri_version_type
method_random_valid Boolean false HTTP::method_random_valid
method_random_invalid Boolean false HTTP::method_random_invalid
method_random_case Boolean false HTTP::method_random_case
version_random_valid Boolean false N/A
version_random_invalid Boolean false N/A
version_random_case Boolean false N/A
uri_dir_self_reference Boolean false HTTP::uri_dir_self_reference
uri_dir_fake_relative Boolean false HTTP::uri_dir_fake_relative
uri_use_backslashes Boolean false HTTP::uri_use_backslashes
pad_fake_headers Boolean pad_fake_headers HTTP::pad_fake_headers
pad_fake_headers_count Fixnum 16 HTTP::pad_fake_headers_count
pad_get_params Boolean false HTTP::pad_get_params
pad_get_params_count Boolean 8 HTTP::pad_get_params_count
pad_post_params Boolean false HTTP::pad_post_params
pad_post_params_count Fixnum 8 HTTP::pad_post_params_count
uri_fake_end Boolean false HTTP::uri_fake_end
uri_fake_params_start Boolean false HTTP::uri_fake_params_start
header_folding Boolean false HTTP::header_folding
chunked_size Fixnum 0 N/A

NTLM 参数

HTTP 身份验证在 Rex::Proto::Http::Client 中是自动的, 当涉及到 NTLM 提供程序时, 它有自己的选项. 你必须使用 #set_config 方法来设置:

选项名 数据类型 默认值 已知可配置选项
usentlm2_session Boolean true NTLM::UseNTLM2_session
use_ntlmv2 Boolean true NTLM::UseNTLMv2
send_lm Boolean true NTLM::SendLM
send_ntlm Boolean true NTLM::SendNTLM
SendSPN Boolean true NTLM::SendSPN
UseLMKey Boolean false NTLM::UseLMKey
domain String WORKSTATION DOMAIN
DigestAuthIIS Boolean true DigestAuthIIS

注意: "已知配置选项" 表示 HttpClient 有一个数据存储选项. 如果你不能使用 HttpClient, 那么你将不得不考虑自己注册它们.

URI 解析

Rex::Proto::Http::Client 实际上不支持 URI 解析, 所以对于 URI 格式验证和规范化, 你自己做, 你应该做.

对于 URI 格式验证, 我们建议使用 Ruby 的 URI 模块. 你可以使用 HttpClient 的 #target_uri 方法作为示例.

对于 URI 规范化, 我们推荐 HttpClient 的 #normalize_uri 方法作为示例.

完整示例

cli = Rex::Proto::Http::Client.new(rhost, rport, {}, ssl, ssl_version, proxies, user, pass)
cli.set_config(
  'vhost' => vhost,
  'agent' => datastore['UserAgent'],
  'uri_encode_mode'        => datastore['HTTP::uri_encode_mode'],
  'uri_full_url'           => datastore['HTTP::uri_full_url'],
  'pad_method_uri_count'   => datastore['HTTP::pad_method_uri_count'],
  'pad_uri_version_count'  => datastore['HTTP::pad_uri_version_count'],
  'pad_method_uri_type'    => datastore['HTTP::pad_method_uri_type'],
  'pad_uri_version_type'   => datastore['HTTP::pad_uri_version_type'],
  'method_random_valid'    => datastore['HTTP::method_random_valid'],
  'method_random_invalid'  => datastore['HTTP::method_random_invalid'],
  'method_random_case'     => datastore['HTTP::method_random_case'],
  'uri_dir_self_reference' => datastore['HTTP::uri_dir_self_reference'],
  'uri_dir_fake_relative'  => datastore['HTTP::uri_dir_fake_relative'],
  'uri_use_backslashes'    => datastore['HTTP::uri_use_backslashes'],
  'pad_fake_headers'       => datastore['HTTP::pad_fake_headers'],
  'pad_fake_headers_count' => datastore['HTTP::pad_fake_headers_count'],
  'pad_get_params'         => datastore['HTTP::pad_get_params'],
  'pad_get_params_count'   => datastore['HTTP::pad_get_params_count'],
  'pad_post_params'        => datastore['HTTP::pad_post_params'],
  'pad_post_params_count'  => datastore['HTTP::pad_post_params_count'],
  'uri_fake_end'           => datastore['HTTP::uri_fake_end'],
  'uri_fake_params_start'  => datastore['HTTP::uri_fake_params_start'],
  'header_folding'         => datastore['HTTP::header_folding'],
  'usentlm2_session'       => datastore['NTLM::UseNTLM2_session'],
  'use_ntlmv2'             => datastore['NTLM::UseNTLMv2'],
  'send_lm'                => datastore['NTLM::SendLM'],
  'send_ntlm'              => datastore['NTLM::SendNTLM'],
  'SendSPN'                => datastore['NTLM::SendSPN'],
  'UseLMKey'               => datastore['NTLM::UseLMKey'],
  'domain'                 => datastore['DOMAIN'],
  'DigestAuthIIS'          => datastore['DigestAuthIIS']
)
cli.connect
req = cli.request_cgi({'uri'=>'/'})
res = cli.send_recv(req)
cli.close
Clone this wiki locally