-
Notifications
You must be signed in to change notification settings - Fork 3
使用 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 初始化程序创建一个新的 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')
尽管我们本文档的主要主题是关于 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'
}
})
以下是如何使用 #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, 那么你将不得不考虑自己注册它们.
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