
I help early-stage SaaS founders secure their applications through manual penetration testing and clear, actionable reports.
I’m Aryaman Malhotra, a cybersecurity researcher focused on web application security and vulnerability discovery.I actively participate in Capture The Flag (CTF) competitions and security research, applying offensive security techniques to identify real-world vulnerabilities in modern web applications.My goal is to help organizations identify and fix security weaknesses before they can be exploited.
Testing web applications for vulnerabilities such as SQL injection, cross-site scripting (XSS), broken authentication, and access control flaws.
Assessing APIs for authentication issues, improper authorization, and other vulnerabilities that may expose sensitive data.
Systematic identification and analysis of security weaknesses across applications and services.
Understanding the target application, attack surface, and potential entry points.
Manual testing combined with automated tools to identify security weaknesses.
Safely validating vulnerabilities to determine their real-world impact.
Providing clear documentation of vulnerabilities along with remediation guidance.
Target: crAPI (Completely Ridiculous API) — an intentionally vulnerable application simulating a real-world
e-commerce backend
Category: Broken Object Level Authorization (BOLA) — OWASP API Security Top 10: API1
Severity: High
Vulnerability Summary
An authenticated user could access order data belonging to other users by simply modifying the order ID in an API request. The API performed no server-side check to verify whether the requesting user was the legitimate owner of the requested object, making unauthorized data access trivial.
Environment
crAPI is an intentionally vulnerable API designed to simulate real-world API security flaws. All testing was performed in an isolated local environment. No real user data was accessed or affected.
Steps to Reproduce
1. Authenticate as a normal user
Log in to the application and obtain a valid session token.
2. Intercept a legitimate request
Using Burp Suite, intercept the API request that fetches a user-specific order. The request looks like this:
GET /workshop/api/shop/orders/1124
Authorization: Bearer <your_token>
3. Modify the object ID
Change the order ID in the request to an ID belonging to another user:
GET /workshop/api/shop/orders/1125
Authorization: Bearer <your_token>
4. Observe the response
The API returns full order details belonging to the other user — no authorization error, no ownership check, no additional privileges required.
Impact
In a real production environment, this vulnerability would allow an attacker to:
• Access other users' order history and personal information
• Enumerate all orders across the platform by iterating IDs
• Tamper with or manipulate objects belonging to other users
• Create serious GDPR/compliance exposure and reputational damage for the businessNo elevated privileges or complex exploitation are required — any authenticated user can exploit this.
A systemic pattern of similar authorization gaps was observed across multiple object access endpoints, indicating this is an architectural issue rather than an isolated oversight.
Root Cause
The API trusted client-supplied object IDs without enforcing server-side ownership validation. Authorization was assumed rather than explicitly verified — a pattern that consistently produces BOLA vulnerabilities at scale.
Remediation
• Enforce strict server-side authorization checks on every object access request
• Validate that the authenticated user is the legitimate owner of the requested resource before returning data
• Never rely solely on client-supplied IDs for access control decisions
• Apply consistent ownership checks across all object endpoints, not just high-visibility ones
Target: PortSwigger Web Security Academy — E-commerce checkout lab
Category: Mass Assignment / Broken Object Property Level Authorization — OWASP API Security Top 10: API3:2023
Severity: Medium
Vulnerability Summary
An API endpoint accepted undocumented parameters that were never intended to be user-controlled. By injecting a discount field into the checkout request, it was possible to apply a 100% discount and complete a purchase worth $1,337 at zero cost. No authentication bypass or elevated privileges were required.
Environment
PortSwigger Web Security Academy lab simulating a real-world e-commerce checkout flow. All testing was performed in an isolated lab environment. No real systems or transactions were affected.
Discovery
While intercepting the checkout flow in Burp Suite, the GET request loading the checkout page returned this response structure:
{
"chosen_discount": {
"percentage": 0
},
"chosen_products": [...]
}
A discount field existed in the system — set to 0% by default — but was absent from the POST request the UI sent when placing an order:
{
"chosen_products": [
{
"product_id": "1",
"quantity": 1
}
]
}
The discrepancy raised a direct question: if the discount field exists in the data model but isn't sent in the order request, what happens if it's added manually?
Steps to Reproduce
1. Authenticate and add item to cart
Log in as a normal user and add the target product to the cart.
2. Intercept the checkout POST request
Using Burp Suite, intercept the order placement request:
POST /api/checkout
{
"chosen_products": [
{
"product_id": "1",
"quantity": 1
}
]
}
3. Inject the discount parameter
Modify the request to include the discount field observed in the GET response:
POST /api/checkout
{
"chosen_discount": {
"percentage": 100
},
"chosen_products": [
{
"product_id": "1",
"quantity": 1
}
]
}
4. Forward the request
The API accepted the modified request, applied the 100% discount, and completed the order. No validation error. No authorization check.
Root Cause
The API accepted the entire incoming request object and mapped it directly to the data model without validating which fields were permitted. The development assumption — that users wouldn't send fields absent from the UI form — was never enforced at the API level.
This is a classic Mass Assignment pattern: the discount field was never meant to be user-controlled, but no explicit allowlist prevented it from being submitted and processed.
Impact
In a real production environment, this vulnerability class enables:
• Price manipulation in e-commerce checkouts
• Unauthorized discount or coupon application
• Privilege escalation by injecting fields like is_admin.
• Bypassing payment verification stepsMass assignment appears regularly in real-world bug bounties — not because it's sophisticated, but because it's easy to overlook when building features quickly and letting frameworks map entire request objects to data models.
Remediation• Define an explicit allowlist of accepted fields for every API endpoint
• Reject or strip any parameters not on the allowlist before processing
• Never rely on UI constraints as a substitute for server-side input validation
• Audit all endpoints that accept JSON bodies for unintended parameter acceptance
Target: crAPI (Completely Ridiculous API) — an intentionally vulnerable API simulating real-world authentication flows
Category: Broken Authentication — OWASP Top 10: A07:2021 | OWASP API Security Top 10: API2:2023
Severity: Critical
Vulnerability Summary
The API accepted JWT tokens without properly validating their cryptographic signature. By decoding the token payload, modifying the role claim from user to admin, and re-encoding it, full administrative access was obtained. The server performed no integrity check — it trusted whatever token was sent.
Environment
crAPI is an intentionally vulnerable API designed to simulate real-world API security flaws. All testing was performed in an isolated local environment. No real user data was accessed or affected.
How JWT Authentication Is Supposed to Work
A JWT token has three parts:
header.payload.signature
The signature is cryptographic proof that the header and payload haven't been modified. A correct implementation:1. Extracts the token from the request
2. Verifies the signature matches the header + payload using the server's secret key
3. If signature fails — rejects the request immediately
4. If signature passes — checks expiration, validates claims, proceedsThe signature is the entire point. Without validating it, the token is just a base64-encoded JSON object anyone can edit.
Steps to Reproduce
1. Authenticate as a normal user
Log in and capture the JWT token issued by the API.
2. Decode the token payload
The payload is base64-encoded, not encrypted. Decode it to reveal the claims:
{
"role": "user",
"email": "[email protected]",
"sub": "124"
}
3. Modify the role claim
Change "role": "user" to "role": "admin"— that's 3 characters changed.
4. Re-encode and send
Base64-encode the modified payload, reconstruct the token with the original header and signature, and send it in the next request.
5. Observe the response
The API accepted the modified token without any validation error. Full admin access granted — no cryptographic check, no signature mismatch error, no rejection.
Impact
With admin access obtained via token manipulation:• All user accounts were visible and modifiable
• Privileged admin endpoints were fully accessible
• Any user could be impersonated by changing the email field
• Tokens could be crafted for users that don't existIn a real production environment — particularly e-commerce or fintech — this vulnerability enables mass data exfiltration, fraudulent transactions, account takeover at scale, and complete platform compromise.
Root Cause
The API was checking that tokens were present and not expired, but was not validating the cryptographic signature. This meant the server had no way to detect that the token had been tampered with after issuance.
This falls into a broader category of JWT misconfigurations seen regularly in production systems:• Accepting tokens with "alg": "none"— no signature required at all
• Not validating signatures (as demonstrated here)
• Using weak secrets that can be brute-forced offline
• Accepting tokens signed with a public key when a private key should be required
Remediation• Always validate the JWT signature on every request before trusting any claims
• Use a strong, randomly generated secret key — never a dictionary word or default value
• Explicitly reject tokens with "alg": "none" at the library or middleware level
• Do not trust client-supplied claims (role, is_admin, permissions) without signature verification
• Use a well-maintained JWT library — never implement JWT validation manually