Skip to content

Commit 50b2ae4

Browse files
committed
Add a plugin for making curl-like http requests
1 parent fc61f1a commit 50b2ae4

File tree

1 file changed

+202
-0
lines changed

1 file changed

+202
-0
lines changed

plugins/requests.rb

Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
require 'uri'
2+
3+
module Msf
4+
5+
class Plugin::HTTPRequest < Msf::Plugin
6+
7+
class ConsoleCommandDispatcher
8+
include Msf::Ui::Console::CommandDispatcher
9+
10+
def name
11+
'Request'
12+
end
13+
14+
def commands
15+
{
16+
'http_request' => 'Make an HTTP request'
17+
}
18+
end
19+
20+
def parse_args(args)
21+
help_line = 'Usage: http_request [options] uri'
22+
opt_parser = Rex::Parser::Arguments.new(
23+
'-0' => [ false, 'Use HTTP 1.0' ],
24+
'-1' => [ false, 'Use TLSv1 (SSL)' ],
25+
'-2' => [ false, 'Use SSLv2 (SSL)' ],
26+
'-3' => [ false, 'Use SSLv3 (SSL)' ],
27+
'-A' => [ true, 'User-Agent to send to server' ],
28+
'-d' => [ true, 'HTTP POST data' ],
29+
'-G' => [ false, 'Send the -d data with a HTTP GET' ],
30+
'-h' => [ false, 'This help text' ],
31+
'-H' => [ true, 'Custom header to pass to server' ],
32+
'-i' => [ false, 'Include headers in the output' ],
33+
'-I' => [ false, 'Show document info only' ],
34+
'-o' => [ true, 'Write output to <file> instead of stdout' ],
35+
'-u' => [ true, 'Server user and password', ],
36+
'-X' => [ true, 'Request method to use' ]
37+
)
38+
39+
options = {
40+
:auth_password => nil,
41+
:auth_username => nil,
42+
:headers => { },
43+
:print_body => true,
44+
:print_headers => false,
45+
:method => nil,
46+
:output_file => nil,
47+
:ssl_version => 'Auto',
48+
:uri => nil,
49+
:user_agent => Rex::Proto::Http::Client::DefaultUserAgent,
50+
:version => '1.1'
51+
}
52+
53+
opt_parser.parse(args) do |opt, idx, val|
54+
case opt
55+
when '-0'
56+
options[:version] = '1.0'
57+
when '-1'
58+
options[:ssl_version] = 'TLS1'
59+
when '-2'
60+
options[:ssl_version] = 'SSL2'
61+
when '-3'
62+
options[:ssl_version] = 'SSL3'
63+
when '-A'
64+
options[:user_agent] = val
65+
when '-d'
66+
options[:data] = val
67+
options[:method] ||= 'POST'
68+
when '-G'
69+
options[:method] = 'GET'
70+
when '-h'
71+
print_line(help_line)
72+
print_line(opt_parser.usage)
73+
return
74+
when '-H'
75+
name, _, value = val.partition(':')
76+
options[:headers][name] = value.strip
77+
when '-i'
78+
options[:print_headers] = true
79+
when '-I'
80+
options[:print_headers] = true
81+
options[:print_body] = false
82+
options[:method] ||= 'HEAD'
83+
when '-o'
84+
options[:output_file] = val
85+
when '-u'
86+
val = val.partition(':')
87+
options[:auth_username] = val[0]
88+
options[:auth_password] = val[2]
89+
when '-X'
90+
options[:method] = val
91+
else
92+
options[:uri] = val
93+
end
94+
end
95+
96+
if options[:uri].nil?
97+
print_line(help_line)
98+
print_line(opt_parser.usage)
99+
return
100+
end
101+
102+
options[:method] ||= 'GET'
103+
options[:uri] = URI(options[:uri])
104+
options
105+
end
106+
107+
def output_line(opts, line)
108+
if opts[:output_file].nil?
109+
if line[-2..-1] == "\r\n"
110+
print_line(line[0..-3])
111+
elsif line[-1] == "\n"
112+
print_line(line[0..-2])
113+
else
114+
print_line(line)
115+
end
116+
else
117+
opts[:output_file].write(line)
118+
end
119+
end
120+
121+
def cmd_http_request(*args)
122+
opts = parse_args(args)
123+
return unless opts
124+
125+
unless opts[:output_file].nil?
126+
begin
127+
opts[:output_file] = File.new(opts[:output_file], 'w')
128+
rescue ::Errno::EACCES, Errno::EISDIR, Errno::ENOTDIR
129+
print_error('Failed to open the specified file for output')
130+
return
131+
end
132+
end
133+
134+
uri = opts[:uri]
135+
http_client = Rex::Proto::Http::Client.new(
136+
uri.host,
137+
uri.port,
138+
{'Msf' => framework},
139+
uri.scheme == 'https',
140+
opts[:ssl_version]
141+
)
142+
143+
begin
144+
http_client.connect
145+
request = http_client.request_cgi(
146+
'agent' => opts[:user_agent],
147+
'data' => opts[:data],
148+
'headers' => opts[:headers],
149+
'method' => opts[:method],
150+
'password' => opts[:auth_password],
151+
'query' => uri.query,
152+
'uri' => uri.path,
153+
'username' => opts[:auth_username],
154+
'version' => opts[:version]
155+
)
156+
157+
response = http_client.send_recv(request)
158+
rescue ::OpenSSL::SSL::SSLError
159+
print_error('Encountered an SSL error')
160+
rescue ::Errno::ECONNRESET => ex
161+
print_error('The connection was reset by the peer')
162+
rescue ::EOFError, Errno::ETIMEDOUT, Rex::ConnectionError, ::Timeout::Error
163+
print_error('Encountered an error')
164+
rescue ::Exception => ex
165+
print_line("An error of type #{ex.class} happened, message is #{ex.message}")
166+
ensure
167+
http_client.close
168+
end
169+
return unless response
170+
171+
if opts[:print_headers]
172+
output_line(opts, response.cmd_string)
173+
output_line(opts, response.headers.to_s)
174+
end
175+
176+
output_line(opts, response.body) if opts[:print_body]
177+
opts[:output_file].close unless opts[:output_file].nil?
178+
end
179+
end
180+
181+
def initialize(framework, opts)
182+
super
183+
add_console_dispatcher(ConsoleCommandDispatcher)
184+
print_status("#{name} plugin loaded.")
185+
end
186+
187+
def cleanup
188+
remove_console_dispatcher('HTTP Request')
189+
end
190+
191+
def name
192+
'Requests'
193+
end
194+
195+
def desc
196+
'Make Requests'
197+
end
198+
199+
protected
200+
end
201+
202+
end

0 commit comments

Comments
 (0)