Skip to content

Commit f4a1e98

Browse files
committed
Add gitlab login scanner
1 parent 84df403 commit f4a1e98

File tree

3 files changed

+200
-0
lines changed

3 files changed

+200
-0
lines changed
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
require 'metasploit/framework/login_scanner/http'
2+
3+
module Metasploit
4+
module Framework
5+
module LoginScanner
6+
7+
# Gitlab login scanner
8+
class Gitlab < HTTP
9+
10+
# Inherit LIKELY_PORTS,LIKELY_SERVICE_NAMES, and REALM_KEY from HTTP
11+
CAN_GET_SESSION = false
12+
DEFAULT_PORT = 80
13+
PRIVATE_TYPES = [ :password ]
14+
15+
# (see Base#set_sane_defaults)
16+
def set_sane_defaults
17+
self.uri = '/users/sign_in' if self.uri.nil?
18+
self.method = 'POST' if self.method.nil
19+
20+
super
21+
end
22+
23+
def attempt_login(credential)
24+
result_opts = {
25+
credential: credential,
26+
host: host,
27+
port: port,
28+
protocol: 'tcp',
29+
service_name: ssl ? 'https' : 'http'
30+
}
31+
begin
32+
cli = Rex::Proto::Http::Client.new(host,
33+
port,
34+
{
35+
'Msf' => framework,
36+
'MsfExploit' => framework_module
37+
},
38+
ssl,
39+
ssl_version,
40+
proxies)
41+
configure_http_client(cli)
42+
cli.connect
43+
44+
# Get a valid session cookie and authenticity_token for the next step
45+
req = cli.request_cgi({
46+
'method' => 'GET',
47+
'cookie' => 'request_method=GET',
48+
'uri' => self.uri
49+
})
50+
51+
res = cli.send_recv(req)
52+
53+
if res.body.include? 'user[email]'
54+
user_field = 'user[email]'
55+
elsif res.body.include? 'user[login]'
56+
user_field = 'user[login]'
57+
else
58+
raise RuntimeError, 'Not a valid Gitlab login page'
59+
end
60+
61+
local_session_cookie = res.get_cookies.scan(/(_gitlab_session=[A-Za-z0-9%-]+)/).flatten[0]
62+
auth_token = res.body.scan(/<input name="authenticity_token" type="hidden" value="(.*?)"/).flatten[0]
63+
64+
raise RuntimeError, 'Unable to get Session Cookie' unless local_session_cookie
65+
raise RuntimeError, 'Unable to get Authentication Token' unless auth_token
66+
67+
# Perform the actual login
68+
req = cli.request_cgi({
69+
'method' => 'POST',
70+
'cookie' => local_session_cookie,
71+
'uri' => self.uri,
72+
'vars_post' =>
73+
{
74+
'utf8' => "\xE2\x9C\x93",
75+
'authenticity_token' => auth_token,
76+
"#{user_field}" => credential.public,
77+
'user[password]' => credential.private,
78+
'user[remember_me]' => 0
79+
}
80+
})
81+
82+
res = cli.send_recv(req)
83+
if res && res.code == 302
84+
result_opts.merge!(status: Metasploit::Model::Login::Status::SUCCESSFUL, proof: res.headers)
85+
else
86+
result_opts.merge!(status: Metasploit::Model::Login::Status::INCORRECT, proof: res)
87+
end
88+
rescue ::EOFError, Errno::ETIMEDOUT, Rex::ConnectionError, ::Timeout::Error => e
89+
result_opts.merge!(status: Metasploit::Model::Login::Status::UNABLE_TO_CONNECT, proof: e)
90+
ensure
91+
cli.close
92+
end
93+
Result.new(result_opts)
94+
end
95+
end
96+
end
97+
end
98+
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)