July 23, 2026
Breaking VulnBank: A complete web application penetration testing walkthrough
Introduction

By Akwaeze Odera Gerald
58 min read
Introduction
Modern banking platforms rely heavily on web/mobile applications to handle financial transactions, manage sensitive user data, and provide digital services to customers. While these systems offer convenience and accessibility, they also introduce significant security risks if not designed and implemented securely.
In this walkthrough, I conduct a full penetration testing assessment of the intentionally vulnerable banking platform VulnBank. The goal of this exercise is to simulate how an attacker would approach a web application, identify weaknesses, and demonstrate how those vulnerabilities could be exploited.
Rather than simply listing discovered vulnerabilities, this article documents the entire attacker thought process, from reconnaissance to exploitation and finally remediation. Each vulnerability will be explained clearly so that both technical and non-technical readers can understand how the issue occurs and why it matters.
During this assessment, over 40 security weaknesses were identified across authentication, authorization, business logic, GraphQL, file handling, virtual cards, and payment workflows. Several issues were critical in severity, including Broken Object Level Authorization (BOLA), race conditions, weak JWT implementation, mass assignment, unrestricted file upload, GraphQL misconfigurations, and business logic flaws capable of resulting in unauthorized financial operations.
Throughout the assessment, I will demonstrate:
- How attackers map and analyze an application.
- How vulnerabilities are discovered and validated.
- How exploitation can impact financial systems.
- How these vulnerabilities should be properly mitigated.
Disclaimer: All testing documented in this article was performed in a controlled lab environment using VulnBank, an intentionally vulnerable web application created by Al-Amir Badmus for educational purposes. No testing was conducted against production systems or applications without explicit authorization. The purpose of this assessment is to demonstrate common web application security vulnerabilities, their potential business impact, and recommended remediation techniques.
Link: vulnbank.org
Reconnaissance & Attack Surface Mapping
Reconnaissance is the first phase of any penetration test. Before attempting to exploit a system, attackers must first understand how the application works, what features it provides, and where potential weaknesses might exist.
This phase focuses on observing the application from the outside, just as a real attacker would. The goal is to identify the application's functionality, locate backend endpoints, and determine which parts of the system handle sensitive data.
Even simple interactions with a website can reveal valuable information about how the system operates internally.
Initial Application Exploration
The first step was interacting with the application through a browser to identify its core features.
The VulnBank web interface includes several common banking functionalities such as:
- User registration
- User login
- Viewing account balances
- Transferring funds
- Loan services
- Virtual card management
- Bill payments
- Profile management
- AI-powered customer support
Because these features handle financial data and sensitive user information, they represent important potential attack surfaces.
Traffic Interception
To understand how the application communicates with the backend, all browser traffic was routed through an intercepting proxy using Burp Suite.
Intercepting requests allows testers to:
- Observe HTTP requests and responses.
- Identify hidden API endpoints.
- Manipulate request parameters.
- Test input validation mechanisms.
By examining these requests, it becomes possible to see how user data flows through the system and where potential vulnerabilities may exist.
Attack Surface Mapping
After interacting with different components of the application, multiple high-risk attack surfaces were identified. These areas process user-controlled input and form the core entry points for exploitation:
- Authentication & Session Management System: Handles login, JWT issuance, password reset mechanisms, and session handling. Weak implementations here introduce risks such as token tampering, session hijacking, and account takeover.
- Account Management APIs: Includes registration and user profile functionalities. Improper handling of input parameters exposes the application to mass assignment, excessive data exposure, and privilege escalation.
- Transaction Processing Endpoints: Responsible for money transfers and balance updates. These endpoints lack proper validation, making them vulnerable to manipulation such as negative transfers, race conditions, and unauthorized transactions.
- GraphQL API Endpoint (/graphql): A critical attack surface that exposes internal schema through introspection. It allows structured queries to access transaction analytics and financial data. Weak authorization combined with schema exposure significantly increases the risk of data leakage and abuse.
- File Upload & Profile Image Handling: Includes both direct file upload and URL-based image import. This functionality introduces risks such as unrestricted file upload, path traversal, and Server-Side Request Forgery (SSRF).
- Virtual Card Management System: Handles creation and management of virtual cards. Vulnerabilities here include predictable card generation, sensitive data exposure, and broken authorization allowing access to other users' card details.
- Bill Payment APIs: Processes bill transactions and payment history. Weak validation and authorization flaws expose this component to manipulation, information disclosure, and race condition attacks.
Each of these components accepts and processes user-supplied input. Due to insufficient validation, weak authorization controls, and insecure design decisions, they collectively expand the application's attack surface significantly.
With the application structure fully mapped and key entry points identified, the next step was to analyze the authentication and session management mechanisms, as they form the foundation for access control across the entire system.
Authentication and Session Management Mechanisms
What Is Authentication?
Authentication is the process of verifying the identity of a user. It ensures that only legitimate users can access protected resources within an application.
Most web applications implement authentication using credentials such as usernames and passwords. Once authenticated, the server typically issues a token (e.g., JWT) or session identifier that allows the user to maintain access.
If authentication mechanisms are poorly implemented, attackers may:
- Bypass login systems.
- Impersonate legitimate users.
- Gain unauthorized access to sensitive data.
Given its critical role, authentication was one of the first components analyzed.
SQL Injection in Login
Severity: Critical
Description
The login functionality was tested to evaluate how user-supplied credentials are processed by the backend system.
During testing, it was observed that the application does not properly sanitize input from the username field before incorporating it into backend SQL queries. This introduces an SQL Injection vulnerability, allowing attackers to manipulate authentication logic.
Exploitation
A crafted SQL injection payload was inserted into the username field:
' OR 1=1 —
This payload works by:
- Terminating the intended SQL query context (')
- Injecting a condition that always evaluates to true (OR 1=1)
- Commenting out the remainder of the query ( — )
Observed Behavior
- The application accepted the malicious payload without proper validation.
- Authentication controls were bypassed successfully.
- The system logged in as a valid user (|id) without requiring correct credentials.
- However, administrative privileges were NOT granted.
The response confirms isAdmin: false, indicating privilege escalation is no longer possible via this vector.
Impact:
- Authentication bypass allows unauthorized access to user accounts.
- Attackers can impersonate legitimate users.
- Exposure of sensitive user data (e.g., account numbers, session tokens).
- While admin access is not directly achievable, this vulnerability could be combined with other flaws for further exploitation.
Remediation:
- Use parameterized queries (prepared statements) to prevent SQL injection.
- Implement an ORM (Object Relational Mapping) framework to abstract database queries.
- Enforce strict input validation and sanitization on all user inputs.
- Apply least privilege principles to limit database query impact.
- Deploy a web application firewall (WAF) to detect and block injection attempts.
Interestingly, earlier versions of this vulnerability may have allowed privilege escalation, but current testing shows the impact is limited to authentication bypass.
It is worth noting that while SQL injection successfully bypasses authentication, administrative access was not achieved during testing. This may indicate changes in the application's backend data (e.g., absence of administrative user records) rather than proper remediation of the underlying vulnerability.
The presence of SQL injection in an authentication mechanism remains a critical security flaw and highlights insufficient input handling on the server side.
Weak Password Reset Mechanism (3-Digit PIN Disclosure)
Severity: Critical
Description
The password reset functionality was tested across different API versions.
While the newer endpoint (/api/v3/forgot-password) returned a generic response,
I tried to check if older version exits (/api/v1/forgot-password) and it exposed sensitive debug information.
Upon analyzing the server's response, it was observed that the application returned a successful password reset message, indicating that a reset PIN had been sent.
However, the response also included a debug_info object, which exposed sensitive internal data. Most notably, the actual reset PIN was directly disclosed in the response alongside the username and timestamp.
This behavior indicates that the application is improperly exposing sensitive debugging information in production responses. Instead of securely sending the reset PIN through a protected channel (e.g., email, SMS), the system reveals it directly to the client.
As a result, an attacker can simply initiate a password reset for any known username and immediately retrieve the valid reset PIN from the response, effectively bypassing the entire verification process.
Observed Behavior
- The server returns a 200 OK response with a success message.
- A debug_info object is included in the response.
- The reset PIN is exposed in plaintext within this object.
- The backend generates a 3-digit PIN, as shown in the response ("pin_length": 3).
Although the user interface expects a four-digit reset PIN, the backend response disclosed three digits of the PIN directly to the client. This reduced the search space from 10,000 possible combinations to only 10, making brute-force completion trivial.
This vulnerability introduces several critical weaknesses in the password reset process.
Direct PIN Disclosure
The password reset PIN is partially disclosed in the server response through exposed debugging information. Password reset secrets should never be returned to the client, regardless of whether they are fully or partially revealed.
By exposing reset-related information directly in the response, the application significantly weakens the security of the account recovery process and reduces the effectiveness of the verification mechanism.
Weak PIN Entropy
The application generates a short numeric PIN for password reset verification.
Based on testing, the reset process relies on a low-entropy numeric value that provides limited resistance against guessing attacks. Combined with the disclosure of reset information in the API response, the effective search space is drastically reduced.
Frontend-Backend Validation Mismatch
Testing revealed an inconsistency between the frontend and backend implementations.
The user interface requires a four-digit PIN during the reset process, while the backend exposes only a three-digit value through debugging information.
This discrepancy indicates a lack of consistency between frontend and backend validation logic and may allow attackers to bypass intended client-side restrictions through direct API interaction.
Information Disclosure Through Debug Functionality
The API response exposes internal debugging data that should not be accessible in a production environment.
In addition to the reset PIN information, the response reveals implementation details related to the password reset workflow, providing attackers with valuable insight into backend behavior.
Potential Brute-Force Facilitation
Because most of the reset secret is disclosed, the remaining search space becomes extremely small.
An attacker who obtains the exposed PIN information may only need to determine a minimal number of remaining values to successfully complete the password reset process.
Impact
This vulnerability may allow attackers to:
- Significantly weaken the password reset process.
- Recover or reset user passwords without access to the victim's email.
- Bypass intended verification controls.
- Obtain sensitive internal application information.
- Target and compromise arbitrary user accounts.
- Increase the effectiveness of brute-force attacks against the password reset mechanism.
Because password reset functionality directly protects user credentials, successful exploitation can ultimately result in unauthorized account access and account takeover.
Remediation
- Remove all debugging information from production API responses.
- Never expose reset PINs, tokens, or verification secrets to clients.
- Use cryptographically secure, high-entropy reset tokens instead of short numeric PINs.
- Ensure frontend and backend validation logic are consistent.
- Implement rate limiting and brute-force protections on password reset endpoints.
- Expire reset codes after a short period of time.
- Require secure out-of-band verification mechanisms such as email-based reset links.
- Log and monitor suspicious password reset activity.
Weak JWT Implementation & Session Management Failures
Severity: Critical
Description
JSON Web Tokens (JWTs) are used by the application to maintain authenticated user sessions and enforce access control. JWTs are intended to provide a secure mechanism for transmitting identity and authorization information between the client and server.
During testing, multiple weaknesses were identified in the application's JWT implementation and session management design, including;
- Storage of JWTs in browser localStorage.
- Exposure of tokens to client-side JavaScript.
- User-controlled authorization claims within the token payload.
- Acceptance of manipulated token payloads.
- Lack of secure session management controls.
- Absence of effective token revocation mechanisms.
Collectively, these issues significantly weaken both authentication and authorization controls and increase the impact of client-side vulnerabilities such as Cross-Site Scripting (XSS).
Weak JWT Implementation & Session Management
How It Was Discovered
During authenticated testing, the application was observed to use JSON Web Tokens (JWTs) as the sole authentication mechanism. After logging into the application, the issued JWT was extracted and decoded for analysis.
Further inspection of the application revealed several weaknesses in how authentication tokens were generated, stored, and managed throughout the application lifecycle.
The following areas were examined:
- JWT implementation and signing.
- Client-side token storage.
- Token lifetime and expiration.
- Session invalidation after logout or account changes.
- Server-side validation of authentication state.
- Authorization claims embedded within the JWT payload.
Exploitation
Weak JWT Implementation
Analysis of the issued tokens revealed that the application relied heavily on JWTs for authentication while embedding authorization-related information directly within the token payload.
Sensitive attributes such as the authenticated user's identifier and administrative status were included inside the JWT and subsequently trusted by the application during authenticated requests.
Weak JWT Signing Secret
The issued JWT was also analyzed using xJWT.io to evaluate the strength of the signing secret.
The analysis successfully recovered the JWT signing key within seconds.
The recovered signing secret was, secret123
This demonstrates that the application relies on a weak, easily guessable signing key rather than a cryptographically strong secret.
With knowledge of the signing key, an attacker can generate their own valid JWTs, modify token contents, and produce signatures that the server will accept as authentic if signature verification is based on the compromised secret.
JWT Forgery
After recovering the signing secret, it became possible to generate new JWTs containing arbitrary payload values while producing valid signatures.
For example, the original payload,
{
"user_id": 28,
"username": "Gerald",
"is_admin": false
}
could be modified and re-signed using the recovered secret.
Because the signing secret had been compromised, the integrity guarantees normally provided by JWTs no longer existed. Any attacker possessing the secret could create tokens for arbitrary users or manipulate authorization-related claims if the application relied on them.
JWT Exposure Through localStorage
During testing, the application was observed storing the authentication token within the browser's localStorage.
The token remained accessible to any JavaScript executing within the application's origin.
Unlike HttpOnly cookies, values stored in localStorage can be read directly by client-side scripts. Consequently, any successful Cross-Site Scripting (XSS) vulnerability would allow an attacker to immediately retrieve the victim's authentication token and exfiltrate it to an external server.
Because the JWT functions as the application's primary authentication mechanism, possession of the token is sufficient to impersonate the authenticated user until the token expires or is revoked.
During testing, the token was successfully extracted directly from the browser's storage and reused in authenticated requests.
No Session Expiration
Throughout testing, authentication tokens remained valid without any observable expiration mechanism.
Even after extended periods of inactivity, previously issued JWTs continued to authenticate successfully, indicating that session lifetime was not being actively enforced.
This substantially increases the window of opportunity for attackers who obtain stolen tokens.
No Server-Side Token Invalidation
Testing also demonstrated that the application relied entirely on possession of a valid JWT.
No evidence of centralized session tracking or token revocation was observed.
As a result:
- Previously issued tokens remained usable.
- Existing sessions were not invalidated.
- Authentication relied solely on whether the client possessed a valid JWT.
This means that if an attacker obtains a token, access may persist until the token naturally becomes unusable, if expiration is implemented at all.
Impact
An attacker who successfully obtains a valid JWT may be able to:
- Hijack authenticated user sessions.
- Maintain unauthorized access for extended periods.
- Reuse previously stolen authentication tokens.
- Bypass intended session termination.
- Increase the effectiveness of Cross-Site Scripting attacks through token theft.
- Target administrative functionality if authorization controls are improperly enforced.
- Compromise the application's overall trust model.
Remediation
Authentication tokens should be treated as highly sensitive credentials throughout their lifecycle.
To strengthen the application's authentication model:
Secure Token Storage
- Store authentication tokens in HttpOnly cookies.
- Enable the Secure and SameSite cookie attributes.
- Prevent JavaScript access to authentication tokens.
Strengthen JWT Security
- Validate JWT signatures on every authenticated request.
- Use strong cryptographic signing keys.
- Rotate signing secrets periodically.
- Enforce short-lived access tokens using the exp claim.
- Reject expired, malformed, or tampered tokens.
Server-Side Authorization
- Never rely solely on authorization attributes contained within JWT payloads.
- Retrieve user roles and permissions from trusted server-side records.
- Perform authorization checks for every protected resource.
Improve Session Management
- Implement server-side token revocation.
- Invalidate active sessions after password changes, logout, or account recovery.
- Maintain centralized session tracking.
- Enforce inactivity timeouts and maximum session lifetimes.
Authorization Failures
What Is Authorization?
While authentication verifies the identity of a user, authorization determines what that user is allowed to access or perform within the system.
For example, in a banking application, a user should only be able to view and interact with their own financial data. Access to another user's account or transactions should always be restricted.
When authorization controls are improperly implemented, attackers can bypass these restrictions and gain access to sensitive data belonging to other users.
Broken Object Level Authorization (BOLA)
Severity: Critical
Description
BOLA occurs when an application fails to enforce proper access control checks on resources tied to user-specific identifiers.
Instead of validating ownership on the server side, the application trusts user-controlled input such as account numbers. This allows attackers to manipulate these identifiers and access unauthorized data.
In the VulnBank application, the transaction history functionality exposes account-based data without verifying whether the requesting user owns the account.
How It Was Discovered
While interacting with the transaction history feature, a request was made to retrieve transactions associated with the authenticated user.
After confirming normal functionality, the request was modified by replacing the account number with that of another account created during testing.
When the modified request was sent, the application processed it successfully without performing any ownership validation.
The response returned a full list of transactions belonging to the target account, despite the requester not being the owner.
This confirmed that the application does not enforce authorization checks and allows direct access to any account's transaction history by simply modifying the account number.
Evidence of Data Exposure
The response included sensitive financial information such as:
- Transaction amounts.
- Source and destination accounts.
- Timestamps.
- Transaction descriptions.
This demonstrates that all stored data associated with an account is exposed without restriction.
Impact
This vulnerability allows attackers to:
- Access transaction histories of any user.
- Enumerate valid account numbers.
- Monitor financial activity and behavior.
- Extract sensitive and potentially malicious stored data.
In real-world scenarios, this could lead to:
- Privacy violations.
- Financial intelligence gathering.
- Fraud and social engineering attacks.
Remediation
- Enforce strict server-side authorization checks.
- Verify that the authenticated user owns the requested resource.
- Avoid exposing direct object references (e.g account numbers), use indirect references or mapped identifiers.
- Log and monitor unauthorized access attempts.
Broken Object Level Authorization via Token Manipulation
Severity: Critical
Description
This vulnerability is a more severe form of BOLA caused by improper validation of authentication tokens.
The /api/virtual-cards endpoint relies on a JWT provided in the Authorization header but fails to verify whether the token actually belongs to the authenticated session.
As a result, attackers can supply a different user's token and access their sensitive data.
How It Was Discovered
While testing the virtual card functionality, a request was made to retrieve the authenticated user's virtual cards using a valid JWT.
To evaluate authorization controls, the token in the authorization header was replaced with a token belonging to another user, while keeping the session cookie unchanged.
After modifying the token, the request was sent to the server.
The application accepted the request and processed it using the attacker-supplied token, without validating whether it matched the current user session.
As a result, the response returned virtual card details belonging to the targeted user.
This confirmed that the application blindly trusts the Authorization token without enforcing proper ownership or session validation.
Evidence of Sensitive Data Exposure
The response included highly sensitive financial data such as:
- Full card numbers.
- CVV values.
- Expiry dates.
- Card limits.
Impact
This vulnerability allows attackers to:
- Impersonate other users.
- Access virtual cards belonging to any user.
- Retrieve full card details including CVV.
- Perform unauthorized financial transactions.
Additionally, this introduces serious compliance risks:
- PCI-DSS violations.
- Exposure of highly sensitive financial data.
- Potential large-scale data breaches.
In real-world scenarios, this could result in:
- Financial fraud.
- Theft of funds.
- Legal and regulatory consequences.
Remediation
- Validate JWT tokens securely on the server.
- Enforce strict ownership checks for all resources.
- Bind tokens to user sessions and ensure consistency.
- Never expose full card details (mask sensitive data).
- Avoid storing sensitive data such as CVV.
These vulnerabilities highlight a fundamental breakdown in authorization controls.
Even with authentication mechanisms in place, failure to properly validate access rights allows attackers to bypass security boundaries and gain unauthorized access to highly sensitive data.
Mass Assignment & Excessive Data Exposure
Severity: Critical
Description
Mass Assignment occurs when an application automatically binds user-supplied input to internal objects without properly restricting which fields can be modified.
Excessive Data Exposure occurs when an application returns more data than necessary in its API responses, often leaking sensitive internal attributes.
In the VulnBank application, the registration endpoint exposes internal application data and allows user-controlled modification of sensitive fields such as account balance and administrative privileges.
How It Was Discovered
During testing of the registration functionality, a new account was created using the standard registration form, which only required a username and password.
After intercepting the request and analyzing the server's response, it was observed that the application returned additional internal fields that were not part of the user input.
These fields included sensitive attributes such as account number, balance, administrative status, and user ID.
This behavior indicated that the backend was exposing internal object structures and potentially trusting user input for those fields.
Exploitation
After identifying that sensitive fields were being returned in the response, the registration request was modified to include additional parameters that were not present in the original form.
These parameters included fields controlling administrative privileges and account balance.
The modified request was then sent to the server.
The application accepted the injected fields without validation and used them to construct the user account. As a result, the newly created account was assigned elevated privileges and an arbitrarily large balance.
This confirmed that the backend blindly trusts user-supplied input and directly maps it to internal objects without enforcing any restrictions.
Impact
This vulnerability allows attackers to fully manipulate internal application logic.
An attacker can:
- Grant themselves administrative privileges.
- Assign arbitrary account balances.
- Modify internal application state.
- Bypass business logic and security controls.
In a real-world banking system, this could lead to:
- Unauthorized access to administrative functionality.
- Financial fraud at scale.
- Complete compromise of the application.
This vulnerability effectively breaks both authentication and authorization controls.
Together, these flaws enable attackers to understand and directly control backend logic, resulting in full system compromise.
Remediation
Input Handling
- Implement strict whitelisting of allowed input fields.
- Reject any unexpected or extra parameters.
Backend Security
- Use Data Transfer Objects (DTOs) instead of binding directly to models.
- Explicitly map only safe fields from user input.
Output Control
- Remove debug and internal fields from API responses.
- Return only necessary user-facing data.
Access Control
- Never allow client-side control over sensitive fields such as, is_admin, balance, user_id.
Transaction Exploitation
Importance of Transaction Security
Financial transaction systems are highly sensitive components of any banking application. They must enforce strict validation and consistency to prevent fraud, unauthorized manipulation, and financial loss.
Any weakness in transaction handling can be exploited to directly impact account balances and system integrity.
Negative Amount Transfers
Severity: Critical
Description
The application fails to properly validate transaction input, specifically the transfer amount. Instead of enforcing that only positive values are allowed, the system accepts negative amounts.
This leads to a business logic flaw where attackers can manipulate how balances are calculated during transfers.
How It Was Discovered
While testing the fund transfer functionality, a standard transaction request was intercepted and reviewed.
The transfer amount parameter was identified as user-controlled input. To test validation controls, the value was modified to a negative number before sending the request.
Exploitation
After submitting the modified request, the application processed the transaction successfully instead of rejecting it.
The system treated the negative value as a valid input and applied it during balance calculation.
As a result, the transaction logic was reversed, allowing manipulation of account balances. Instead of deducting funds as expected, the operation led to unintended balance changes that could be abused for financial gain.
This confirms that the application lacks proper validation for transaction amounts and fails to enforce basic business rules.
Impact
This vulnerability allows attackers to:
- Manipulate account balances.
- Bypass transaction validation rules.
- Potentially generate or retain unauthorized funds.
In a real-world scenario, this could lead to:
- Direct financial fraud.
- Loss of funds for the platform.
- Breakdown of trust in the system.
Remediation
- Enforce strict validation to allow only positive transaction amounts.
- Implement server-side checks for all financial operations.
- Reject any malformed or logically invalid transactions.
Race Condition in Transfers
Severity: Critical
Description
The application fails to properly synchronize transaction processing, allowing multiple requests to be processed concurrently without ensuring balance consistency.
This results in a race condition where multiple transactions can be executed before the account balance is updated.
How It Was Discovered
While testing the robustness of the transfer functionality, multiple identical transfer requests were sent in rapid succession. This was done to observe how the application handles concurrent operations affecting the same account balance.
When multiple transfer requests were sent almost simultaneously, the application processed several of them before updating the account balance.
Because there was no locking or synchronization mechanism in place, each request was validated against the same initial balance.
This allowed multiple transactions to succeed even when the account should not have had sufficient funds.
As a result, funds were effectively withdrawn multiple times, exceeding the actual available balance.
Impact
This vulnerability allows attackers to:
- Perform multiple withdrawals using the same balance.
- Bypass insufficient balance checks.
- Drain funds beyond available limits.
In real-world scenarios, this could result in:
- Severe financial loss.
- Exploitation of transaction systems at scale.
- Inconsistent and unreliable financial records.
Remediation
- Implement proper transaction locking mechanisms.
- Use atomic operations for balance updates.
- Enforce consistency checks before and after transactions.
- Apply rate limiting on sensitive endpoints.
Both vulnerabilities highlight critical failures in enforcing business logic within the transaction system. The negative transfer issue demonstrates lack of input validation and race condition shows failure in handling concurrent operations.
When combined, these flaws significantly increase the risk of financial exploitation and system abuse.
File Handling Vulnerabilities
Category: Insecure File Handling / Input Validation
Overview
File upload functionality is a common attack surface in web applications. Improper validation of uploaded files, filenames, and storage locations can introduce risks ranging from information disclosure to remote code execution.
During testing, the profile picture upload functionality was assessed to determine whether uploaded files were properly validated, sanitized, and stored securely.
Several weaknesses were identified.
Unrestricted File Upload Behavior
Severity: Low
How It Was Discovered
The application allows users to upload profile pictures through the profile management interface.
Uploaded files are accepted and stored within a web-accessible directory under:
/static/uploads/
The application does not appear to enforce strict filename restrictions before processing uploads and relies heavily on sanitization mechanisms after receiving user input.
Although direct code execution was not observed during testing, accepting user-controlled files into a publicly accessible directory increases the application's attack surface.
How It Was Discovered
While testing profile picture uploads, a legitimate image was uploaded and the request was intercepted in Burp Suite.
The server successfully processed the upload and returned a file path indicating where the file had been stored.
The response confirmed that uploaded content is stored in a publicly accessible location.
Impact
An attacker may be able to:
- Upload unexpected file types.
- Abuse storage functionality.
- Leverage uploaded files during chained attacks.
- Increase attack surface for future exploitation.
Remediation
- Restrict uploads to approved file types only.
- Validate MIME types and file signatures.
- Store uploaded files outside the web root.
- Generate server-side filenames.
- Implement malware scanning where appropriate.
Path Traversal Testing
Severity: Medium
Description
Path Traversal vulnerabilities occur when applications improperly process file paths, allowing attackers to access files outside the intended directory structure.
The upload functionality was tested to determine whether filename parameters could be manipulated to traverse directories.
How It Was Tested
During upload testing, traversal payloads were introduced into the filename parameter.
Instead of rejecting the request, the application accepted the input and transformed the supplied filename before storing it.
The server replaced traversal sequences and special characters with sanitized values before generating the final file path.
Testing confirmed that traversal sequences such as:
../
were not interpreted by the filesystem.
Attempts to reference sensitive locations such as:
../../../etc/passwd
and more complex nested paths were transformed into harmless filenames before storage.
This behavior prevented successful directory traversal.
However, malicious input was still accepted and processed rather than rejected outright.
This indicates reliance on sanitization instead of strict validation.
Impact
No direct path traversal was achieved during testing.
However, the behavior demonstrates:
- Weak input validation controls.
- Acceptance of malicious input.
- Increased risk of future bypasses if sanitization logic changes.
Remediation
- Reject traversal patterns entirely.
- Implement allowlist-based filename validation.
- Normalize paths before processing.
- Log and alert on traversal attempts.
Security Concern
The application relies on sanitization instead of strict validation.
This means:
- Malicious input is still accepted.
- Security depends on transformation logic.
- Potential bypass techniques may still exist.
Overall Assessment
Overall Severity: Medium
The file handling implementation includes several defensive mechanisms, particularly filename sanitization and path normalization, which successfully prevented direct exploitation attempts during testing.
However, the application consistently accepts malicious input and attempts to sanitize it rather than rejecting it outright. This approach increases long-term risk and may become exploitable if additional functionality is introduced or if sanitization logic is bypassed.
While no critical file upload vulnerability was identified during testing, the observed behavior reflects a weak trust model that could contribute to more serious attacks when combined with other vulnerabilities discovered throughout the assessment.
Client-Side Attacks
Cross-Site Scripting (XSS)
Severity: High
Description
Cross-Site Scripting (XSS) occurs when an application processes and renders user-supplied input in the browser without proper sanitization or output encoding. This allows attackers to inject malicious scripts that execute in the context of other users' sessions.
In the VulnBank application, user-controlled input is rendered directly within the interface, creating an opportunity for script injection and execution.
How It Was Discovered
During testing of user interaction features, attention was focused on areas where users can submit content that is later displayed within the application.
The "Description (optional)" field in the money transfer functionality was identified as a potential injection point because:
- It accepts free-form user input.
- It is later displayed in transaction history.
To test for input handling weaknesses, a harmless script-based payload was submitted through this field.
After submitting a transaction containing a crafted payload, the application processed the request normally without any validation errors.
When the transaction was later viewed in the interface, the injected payload was rendered directly in the browser instead of being sanitized or escaped.
This caused the embedded script to execute automatically when the page loaded, confirming that the application:
- Stores user input without sanitization.
- Renders it without output encoding.
Impact
This vulnerability allows attackers to execute arbitrary JavaScript in the browser of other users.
In the context of a banking application, this is particularly severe and can lead to:
- Session Hijacking, extraction of authentication tokens (especially since tokens are stored in localStorage).
- Account Takeover, performing actions on behalf of victims without their knowledge.
- Unauthorized Transactions, triggering transfers or financial operations.
- Credential Theft, capturing login credentials via injected scripts.
- Phishing Attacks, injecting fake UI elements to trick users.
- Data Exfiltration, accessing sensitive information displayed in the application.
Remediation
1. Input Validation
- Sanitize all user inputs on the server side.
- Reject suspicious payloads (e.g., script tags, event handlers).
2. Output Encoding
- Encode all user-generated content before rendering.
- Use frameworks that automatically escape output.
3. Content Security Policy (CSP)
- Implement a strict CSP to prevent script execution.
- Disable inline JavaScript.
4. Secure Token Storage
- Avoid storing sensitive tokens in localStorage.
- Use HTTP-only cookies instead.
5. Defense-in-Depth
- Combine validation, encoding, and browser protections.
- Regularly test for stored and reflected XSS.
This vulnerability highlights how seemingly harmless input fields can become high-risk attack vectors when proper validation and encoding are not enforced.
When combined with other vulnerabilities identified in the application, this XSS issue significantly increases the likelihood of full system compromise and account takeover.
Server-Side Request Forgery (SSRF) via Profile Image Import
Severity: Critical
Description
Server-Side Request Forgery (SSRF) occurs when an application retrieves remote resources on behalf of a user without properly validating the supplied destination.
In VulnBank, users can import a profile picture by providing an external URL. The application retrieves the supplied resource server-side and stores the downloaded content as the user's profile image.
Testing revealed that the application performs insufficient validation on user-supplied URLs, allowing attackers to force the server to initiate arbitrary outbound requests.
This behavior enabled access to:
- Internal services.
- Localhost resources.
- Cloud metadata endpoints.
- Infrastructure configuration data.
- Instance identification information.
The vulnerability ultimately exposed sensitive information about the underlying cloud environment hosting the application.
How It Was Discovered
While testing the profile management functionality, attention was drawn to the "Import via URL" feature available during profile picture updates.
The feature allows users to provide a URL which the application then retrieves and stores as a profile image.
Initial Functionality Verification
A legitimate image URL was first supplied to verify how the feature operated.
The application successfully downloaded the image and returned a path indicating where the file had been stored.
This confirmed that:
- The server initiates outbound requests.
- Downloaded content is stored locally.
- User-supplied URLs are processed server-side.
Because the server was fetching arbitrary resources on behalf of users, the functionality became a strong SSRF candidate.
Blind SSRF Validation
After confirming that the application accepted arbitrary URLs for profile image imports, the next objective was to determine whether the supplied URL was fetched by the server or simply referenced by the client.
To validate this behavior, an attacker-controlled URL generated through Webhook.site was supplied to the Import from URL functionality. The request was intercepted and modified in Burp Suite before being forwarded to the server.
Rather than returning an image directly, the application responded with a success message indicating that the resource had been downloaded and stored locally. The response also included the generated storage path, confirming that the server had processed the supplied URL.
To verify whether the request originated from the application server, the Webhook.site interaction logs were examined immediately after the request completed.
The interaction log recorded an inbound HTTP request originating from the VulnBank server. The captured request included request headers such as the User-Agent (python-requests/2.31.0), confirming that the application backend, not the victim's browser had initiated the outbound connection.
Finally, visiting the Webhook URL directly from the browser displayed the default Webhook.site landing page, which contained no image or downloadable content.
This confirms that the profile picture was not loaded by the browser itself. Instead, the application backend retrieved the resource, saved it locally, and served it back to the client, conclusively demonstrating server-side request forgery.
This behavior confirms a **Blind Server-Side Request Forgery (**Blind SSRF) vulnerability.
The application allows user-supplied URLs to be fetched by the backend without enforcing destination restrictions or validating the requested host. Although the response does not directly return the fetched resource, the successful outbound interaction proves that attackers can force the application server to initiate requests to arbitrary external or internal destinations.
Once SSRF was confirmed, testing progressed from simple external interaction validation to internal network reconnaissance and cloud metadata enumeration, eventually exposing sensitive infrastructure information from the underlying hosting environment.
Internal Network Reconnaissance
After confirming that the server would fetch arbitrary external URLs, the next objective was to determine whether the SSRF vulnerability could reach internal network resources that were inaccessible from the public internet.
Rather than immediately targeting cloud metadata endpoints, internal hosts were probed first to understand the network boundaries and identify services exposed only to the backend server.
Step 1: Probing an Internal Docker Network Host
The first internal target tested was:
This IP address was selected because applications running inside Docker environments commonly communicate over private bridge networks (172.x.x.x).
The request was submitted through the Import from URL functionality.
The server attempted to establish the connection and returned an error message.
Although the request timed out, the response itself confirmed several important observations;
- The backend attempted the connection.
- Internal IP ranges were reachable from the application server.
- Detailed backend exception messages were exposed to users.
- SSRF filtering was not implemented.
Step 2: Accessing Localhost services
The next target was the loopback interface.
Unlike the previous request, the server successfully connected to the local service.
The application returned:
The successful HTTP 200 response confirmed that services running exclusively on localhost were reachable through the vulnerable endpoint.
This significantly increased the impact of the SSRF vulnerability because services that developers often assume are inaccessible from external users could now be reached indirectly through the application itself.
Step 3: Discovering Hidden Internal Endpoints
Having confirmed access to localhost, additional internal paths were tested.
One such endpoint was: http://127.0.0.1:5000/internal/secret
Unlike previous requests, this endpoint returned an internal application file containing sensitive configuration data.
The downloaded file exposed information including:
- Application secret keys.
- JWT signing secret.
- Database host.
- Database username.
- Database password.
- Database port.
- AI API key.
- Internal operating system information.
The application also exposed the downloaded resource through a publicly accessible URL under:
/static/uploads/<generated_filename>
This allowed sensitive backend files retrieved via SSRF to remain accessible after the request completed.
This finding demonstrated that the SSRF vulnerability was not limited to network scanning, it enabled retrieval of confidential backend configuration files that could facilitate further attacks such as database compromise, authentication bypass, and lateral movement.
Cloud Metadata Discovery
After successfully accessing localhost services, the next objective was to determine whether the application could reach cloud-specific metadata services.
Many cloud providers expose an instance metadata service that is only intended to be accessible from within the virtual machine. This service commonly stores information about the running instance, networking configuration, and, in some environments, temporary IAM credentials.
Using the SSRF vulnerability, the following request was submitted:
http://127.0.0.1:5000/latest/meta-data
The application successfully processed the request and returned a 200 OK response.
The response also included debug information confirming that the backend server had fetched the requested resource.
The downloaded file generated by the application was then accessed through the exposed /static/uploads/ directory.
Rather than returning an image, the file contained a list of metadata resources available through the internal service, ami-id, hostname, iam/, instance-id, local-ipv4, public-ipv4, security-groups.
This confirmed that the SSRF vulnerability provided direct access to the metadata service and allowed further enumeration of sensitive instance information.
Cloud metadata services are designed for internal use and should never be reachable by external users.
When an SSRF vulnerability can access these endpoints, attackers may be able to retrieve information such as:
- Instance identifiers.
- Hostnames.
- Private and public IP addresses.
- Network configuration.
- Security group information.
- IAM roles and temporary credentials (when available).
This information significantly increases the impact of an SSRF vulnerability because it enables attackers to map the cloud environment and, in some cases, obtain credentials that can be used to access other cloud resources.
Progressive Metadata Enumeration
Having confirmed access to the cloud metadata service through the /latest/meta-data/ endpoint, the assessment proceeded by systematically enumerating individual metadata resources. This approach reflects the methodology commonly employed during cloud penetration tests, where reconnaissance begins with general instance information before progressively targeting increasingly sensitive resources.
Retrieving the Amazon Machine Image (AMI) Identifier
The first request targeted the instance's Amazon Machine Image (AMI) identifier.
http://127.0.0.1:5000/latest/meta-data/ami-id
The application successfully fetched the resource, stored it locally, and made it accessible through the static uploads directory. The downloaded file disclosed the AMI identifier associated with the running instance.
Observation
- Successfully retrieved the AMI identifier.
- Confirms unrestricted access to cloud instance metadata.
- Provides insight into the operating system image deployed within the environment.
Retrieving the Internal Hostname
The next request queried the hostname endpoint.
http://127.0.0.1:5000/latest/meta-data/hostname
The downloaded response revealed the internal hostname assigned to the instance.
vulnbank.internal
The hostname provides additional intelligence regarding internal naming conventions and infrastructure configuration.
Observation
- Internal hostname successfully disclosed.
- Reveals host naming conventions useful during internal reconnaissance.
Retrieving the Instance Identifier
Enumeration then continued with the instance identifier.
http://127.0.0.1:5000/latest/meta-data/instance-id
The application successfully retrieved and stored the resource. Accessing the downloaded file revealed the unique identifier assigned to the virtual machine.
Observation
- Instance identifier successfully retrieved.
- Enables identification of the compromised cloud instance.
- Useful for cloud asset correlation and infrastructure mapping.
Retrieving the Local IPv4 Address
The assessment then queried the internal IP address assigned to the instance.
http://127.0.0.1:5000/latest/meta-data/local-ipv4
The server returned the instance's private IPv4 address, confirming that internal network information could be accessed through the vulnerable functionality.
Observation
- Internal IP address successfully disclosed.
- Reveals the instance's position within the private cloud network.
- Provides valuable information for lateral movement planning.
Retrieving the Public IPv4 Address
Next, the public-facing IP address was requested.
http://127.0.0.1:5000/latest/meta-data/public-ipv4
The response disclosed the external IPv4 address assigned to the cloud instance.
Observation
- Public IP address successfully retrieved.
- Confirms exposure of externally accessible network information.
- Assists attackers in correlating internal and external cloud assets.
Retrieving Security Group Information
To better understand the instance's network configuration, the security group endpoint was queried.
http://127.0.0.1:5000/latest/meta-data/security-groups
The response revealed the security group associated with the instance.
default
Knowledge of assigned security groups provides valuable information regarding network segmentation and firewall policies protecting the instance.
Observation
- Security group successfully disclosed.
- Reveals cloud networking configuration.
- Supports further infrastructure reconnaissance.
Enumerating IAM Metadata
After collecting general instance metadata, the assessment progressed to Identity and Access Management (IAM) metadata.
The following endpoint was requested:
http://127.0.0.1:5000/latest/meta-data/iam/
Instead of returning credentials directly, the endpoint responded with the available IAM resource.
security-credentials/
This confirmed that the instance had an attached IAM role and that additional identity information was available for enumeration.
Observation
- IAM metadata successfully enumerated.
- Presence of an attached IAM role confirmed.
Discovering the Attached IAM Role
Enumeration continued by requesting the security credentials directory.
http://127.0.0.1:5000/latest/meta-data/iam/security-credentials/
The response disclosed the name of the IAM role assigned to the instance.
vulnbank-role
Knowing the role name enabled direct access to the endpoint containing temporary cloud credentials.
Observation
- IAM role successfully identified.
- Enabled progression to credential retrieval.
Retrieving Temporary Cloud Credentials
Finally, the assessment requested the endpoint corresponding to the discovered IAM role.
http://127.0.0.1:5000/latest/meta-data/iam/security-credentials/vulnbank-role
The application successfully retrieved the resource, stored it within the static uploads directory, and exposed it through a downloadable file.
Inspection of the downloaded response revealed temporary cloud credentials, including:
- AccessKeyId
- SecretAccessKey
- Session Token
- Expiration Timestamp
- Token Type
This represents the highest impact stage of the assessment. The exposed credentials could potentially be used to authenticate directly to cloud services with the privileges assigned to the instance role. Depending on the IAM policy attached to vulnbank-role, an attacker may be able to enumerate cloud resources, access sensitive services, or further compromise the cloud environment.
Observation
- Temporary IAM credentials successfully disclosed.
- Complete cloud authentication material exposed.
- Demonstrates full compromise of the instance identity through SSRF.
Analysis
The SSRF vulnerability extends well beyond simple server-side URL fetching. The testing demonstrated that the application could be used as an internal network reconnaissance tool, allowing an attacker to systematically enumerate resources that are normally inaccessible from the Internet.
The assessment began by confirming that the application successfully processed arbitrary external URLs before progressively targeting internal resources. Requests to internal IP addresses confirmed that the vulnerable server could reach services on its own network, effectively bypassing network segmentation.
Following successful localhost access, the assessment progressed to enumerating cloud-style metadata endpoints in a structured manner:
- /latest/meta-data/ami-id successfully disclosed the Amazon Machine Image (AMI) identifier.
- /latest/meta-data/hostname revealed the internal hostname (vulnbank.internal), confirming the existence of an internal naming scheme.
- /latest/meta-data/instance-id exposed the underlying cloud instance identifier.
- /latest/meta-data/local-ipv4 disclosed the server's internal private IP address.
- /latest/meta-data/public-ipv4 exposed the public IP assigned to the instance.
- /latest/meta-data/security-groups revealed the security group protecting the instance (default).
- /latest/meta-data/iam/ confirmed that IAM metadata was enabled and accessible.
- /latest/meta-data/iam/security-credentials/ enumerated the available IAM role (vulnbank-role).
- /latest/meta-data/iam/security-credentials/vulnbank-role returned temporary AWS IAM credentials, including, Access key ID, Secret Access Key, Session token, Expiration timestamp.
This progression demonstrates a realistic attacker workflow, beginning with low-risk validation before incrementally escalating to increasingly sensitive internal resources. Each successful request disclosed additional information about the underlying cloud infrastructure and confirmed that the application performed unrestricted server-side requests without validating destination addresses.
The disclosure of temporary IAM credentials represents the highest impact stage of the assessment. Rather than merely confirming SSRF, the vulnerability enabled retrieval of credentials that could potentially be used to interact with cloud resources assigned to the instance's IAM role. The ability to access cloud instance metadata indicates that the server was able to communicate with trusted internal services that should never be exposed through user-controlled functionality.
Impact
This vulnerability presents Critical risk due to the breadth of information that can be obtained through unrestricted server-side requests.
Potential impacts include:
- Complete internal network reconnaissance through access to localhost and private IP ranges.
- Disclosure of cloud infrastructure information, including AMI identifiers, instance IDs, hostnames, private IP addresses, public IP addresses, and security group configuration.
- Enumeration of IAM roles attached to the cloud instance.
- Exposure of temporary AWS IAM credentials through the metadata service.
- Potential unauthorized access to cloud resources depending on the permissions assigned to the compromised IAM role.
- Bypass of network isolation controls by forcing the application server to communicate with otherwise inaccessible internal services.
- Significant expansion of the attack surface, allowing attackers to pivot from the web application into the underlying cloud environment.
Remediation
To mitigate this vulnerability:
- Strictly validate all user-supplied URLs before performing server-side requests.
- Implement an allowlist of trusted domains rather than permitting arbitrary destinations.
- Block requests to:
- localhost (127.0.0.0/8)
- private RFC1918 address ranges
- link-local addresses
- metadata service addresses
- IPv6 loopback and internal addresses
- Prevent redirects that resolve to internal resources.
- Disable or restrict outbound connections from the application wherever possible.
- Configure cloud metadata services using Instance Metadata Service Version 2 (IMDSv2) or equivalent protections and disable legacy metadata access where supported.
- Apply the principle of least privilege to IAM roles so that temporary credentials have only the minimum permissions required.
- Avoid exposing internal debugging information or fetched resource details within API responses.
- Continuously monitor and alert on unusual outbound requests from application servers to internal or metadata endpoints.
This combination of application-layer validation, network egress controls, and cloud hardening significantly reduces the likelihood that SSRF can be leveraged for internal reconnaissance or cloud credential compromise.
GraphQL Security Assessment
Severity: High
Overview
Modern web applications increasingly adopt GraphQL as an alternative to traditional REST APIs because it allows clients to request precisely the data they require through a single endpoint. Instead of exposing numerous resource-specific endpoints, GraphQL consolidates application functionality behind a unified query interface, providing greater flexibility and reducing over-fetching of data.
While these characteristics improve developer and client experience, they also introduce a unique attack surface. Unlike conventional REST APIs, GraphQL implementations may unintentionally expose significant information about their internal architecture through schema introspection, verbose error handling, excessive data exposure, and insufficient authorization controls. When improperly secured, a single GraphQL endpoint can provide attackers with an accurate blueprint of the application's backend, dramatically reducing reconnaissance effort and enabling highly targeted security testing.
During the assessment of VulnBank, attention was directed toward the application's /graphql endpoint after reconnaissance identified GraphQL support for transaction analytics. Because GraphQL centralizes multiple business operations behind a single endpoint, the assessment focused not only on discovering available queries but also on understanding how authorization decisions were enforced and whether sensitive financial analytics could be accessed beyond their intended scope.
Rather than treating GraphQL as an isolated feature, the assessment evaluated it as a complete attack surface. This included schema enumeration, resolver behavior, authorization enforcement, transaction analytics exposure, and correlation with the underlying source code to validate observed behavior.
The GraphQL assessment was therefore divided into three sequential phases:
Phase 1: Enumerate the GraphQL schema and reconstruct the exposed API through introspection.
Phase 2: Assess resolver behavior, authorization controls, parameter handling, and GraphQL execution characteristics.
Phase 3: Evaluate the security impact of the transaction analytics resolver by comparing standard-user and administrator access, validating implementation through source code review, and assessing the overall exposure of the analytics interface.
Each phase builds upon the previous one, progressing from reconnaissance to behavioral analysis and finally to security impact validation.
Discovery of the GraphQL Endpoint
The GraphQL endpoint was initially accessed using an HTTP GET request to determine how the application responded before interacting with it through GraphQL queries.
Rather than returning a generic error, the server disclosed operational information intended for API consumers. The response confirmed that the endpoint supported GraphQL requests, indicated that schema introspection was available, and referenced example query files associated with transaction analytics.
Although no sensitive business data was exposed at this stage, the response immediately confirmed that the endpoint warranted further investigation. The information disclosed by the server significantly reduced the effort required to begin mapping the GraphQL API and established a clear starting point for systematic enumeration.
The assessment therefore proceeded by validating GraphQL request handling before performing full schema introspection.
Validating GraphQL Request Processing
Before attempting schema enumeration, a POST request was submitted without a GraphQL query to verify that the endpoint was actively processing GraphQL operations.
The server responded with a GraphQL validation error stating that a query was required. Although seemingly insignificant, this confirmed that requests were being processed directly by the GraphQL execution engine rather than rejected at the HTTP layer.
To verify that introspection requests would be accepted, a minimal GraphQL query was then submitted:
{
"query":"{ __typename }"
}
This response confirmed that GraphQL requests were being processed successfully and that introspection functionality remained available to authenticated users.
Having established that the endpoint accepted GraphQL operations and supported introspection, the assessment proceeded to reconstruct the exposed schema in Phase 1.
Phase 1: GraphQL Schema Enumeration and Attack Surface Mapping
Objective
Following the discovery of the /graphql endpoint during application reconnaissance, the first objective was to determine whether GraphQL introspection was enabled and to identify the application's exposed attack surface.
Unlike REST APIs, GraphQL applications frequently disclose their complete schema through the built-in introspection system. When enabled in production, introspection allows authenticated users to enumerate available queries, object types, arguments, nested relationships, and runtime metadata without requiring prior knowledge of the implementation.
The objective of this phase is to;
- Determine whether schema introspection was enabled.
- Enumerate the exposed GraphQL schema.
- Identify available business objects.
- Enumerate user-controlled query arguments.
- Reconstruct the GraphQL data model.
- identify potential entry points for subsequent security testing.
All testing was performed using authenticated requests through Burp Suite Repeater.
GraphQL Schema Introspection
The first request submitted the standard GraphQL introspection query to determine whether the server exposed its schema.
Query
{
"query":"{ __schema { types { name fields { name } } } }"
}
Observation
The request completed successfully and returned the complete GraphQL schema.
Rather than restricting introspection, the server disclosed every registered GraphQL object, including business types, scalar types and internal GraphQL metadata.
The successful response confirmed that schema introspection was enabled for authenticated users.
From an attacker's perspective, this significantly reduces reconnaissance effort by eliminating the need to infer object names, available fields, or query structure through trial and error.
Root Query Enumeration
The introspection response revealed that the application exposed a single business query.
Root Query
Query
— — transactionSummary
Observation
Only one application-specific query was exposed through the GraphQL schema.
Although the GraphQL surface appears relatively small, the transactionSummary resolver exposes a rich object containing financial metrics, transaction history and aggregated analytics. Consequently, this resolver became the primary target for subsequent testing.
Enumerating TransactionSummaryType
Having identified the root query, the next step was to inspect the object returned by the resolver.
Query
{
"query":"{ __type(name:"TransactionSummaryType") { name fields { name type { kind name ofType { kind name } } } } }"
}
The resolver exposes significantly more than basic transaction information.
In addition to transaction counts and balances, it returns financial analytics, lending statistics, billing metrics and nested transaction objects.
This immediately indicated that the endpoint functions as an analytics API rather than a simple transaction lookup service, making it a high-value target for authorization testing.
Enumerating Nested Objects
The nested objects referenced by TransactionSummaryType were then inspected individually.
TransactionTypeAggregate
Query
{ "query": "{ __type(name: "TransactionTypeAggregate") { fields { name type { kind name ofType { kind name } } } } }" }
The object exposed three aggregation fields
TransactionRecordType
Query
{ "query":"{ __type(name:"TransactionRecordType") { fields { name type { kind name ofType { kind name } } } } }" }
The response disclosed the complete structure of each transaction record.
id, fromAccount, fromUsername, toAccount, toUsername, amount, timestamp, transactionType, description.
The resolver exposes both financial data and user-identifying information for every transaction record. This confirmed that successful authorization bypasses would potentially expose sensitive transactional metadata rather than simple account balances.
Query Argument Enumeration
The root query was then inspected to determine which parameters could be controlled by clients.
Query
{ "query":"{ __type(name:"Query") { fields { name args { name type { kind name ofType { kind name } } defaultValue } } } }" }
The resolver accepts two client-controlled parameters:
- accountNumber
- limit
The presence of an explicit accountNumber parameter immediately identified a potential object-level authorization test case, while the limit argument suggested possible opportunities to evaluate excessive data exposure and resource handling.
These parameters became the primary focus of Phase 2.
Directive Enumeration
For completeness, the available GraphQL directives were enumerated.
Query
{
"query":"{ __schema { directives { name locations } } }"
}
The server disclosed the standard GraphQL directives, no custom directives were present.
Although these directives are part of the GraphQL specification and are not vulnerabilities by themselves, their disclosure further confirms that unrestricted introspection remains enabled.
Summary
The GraphQL endpoint exposed unrestricted schema introspection to authenticated users, allowing complete reconstruction of the application's GraphQL API without requiring prior knowledge of its implementation. Through systematic enumeration, the assessment identified the sole business resolver (transactionSummary), mapped its complete return structure, discovered nested business objects, enumerated client-controlled arguments, and identified the available GraphQL directives.
The information obtained during this reconnaissance phase provided a precise blueprint of the application's GraphQL implementation and directly informed the testing strategy adopted in the subsequent phases. Specifically, the discovery of the accountNumber and limit arguments established the foundation for the authorization, parameter manipulation, and analytics testing performed in Phases 2 and 3.
Phase 2: TransactionSummary Resolver Characterization
Following successful GraphQL schema enumeration in Phase 1, the next objective was to understand the behaviour of the newly discovered transactionSummary resolver.
Rather than immediately attempting exploitation, this phase focused on documenting how the resolver behaves under normal conditions, identifying its accepted arguments, mapping the exposed data model, and establishing a baseline for the security testing performed in Phase 3.
Testing was conducted using authenticated GraphQL requests through Burp Suite Repeater.
Default Resolver Behaviour
Query
{
"query":"{ transactionSummary { accountNumber } }"
}
The resolver returned the authenticated user's account number even though no accountNumber argument was supplied. This demonstrates that the resolver derives the account context directly from the authenticated JWT rather than requiring the client to explicitly specify an account identifier.
The endpoint therefore supports implicit account scoping.
Complete Object Enumeration
Query
{
"query":"{ transactionSummary { scope accountNumber totalTransactions totalVolume inflowTotal outflowTotal netFlow largestTransaction totalLoansGiven pendingLoansCount pendingLoansTotal billPaymentTotal billPaymentsCount byType { transactionType count totalAmount } recentTransactions { id fromAccount fromUsername toAccount toUsername amount timestamp transactionType description } } }"
}
The resolver successfully returned every exposed field within the TransactionSummaryType object.
The response contained, Account metadata, Financial aggregates, Loan statistics, Bill payment metrics, Transaction classifications, Recent transaction history.
This confirmed that the GraphQL schema accurately reflected the resolver implementation and that no field-level authorization restrictions were applied within the object itself.
Instead, authorization occurs before object construction.
Explicit Account Parameter
Query
{
"query":"{ transactionSummary(accountNumber:"INSERTYOURACCOUNTHacker") { accountNumber totalTransactions totalVolume } }"
}
Supplying the authenticated account number explicitly produced the same response as omitting the parameter.
The resolver supports both explicit and implicit account resolution for the authenticated user.
Optional Argument Behaviour
The optional accountNumber parameter was tested with several values.
Queries
Authenticated account
{
"query":"{ transactionSummary(accountNumber:"YOUR-ACCOUNT") { accountNumber } }"
}
Null
{
"query":"{ transactionSummary(accountNumber:null) { accountNumber } }"
}
Empty string
{
"query":"{ transactionSummary(accountNumber:"") { accountNumber } }"
}
Whitespace
{
"query":"{ transactionSummary(accountNumber:" ") { accountNumber } }"
}
- omitted argument → authenticated account.
- null → authenticated account.
- empty string → authenticated account.
- whitespace → authorization failure.
The resolver treats omitted, null and empty values identically.
Whitespace, however, is interpreted as an actual identifier and proceeds through the authorization logic.
This demonstrates inconsistent input normalization.
Transaction History Retrieval
Query
{
"query":"{ transactionSummary(limit:15) { recentTransactions { id fromAccount fromUsername toAccount toUsername amount transactionType description } } }"
}
Observation
The resolver returned detailed transaction records associated with the authenticated account.
Each record contained:
- sender account.
- sender username.
- recipient account.
- recipient username.
- amount.
- transaction type.
- description.
The endpoint exposes a rich transaction object intended for authenticated users.
At this stage, no indication existed that another user's records could be accessed.
Aggregate Financial Metrics
Query
{
"query":"{ transactionSummary { totalTransactions totalVolume inflowTotal outflowTotal netFlow largestTransaction } }"
}
Aggregate financial statistics were successfully returned. The resolver performs server-side aggregation before returning summarized financial metrics.
Transaction Classification
Query
{
"query":"{ transactionSummary { byType { transactionType count totalAmount } } }"
}
Transactions were grouped by transaction type together with their counts and cumulative amounts. The resolver performs server-side analytics rather than returning raw transaction records alone.
This functionality becomes particularly significant during the security assessment performed in Phase 3.
Runtime Type Identification
Query
{
"query":"{ transactionSummary { __typename } }"
}
The GraphQL server disclosed the runtime object type.
TransactionSummaryType
The __typename meta-field remained available, allowing clients to identify runtime object types even after schema enumeration.
Phase 2 Summary
Phase 2 established the functional behaviour of the transactionSummary resolver before attempting active exploitation.
Testing confirmed that the resolver:
- derives account context directly from the authenticated JWT when no account number is supplied.
- Supports both implicit and explicit account resolution for the authenticated user.
- Exposes detailed transaction histories together with aggregate financial analytics.
- Provides transaction classifications and summary statistics through a single GraphQL object.
- Performs authorization prior to constructing the response object.
- Exhibits inconsistent handling of optional input values (null, empty string and whitespace), and
- Exposes GraphQL runtime type information through the __typename meta-field.
Although the resolver exposes extensive financial data, no security conclusions are drawn in this phase. The objective here was to understand its behaviour and establish a baseline for the exploitation exercises presented in the next phase.
Phase 3: Exploitation of the Transaction Analytics Resolver
Objective
Following the resolver behavior assessment in Phase 2, the final stage of the GraphQL assessment focused on determining the practical security impact of the exposed transactionSummary resolver.
Rather than identifying new GraphQL fields, this phase evaluates how the resolver behaves under different privilege levels and whether authenticated users can access analytics beyond their intended authorization scope.
The objectives is to determine:
- Whether transaction analytics are restricted according to user privileges.
- Whether administrator accounts expose a broader analytics surface.
- Whether authorization decisions are enforced consistently.
- Whether sensitive financial metrics can be aggregated through GraphQL.
- Whether the implementation aligns with the documented GraphQL security model.
Testing was performed using both standard user accounts and administrative accounts created during the assessment.
Standard User Transaction Analytics
A standard authenticated account queried the resolver without providing an explicit account number.
Query
{
"query":"{ transactionSummary { scope totalTransactions totalVolume inflowTotal outflowTotal netFlow largestTransaction } }"
}
The resolver returned, scope, totalTransactions, totalVolume, inflowTotal, outflowTotal, netFlow, largestTransaction.
The returned values represented only the authenticated user's account.
The resolver automatically scoped all analytics to the authenticated user's account.
No other customer records were included.
This confirms the authorization logic identified during Phase 2, Instead of trusting client-supplied identifiers, the resolver derives the account context directly from the authenticated JWT and limits aggregation to that account.
Recent Transaction Analytics
The resolver was then instructed to return the maximum number of recent transactions.
Query
{
"query":"{ transactionSummary(limit:15) { scope recentTransactions { id fromAccount fromUsername toAccount toUsername amount transactionType description } } }"
}
The resolver returned only transactions associated with the authenticated account.
Although authorization was correctly enforced, the resolver exposes considerably richer metadata than would normally be required for a simple transaction summary endpoint.
This increases the amount of information available to authenticated users and broadens the impact should an authorization flaw later be introduced.
Transaction Type Aggregation
The transaction breakdown functionality was evaluated.
Query
{
"query":"{ transactionSummary { byType { transactionType count totalAmount } } }"
}
The resolver successfully grouped transactions by transaction type.
This demonstrates that GraphQL is exposing analytical information rather than simply returning raw transaction records.
While appropriate for reporting dashboards, these aggregation capabilities increase the value of the endpoint if broader authorization weaknesses were ever introduced.
Administrator Analytics Surface
The same resolver was queried using an administrator account.
Query
{
"query":"{ transactionSummary { scope totalTransactions totalVolume inflowTotal outflowTotal netFlow largestTransaction totalLoansGiven pendingLoansCount pendingLoansTotal billPaymentTotal billPaymentsCount } }"
}
Unlike standard users, administrator accounts automatically received global financial analytics spanning the entire application.
No account number parameter was required.
This represents the intended administrative functionality implemented within the resolver.
Source code review confirms that when an authenticated user possesses the is_admin attribute, the resolver removes the account restriction and aggregates every transaction within the database.
While expected for administrative dashboards, this substantially increases the sensitivity of the endpoint. A compromise of an administrator JWT would immediately expose institution-wide financial metrics, transaction volumes, lending statistics, and billing information through a single GraphQL query.
Source Code Validation
To verify that the observed behavior matched the implementation, the GraphQL resolver was reviewed.
The resolver determines the execution scope through the _resolve_scope() function:
if actor['is_admin']:
return None, 'global', None
When the authenticated user possesses administrative privileges:
- account filtering is removed.
- _load_transactions(None) retrieves every transaction.
- _build_transaction_summary() aggregates the complete dataset.
Conversely, standard users are restricted to:
WHERE from_account=
OR to_account=
The source code fully explains the behavior observed during testing.
Rather than an authorization bypass, the GraphQL endpoint intentionally exposes two execution contexts:
- Account scope for regular users, and
- Global scope for administrators.
This validates the findings obtained through dynamic testing.
Phase 3 Summary
Unlike Phase 2, which focused on resolver robustness and authorization testing, this phase assessed the practical exposure of the GraphQL analytics functionality.
Testing confirmed that:
- Standard users are correctly restricted to account-level analytics.
- Recent transaction data exposes detailed financial metadata for the authenticated account.
- Transaction aggregation features provide analytical insight beyond simple record retrieval.
- Administrator accounts automatically receive institution-wide financial analytics.
- Source code review confirmed that this behavior is implemented intentionally through role-based execution paths.
Although no authorization bypass was demonstrated, the assessment shows that the GraphQL endpoint exposes a high-value analytics interface whose security depends entirely on the integrity of the authentication mechanism. Consequently, weaknesses elsewhere in the authentication layer particularly the application's intentionally vulnerable JWT implementation would significantly increase the impact of this endpoint by granting unrestricted access to organization-wide financial analytics.
Overall Impact
The assessment demonstrated that the GraphQL endpoint exposed significantly more information than necessary for authenticated users. Unrestricted schema introspection enabled complete enumeration of the GraphQL API, while administrator accounts could access application-wide transaction analytics through a single resolver. Although authorization controls successfully prevented ordinary users from accessing other users' transaction summaries, the exposed analytics functionality, combined with GraphQL information disclosure, increases the application's attack surface and would provide valuable financial intelligence if an administrative account were compromised.
Remediation
To strengthen the GraphQL implementation, the following controls should be implemented:
- Disable GraphQL introspection in production environments.
- Enforce query depth and complexity limits to mitigate resource exhaustion attacks.
- Restrict privileged analytics to dedicated administrative interfaces where appropriate.
- Replace dynamically constructed SQL statements with parameterized queries.
- Reduce verbose GraphQL error messages returned to clients.
- Strengthen the underlying JWT authentication mechanism, as GraphQL security depends on trusted authentication.
Conclusion
The GraphQL section demonstrated that while authorization controls correctly restricted standard users to their own transaction data, the endpoint still disclosed valuable information through unrestricted schema introspection and privileged analytics functionality. Source code review also identified unsafe SQL query construction within the resolver, although practical exploitation could not be demonstrated through the exposed GraphQL interface. Overall, the assessment highlights the importance of combining GraphQL-specific hardening measures with secure coding practices to reduce the attack surface and better protect sensitive financial data.
Bill Payment System Assessment
Severity: Critical
Description
The bill payment functionality was assessed to determine whether the application correctly validates user input, enforces business rules, and protects financial transactions against unauthorized manipulation.
Unlike conventional vulnerabilities that focus solely on authentication or access control, payment systems must also preserve transactional integrity. Improper validation, predictable identifiers, and concurrency flaws can allow attackers to manipulate payment operations, bypass financial restrictions, or interfere with backend processing.
During testing, several weaknesses were identified within the bill payment workflow. These included predictable biller identifiers, insufficient input validation, weak business rule enforcement, and a race condition that allowed multiple payments to be processed simultaneously against the same account balance.
Although each issue represents a separate weakness, together they demonstrate that critical financial operations rely heavily on client-supplied values while lacking sufficient server-side validation.
How It Was Discovered
Testing began by interacting with the Bill Payments feature through the application dashboard while intercepting all traffic using Burp Suite.
Several API endpoints responsible for bill payment functionality were identified, including:
GET /api/billers/by-category/{id}
GET /api/bill-payments/history
POST /api/bill-payments/create
The assessment focused on determining:
- Whether billers could be enumerated.
- Whether payment parameters were properly validated;
- Whether business rules were enforced server-side; and
- Whether concurrent payment requests were safely processed.
Enumerating Bill Payment Resources
The first step involved examining how the application exposed bill payment metadata.
A request to retrieve bill categories returned all available payment categories together with sequential numeric identifiers, each mapped to predictable numeric identifiers.
Subsequent requests were then issued to retrieve billers associated with individual categories.
GET /api/billers/by-category/1
Changing only the category identifier successfully returned a different set of billers.
For example:
GET /api/billers/by-category/2
GET /api/billers/by-category/3
GET /api/billers/by-category/4
all produced valid responses containing different biller information.
Requesting a non-existent category returned an empty result rather than an authorization error.
GET /api/billers/by-category/5
This behaviour confirms that biller resources are referenced using predictable sequential identifiers and can be systematically enumerated.
Although biller names are not confidential information, exposing predictable internal identifiers significantly reduces the effort required to map backend resources.
An attacker can automatically enumerate available payment providers, identify valid biller IDs, and use those identifiers during subsequent testing of payment-related endpoints.
Payment Amount Validation Bypass
The payment creation endpoint was then tested to determine whether the server validated monetary values before processing transactions.
The following request was submitted.
POST /api/bill-payments/create
Despite supplying a negative amount, the server processed the request successfully.
The same behaviour was observed for additional edge cases, where '-20', '0', '0.01' where accepted, while a very large value was rejected only because of insufficient balance.
These results indicate that the application performs balance verification but fails to enforce fundamental business rules governing valid payment amounts.
Negative, zero-value, and extremely small payments should be rejected before transaction processing begins.
Instead, the server accepts these values as legitimate financial operations.
Predictable Biller Identifiers
Testing then focused on determining whether biller identifiers followed predictable patterns.
After identifying valid billers through enumeration, the payment request was modified by incrementing the biller identifier.
Additional identifiers produced identical responses.
The responses demonstrate that billers are referenced using sequential numeric identifiers.
Although unauthorized biller access was not observed, the predictable identifier scheme enables straightforward resource enumeration and allows attackers to distinguish valid identifiers from invalid ones.
Race Condition in Bill Payment Processing
The final phase assessed how the application handled concurrent payment requests.
A legitimate payment request was duplicated across thirteen Burp Suite Repeater tabs and executed simultaneously using Send Group (Parallel).
Each request contained identical parameters.
Of the fourteen requests submitted simultaneously:
- 9 completed successfully.
- 5 were rejected due to insufficient funds.
Following execution, the account balance became:
The results demonstrate a classic race condition.
Each request independently verified the available balance before previous transactions completed and updated the account state.
As a result, multiple transactions were authorized against the same available balance, allowing the account to become overdrawn.
This indicates that balance validation and balance updates are not executed as a single atomic transaction.
Impact
Successful exploitation of these weaknesses allows an attacker to:
- Enumerate bill payment resources.
- Manipulate payment values that should be rejected.
- Identify valid biller identifiers.
- Process multiple payments concurrently against the same account balance.
Within a financial application, these weaknesses undermine the integrity of payment processing and increase the risk of fraudulent transactions and inconsistent account balances.
Remediation
The bill payment workflow should enforce strict server-side validation of all payment parameters, reject invalid transaction amounts, replace predictable resource identifiers where appropriate, and perform balance verification and deduction within a single atomic database transaction. Implementing row-level locking or equivalent concurrency controls will prevent multiple concurrent requests from authorizing payments against the same available balance.
Conclusion
The assessment identified multiple weaknesses throughout the bill payment workflow, ranging from predictable resource enumeration to insufficient business rule enforcement and a critical race condition affecting financial transactions. While the enumeration issues primarily facilitate reconnaissance, the payment validation flaws and concurrency vulnerability directly impact transaction integrity, allowing unauthorized payment scenarios that should not be possible within a secure banking application. Together, these findings demonstrate the importance of combining robust input validation with strong transactional consistency controls in financial systems.
Virtual Card Testing
Severity: High
Following the assessment of the Bill Payments module, attention shifted to the Virtual Card functionality. This feature enables customers to create virtual payment cards, fund them directly from their primary account balance, configure spending limits, freeze or unfreeze cards, and review transaction history. Because these operations involve sensitive financial assets, the assessment focused on authorization controls, business logic enforcement, input validation, transaction integrity, and the protection of sensitive payment information.
The objective was to determine whether users could interact only with their own virtual cards and whether the application correctly enforced server-side validation during financial operations. Throughout testing, several critical security weaknesses were identified, including Broken Object Level Authorization (BOLA), Mass Assignment, Business Logic flaws, Sensitive Data Exposure, inadequate server-side validation, and Race Conditions affecting transaction processing.
Discovering the Virtual Card Functionality
Testing began by creating a legitimate virtual card through the application's normal workflow. The request below was intercepted using Burp Suite.
POST /api/virtual-cards/create
The server responded successfully and returned the complete virtual card information.
To understand how the application managed virtual cards after creation, the listing endpoint was examined.
GET /api/virtual-cards
The response returned all virtual cards belonging to the authenticated account together with their internal identifiers, balances, spending limits, status, and complete card information.
During repeated testing, another observation became immediately apparent. Every newly created virtual card received a sequential numeric identifier.
Examples observed throughout testing included, 8551, 8552, 8553.
Although sequential identifiers alone are not necessarily a vulnerability, they dramatically simplify resource enumeration whenever authorization controls are weak. This observation became particularly significant during subsequent authorization testing.
Broken Object Level Authorization (BOLA)
Having identified that virtual cards were referenced using predictable numeric identifiers, the next objective was determining whether the application verified ownership before performing sensitive operations.
The first endpoint tested was the virtual card funding endpoint.
A legitimate funding request appeared as follows:
POST /api/virtual-cards/8551/fund
Rather than targeting the authenticated user's own virtual card, only the card identifier within the URL was modified.
POST /api/virtual-cards/8510/fund
The server accepted the modified request and processed the funding operation successfully.
No authorization error was returned despite the authenticated user not owning the referenced virtual card.
This confirmed that the backend relied solely on the supplied identifier without validating ownership of the requested resource.
Since the funding endpoint lacked proper authorization checks, additional endpoints were tested using the same methodology.
The freeze endpoint was intercepted and the virtual card identifier was changed to another user's card.
POST /api/virtual-cards/8539/toggle-freeze
The application again processed the request successfully.
Repeated testing confirmed that arbitrary virtual cards could be frozen and unfrozen simply by changing the identifier within the request URL. If the targeted card was already frozen, the request restored it to an active state. If it was active, it became frozen.
The absence of object-level authorization across multiple endpoints demonstrated a systemic Broken Object Level Authorization vulnerability throughout the Virtual Card module.
Business Logic and Mass Assignment Weaknesses
After authorization testing, attention shifted toward server-side validation of financial parameters.
The first observation occurred during card creation.
Instead of supplying a positive spending limit, a negative value was submitted.
POST /api/virtual-cards/create
Rather than rejecting the request, the application successfully created the card.
This demonstrated insufficient validation of financial values during virtual card creation.
Testing then focused on determining whether server-side calculations could be influenced by client-controlled parameters.
While reviewing requests to the funding endpoint, an additional parameter named exchange_rate was manually introduced.
POST /api/virtual-cards/4872/fund
Instead of ignoring the unexpected parameter, the application accepted it and used it during processing.
The response clearly shows that the backend trusted a client-supplied exchange rate, producing a negative converted amount.
Financial calculations such as currency conversion should always be derived exclusively by trusted server-side logic. Allowing user-controlled values to influence these calculations introduces significant business logic weaknesses and creates opportunities for financial manipulation.
Sensitive Data Exposure
While reviewing responses from both the card creation and listing endpoints, another issue became immediately apparent.
The API routinely returned complete virtual card information, including:
- Full card number (PAN)
- CVV
- Expiration date
Rather than masking sensitive payment information, the application exposed complete payment credentials within ordinary API responses.
This significantly increases the impact of any account compromise or authorization bypass, since an attacker obtaining access to another user's virtual card immediately gains complete payment credentials.
Discovering a Race Condition in Virtual Card Funding
The final assessment focused on transaction integrity under concurrent execution.
Since funding operations deduct money directly from the user's primary account balance, the funding endpoint was tested for race conditions using Burp Suite Repeater's Send Group (Parallel) functionality.
Fifteen identical funding requests were dispatched simultaneously against the same virtual card.
POST /api/virtual-cards/8551/fund
Only two requests failed with the expected insufficient balance response.
The remaining requests completed successfully.
Once every request had finished processing, the account balance was inspected and had dropped to −670, despite the account not containing sufficient funds to authorize every transaction.
This demonstrates that balance validation and debit operations are performed independently rather than atomically. Multiple concurrent requests were able to validate the same available balance before earlier transactions completed, allowing several funding operations to succeed simultaneously.
Overall Impact
The Virtual Card module exhibited multiple weaknesses affecting authorization, business logic, input validation, transaction integrity, and the protection of sensitive financial information. Broken Object Level Authorization vulnerabilities allowed authenticated users to manipulate virtual cards belonging to other customers by simply modifying resource identifiers. Inadequate server-side validation permitted invalid financial values and client-controlled parameters to influence sensitive operations, while sensitive payment information was unnecessarily exposed through API responses. Finally, race conditions within the funding workflow enabled concurrent transactions to bypass balance validation, resulting in unauthorized overdrafts and inconsistent account balances.
If exploited in a production environment, these weaknesses could allow attackers to manipulate virtual cards owned by other users, interfere with financial transactions, expose sensitive payment credentials, and compromise the integrity of the banking platform.
Remediation
The Virtual Card module should enforce strict object-level authorization on every endpoint that references a virtual card identifier to ensure users can access only resources they own. All financial calculations, including exchange rate conversions, spending limits, and balance updates, should be performed exclusively on the server without trusting client-supplied values. Sensitive payment information such as full card numbers and CVV values should never be returned in standard API responses and should instead be masked in accordance with payment industry best practices. Additionally, robust server-side input validation should reject invalid financial values, and all balance-related operations should be processed atomically through transactions or locking mechanisms to prevent race conditions and maintain financial consistency.
Conclusion
The Virtual Card assessment demonstrated that although the feature provides convenient digital payment capabilities, several core security controls were either absent or insufficiently implemented. Weak authorization checks, inadequate validation, excessive exposure of sensitive information, and concurrency flaws collectively increased the attack surface and allowed unauthorized manipulation of critical financial operations. Addressing these issues requires a defense-in-depth approach that combines proper authorization enforcement, secure business logic implementation, rigorous server-side validation, protection of sensitive payment data, and atomic processing of financial transactions to preserve both the confidentiality and integrity of the platform.
Final Thoughts
Throughout this assessment, VulnBank demonstrated many of the security issues that frequently appear in modern web applications, APIs, and digital banking platforms. While the application intentionally contains vulnerabilities for educational purposes, the scenarios explored throughout this assessment closely resemble weaknesses that have been identified in real-world financial systems.
The testing process uncovered a broad range of security flaws spanning multiple categories, including Authentication flaws, Broken Object Level Authorization (BOLA), Broken Object Property Level Authorization (BOPLA), Mass Assignment, Race Conditions, Business Logic vulnerabilities, GraphQL security misconfigurations, Information Disclosure, Weak Session Management, JWT implementation flaws, Server-Side Request Forgery (SSRF), Cross-Site Scripting (XSS), and inadequate input validation. Individually, each vulnerability presents a security concern. Collectively, however, they demonstrate how seemingly isolated weaknesses can be chained together to achieve far greater impact than any single flaw alone.
One of the most important lessons from this assessment is that modern attacks rarely depend on a single vulnerability. Instead, attackers often combine information disclosure with weak authorization controls, predictable identifiers, business logic flaws, and concurrency issues to compromise sensitive functionality. A leaked account number may enable BOLA, BOLA may expose financial information, race conditions may bypass balance validation; excessive debug information may simplify further exploitation. Security failures are often interconnected rather than independent.
The assessment also highlights that traditional input validation alone is no longer sufficient. Financial applications must implement security controls at every layer of the application stack. Authorization must be enforced on every object reference, business rules must be validated server-side, sensitive information should never be unnecessarily exposed, and financial transactions must execute atomically to prevent concurrency-related abuse. Modern banking platforms require defense-in-depth, where authentication, authorization, input validation, transaction integrity, monitoring, and secure error handling work together rather than relying on any single security mechanism.
Equally important is the role of secure software development practices. Many of the identified vulnerabilities could have been prevented through secure coding standards, comprehensive code reviews, automated security testing, threat modeling during development, and continuous penetration testing before deployment. Security should not be treated as a final testing phase but as an integral part of the software development lifecycle.
Although VulnBank is intentionally vulnerable, the techniques used throughout this assessment reflect the same methodology employed during professional penetration tests. Every finding began with reconnaissance, followed by careful observation of application behavior, hypothesis-driven testing, validation of exploitability, assessment of impact, and finally the recommendation of practical remediation measures. This structured approach ensures that reported vulnerabilities are both technically accurate and operationally meaningful.
For developers, this assessment serves as a reminder that seemingly minor implementation decisions, such as exposing debug information, trusting client-controlled parameters, or failing to verify resource ownership can have significant security consequences. For security professionals, it reinforces the importance of understanding application logic rather than relying solely on automated vulnerability scanners. Many of the highest-impact findings identified during this assessment required manual analysis, careful experimentation, and an understanding of how the application's business processes operated.
Ultimately, application security is not achieved by eliminating individual vulnerabilities one at a time, it is achieved by designing systems that remain resilient even when individual components fail. Building secure financial applications requires secure architecture, continuous validation, layered defenses, and a security-first mindset throughout the entire development lifecycle.
Before closing, I would like to express my sincere appreciation to Al-Amir Badmus, the creator of VulnBank. Developing an intentionally vulnerable banking application that accurately reflects modern web application security challenges is no small feat. VulnBank provided an excellent hands-on environment for exploring real-world vulnerabilities, practicing offensive security techniques, and understanding how seemingly minor weaknesses can combine into critical security risks. Platforms like this play an important role in helping aspiring penetration testers, developers, and security engineers gain practical experience in a safe and controlled environment.
Thank you for taking the time to read through this assessment. I hope the methodology, findings, exploitation process, and remediation strategies documented throughout this walkthrough provide practical insight into modern application security testing. Whether you're a developer looking to build more secure applications, a security engineer strengthening defensive controls, or an aspiring penetration tester developing your skills, my hope is that this assessment encourages you to look beyond automated tools, think critically about application logic, and approach security testing with curiosity, discipline, and a commitment to responsible disclosure.
Security is not achieved by simply fixing vulnerabilities after they're discovered, it begins with understanding how attackers think, validating assumptions through continuous testing, and building systems that remain resilient even when those assumptions are challenged.
Every vulnerability documented in this assessment represents a lesson. The goal of offensive security isn't to break applications, it's to help build stronger ones.
Security is a journey of continuous learning. Every assessment, every vulnerability discovered, and every remediation implemented contributes to building safer software and a more secure digital ecosystem. Keep learning, keep testing responsibly, and keep building secure systems.
If you found this walkthrough helpful, feel free to connect with me. I enjoy documenting practical security research, vulnerability discoveries, and penetration testing methodologies to help both security professionals and developers build more secure applications.
LinkedIn: Link
GitHub: Link
X (Twitter): Link
More hands-on assessments, API security research, mobile and web application testing, and CTF write-ups will be published soon.