Skip to content

Commit 6546d30

Browse files
David MaloneyDavid Maloney
authored andcommitted
Land rapid7#5004, Http Login Refactor
Land Wei's PR to refactor the http login scanner moving the send request code into it's own method
2 parents 376bf13 + 6e3e696 commit 6546d30

File tree

4 files changed

+78
-65
lines changed

4 files changed

+78
-65
lines changed

lib/metasploit/framework/login_scanner/http.rb

Lines changed: 56 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -187,13 +187,66 @@ def check_setup
187187
error_message
188188
end
189189

190+
# Sends a HTTP request with Rex
191+
#
192+
# @param [Hash] Native support includes the following (also see Rex::Proto::Http::Request#request_cgi)
193+
# @option opts[String] 'host' The remote host
194+
# @option opts[Fixnum] 'port' The remote port
195+
# @option opts[Boolean] 'ssl' The SSL setting, TrueClass or FalseClass
196+
# @option opts[String] 'proxies' The proxies setting
197+
# @option opts[Credential] 'credential' A credential object
198+
# @option opts['Hash'] 'context' A context
199+
# @raise [Rex::ConnectionError] One of these errors has occured: EOFError, Errno::ETIMEDOUT, Rex::ConnectionError, ::Timeout::Error
200+
# @return [Rex::Proto::Http::Response] The HTTP response
201+
# @return [NilClass] An error has occured while reading the response (see #Rex::Proto::Http::Client#read_response)
202+
def send_request(opts)
203+
rhost = opts['host'] || host
204+
rport = opts['rport'] || port
205+
cli_ssl = opts['ssl'] || ssl
206+
cli_ssl_version = opts['ssl_version'] || ssl_version
207+
cli_proxies = opts['proxies'] || proxies
208+
username = opts['credential'] ? opts['credential'].public : ''
209+
password = opts['credential'] ? opts['credential'].private : ''
210+
realm = opts['credential'] ? opts['credential'].realm : nil
211+
context = opts['context'] || { 'Msf' => framework, 'MsfExploit' => framework_module}
212+
213+
res = nil
214+
cli = Rex::Proto::Http::Client.new(
215+
rhost,
216+
rport,
217+
context,
218+
cli_ssl,
219+
cli_ssl_version,
220+
cli_proxies,
221+
username,
222+
password
223+
)
224+
configure_http_client(cli)
225+
226+
if realm
227+
cli.set_config('domain' => credential.realm)
228+
end
229+
230+
begin
231+
cli.connect
232+
req = cli.request_cgi(opts)
233+
res = cli.send_recv(req)
234+
rescue ::EOFError, Errno::ETIMEDOUT, Rex::ConnectionError, ::Timeout::Error => e
235+
raise Rex::ConnectionError, e.message
236+
ensure
237+
cli.close
238+
end
239+
240+
res
241+
end
242+
243+
190244
# Attempt a single login with a single credential against the target.
191245
#
192246
# @param credential [Credential] The credential object to attempt to
193247
# login with.
194248
# @return [Result] A Result object indicating success or failure
195249
def attempt_login(credential)
196-
197250
result_opts = {
198251
credential: credential,
199252
status: Metasploit::Model::Login::Status::INCORRECT,
@@ -209,32 +262,13 @@ def attempt_login(credential)
209262
result_opts[:service_name] = 'http'
210263
end
211264

212-
http_client = Rex::Proto::Http::Client.new(
213-
host, port, {'Msf' => framework, 'MsfExploit' => framework_module}, ssl, ssl_version,
214-
proxies, credential.public, credential.private
215-
)
216-
217-
configure_http_client(http_client)
218-
219-
if credential.realm
220-
http_client.set_config('domain' => credential.realm)
221-
end
222-
223265
begin
224-
http_client.connect
225-
request = http_client.request_cgi(
226-
'uri' => uri,
227-
'method' => method
228-
)
229-
230-
response = http_client.send_recv(request)
266+
response = send_request('credential'=>credential, 'uri'=>uri, 'method'=>method)
231267
if response && response.code == 200
232268
result_opts.merge!(status: Metasploit::Model::Login::Status::SUCCESSFUL, proof: response.headers)
233269
end
234-
rescue ::EOFError, Errno::ETIMEDOUT, Rex::ConnectionError, ::Timeout::Error => e
270+
rescue Rex::ConnectionError => e
235271
result_opts.merge!(status: Metasploit::Model::Login::Status::UNABLE_TO_CONNECT, proof: e)
236-
ensure
237-
http_client.close
238272
end
239273

240274
Result.new(result_opts)

lib/metasploit/framework/login_scanner/symantec_web_gateway.rb

Lines changed: 0 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -27,41 +27,6 @@ def check_setup
2727
end
2828

2929

30-
# Sends a HTTP request with Rex
31-
#
32-
# @param (see Rex::Proto::Http::Request#request_raw)
33-
# @raise [Rex::ConnectionError] Something has gone wrong while sending the HTTP request
34-
# @return [Rex::Proto::Http::Response] The HTTP response
35-
def send_request(opts)
36-
res = nil
37-
cli = Rex::Proto::Http::Client.new(host, port,
38-
{
39-
'Msf' => framework,
40-
'MsfExploit' => framework_module
41-
},
42-
ssl,
43-
ssl_version,
44-
proxies
45-
)
46-
configure_http_client(cli)
47-
begin
48-
cli.connect
49-
req = cli.request_cgi(opts)
50-
res = cli.send_recv(req)
51-
rescue ::Errno::EPIPE, ::Timeout::Error => e
52-
# We are trying to mimic the same type of exception rescuing in
53-
# Msf::Exploit::Remote::HttpClient. But instead of returning nil, we'll consistently
54-
# raise Rex::ConnectionError so the #attempt_login can return the error message back
55-
# to the login module.
56-
raise Rex::ConnectionError, e.message
57-
ensure
58-
cli.close
59-
end
60-
61-
res
62-
end
63-
64-
6530
# Returns the latest sid from Symantec Web Gateway.
6631
#
6732
# @returns [String] The PHP Session ID for Symantec Web Gateway login

spec/lib/metasploit/framework/login_scanner/http_spec.rb

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,26 @@
88
it_behaves_like 'Metasploit::Framework::LoginScanner::RexSocket'
99
it_behaves_like 'Metasploit::Framework::LoginScanner::HTTP'
1010

11+
subject do
12+
described_class.new
13+
end
14+
15+
let(:response) { Rex::Proto::Http::Response.new(200, 'OK') }
16+
17+
before(:each) do
18+
allow_any_instance_of(Rex::Proto::Http::Client).to receive(:request_cgi).with(any_args)
19+
allow_any_instance_of(Rex::Proto::Http::Client).to receive(:send_recv).with(any_args).and_return(response)
20+
allow_any_instance_of(Rex::Proto::Http::Client).to receive(:set_config).with(any_args)
21+
allow_any_instance_of(Rex::Proto::Http::Client).to receive(:close)
22+
allow_any_instance_of(Rex::Proto::Http::Client).to receive(:connect)
23+
end
24+
25+
describe '#send_request' do
26+
context 'when a valid request is sent' do
27+
it 'returns a response object' do
28+
expect(subject.send_request({'uri'=>'/'})).to be_kind_of(Rex::Proto::Http::Response)
29+
end
30+
end
31+
end
32+
1133
end

spec/lib/metasploit/framework/login_scanner/symantec_web_gateway_spec.rb

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -72,14 +72,6 @@
7272
end
7373
end
7474

75-
describe '#send_request' do
76-
context 'when a valid request is sent' do
77-
it 'returns a response object' do
78-
expect(subject.send_request({'uri'=>'/'})).to be_kind_of(Rex::Proto::Http::Response)
79-
end
80-
end
81-
end
82-
8375
describe '#get_last_sid' do
8476
let(:response) do
8577
res = Rex::Proto::Http::Response.new(200, 'OK')

0 commit comments

Comments
 (0)