Security assessment · Web + API security

VulnBank

A banking simulation tested for weaknesses in authentication, authorization, sessions and transaction logic.

Lab assessment · Simulated environment

Assessment overview

Testing question

Can account or transaction rules be bypassed?

Approach

I tested authentication, sessions and transaction workflows through the web interface and API.

Results

26 test cases: 21 FAIL and 5 PASS, with 81 screenshots. These are lab results, not production metrics. The counts describe test cases, not distinct controls.

Security assessment / Report

Penetration testing report

View the HTML report or open an available original PDF below. Each PDF link shows its language.

Evidence / Test-case index

Browse test cases

PASS means the application behaved as expected in that test. FAIL means the test confirmed a security weakness. Counts refer to test cases, not distinct controls.

01
Account Takeover - Password Reset - Reset PIN DisclosureThe /api/v1/forgot-password endpoint returns the victim's reset PIN directly in the HTTP response body in the debug_info.pin field. This PIN should only be delivered to a channel owned by the victim (email). Anyone …
FAIL
02
Active Debug Code - Application Server - Werkzeug DebuggerThe application runs on the Flask development server in debug mode (app.run(debug=True)), so the interactive Werkzeug console is served without authentication at /console. That page also exposes the debugger's SECRE…
FAIL
03
Broken Object Level Authorization - Accounts - Balance LookupThe /check_balance/<account_number> endpoint returns an account balance based on the account number in the path without checking that the account belongs to the token holder. A valid token is enough to read any acco…
FAIL
04
Broken Object Level Authorization - Merchant Payments - Payments By MerchantThe /api/v1/payments/merchant_id/<merchant_id> endpoint returns a list of payments based on the merchant_id in the path without checking that the caller's API key actually belongs to that merchant. The check only co…
FAIL
05
Broken Object Level Authorization - Users - User Detail APIThe /api/v3/user/<user_id> endpoint returns a user profile based on the user_id in the path without checking that the profile belongs to the token holder or that the caller is an administrator. user_id is a sequenti…
FAIL
06
Broken Object Level Authorization - Virtual Cards - Card OperationsThe virtual card endpoints accept card_id from the URL path and operate on it without verifying that the card belongs to the token holder. Card identifiers are sequential integers and therefore easy to enumerate.
FAIL
07
Excessive Data Exposure - GraphQL - Schema IntrospectionThe /graphql endpoint serves __schema introspection queries to ordinary authenticated users. Every type, field, and argument in the schema can be downloaded, including fields not used by the official interface.
FAIL
08
Insufficient Session Expiration - Authentication - JWT LifetimeIssued JWT tokens contain no exp claim, so they never expire, and there is no token revocation mechanism. The token is also accepted from several locations at once, including URL query parameters, and in the web int…
FAIL
09
JWT Signature Not Verified - Authentication - Token ValidationThe verify_token function in auth.py catches InvalidSignatureError and then retries the decode with options={'verify_signature': False}, returning the payload. As a result, tokens with an invalid signature are still…
FAIL
10
Mass Assignment - Registration - Privilege FieldsThe /register endpoint copies every key in the JSON request into the INSERT statement without a column allow-list. Sensitive fields such as is_admin and balance, which should be determined by the server, can be sent…
FAIL
11
Race Condition - Transfer - Balance CheckThe /transfer endpoint reads the sender's balance, checks that funds are sufficient, then writes the new balance as a separate operation with no atomic database transaction and no row locking. Ten concurrently sent …
FAIL
12
Security Misconfiguration - CORS - Wildcard OriginThe application sends an Access-Control-Allow-Origin header with the wildcard value (*) on every response, including responses from endpoints that return sensitive data. This policy removes the same-origin protectio…
FAIL
13
Sensitive Data Exposure - Debug Endpoint - User CredentialsThe /debug/users endpoint is reachable without authentication and returns the entire users table, including the password column. Passwords are stored in plaintext rather than hashed, so they can be used immediately.…
FAIL
14
Sensitive Data Exposure - Web Root - Environment FilesThe environment configuration file .env and its backup copy .env.bak are served directly from the web root without authentication. These files contain the application secret key, JWT secret, and database credentials…
FAIL
15
Server-Side Request Forgery - Profile Picture - Import By URLThe /upload_profile_picture_url endpoint accepts a full URL from the client and performs a server-side requests.get without a scheme or host allow-list, follows redirects, and does not validate content-type. The ups…
FAIL
16
SQL Injection - Authentication - Login BypassThe /login endpoint builds its SQL query with an f-string that interpolates the raw username and password without parameterization: SELECT * FROM users WHERE username='{username}' AND password='{password}'. An attac…
FAIL
17
SQL Injection - GraphQL - Transaction Summary ResolverThe transactionSummary resolver passes the accountNumber argument to the _load_actor_by_account_number function, which builds a query with an f-string: SELECT id, username, account_number, is_admin FROM users WHERE …
FAIL
18
Stored Cross-Site Scripting - User Profile - Bio FieldThe /update_bio endpoint stores bio content without any sanitization. The administrative panel then injects that value into the DOM through innerHTML in a template literal without escaping (templates/admin.html), wh…
FAIL
19
Unrestricted File Upload - Profile Picture - File Type ValidationThe /upload_profile_picture endpoint stores any file without validating extension, content-type, or magic bytes, and without a size limit. Files are stored in static/uploads, which the server serves directly, and Fl…
FAIL
20
Unrestricted Resource Consumption - Authentication - Login Rate LimitingThe /login endpoint applies no rate limiting, account lockout, progressive delay, or CAPTCHA. Consecutive failed login attempts are all served at full speed. Failure responses also differ by condition, making userna…
FAIL
21
Unrestricted Resource Consumption - Password Reset - PIN Rate LimitingThe password reset endpoint applies no rate limiting, account lockout, or limit on the number of PIN attempts. The recovery PIN is only 3 digits, so the guessing space is just 1000 possibilities and can be exhausted…
FAIL
22
Broken Function Level Authorization - Admin - Administrative EndpointsThe test attempted to call administrative functions directly with an ordinary user token, bypassing the admin panel interface. Tested endpoints included creating a new administrator, deleting accounts, and suspendin…
PASS
23
Broken Object Level Authorization - GraphQL - Transaction SummaryThe test attempted to read another user's transaction summary through the accountNumber argument of the GraphQL transactionSummary resolver, including the victim's account and the administrator's account.
PASS
24
Broken Object Level Authorization - Merchant Payments - Payment By IDThe test attempted to read the details of another merchant's transaction through /api/v1/payments/<payment_id> using a different merchant's API key.
PASS
25
JWT Algorithm Confusion - Authentication - alg noneThe test attempted to bypass JWT signature verification by changing the algorithm header to none and emptying the signature section, a classic token forgery technique. The source code lists none among the accepted a…
PASS
26
Path Traversal - Profile Picture - Upload FilenameThe test attempted to write a file outside the upload directory by inserting ../ sequences into the client-supplied filename. A comment in the source code states that path traversal is possible, so this endpoint was…
PASS