Case 04 December 2025 — January 2026 Web Application Penetration Test

Web Application Security Assessment
Plia

Full-scope web application penetration test of a SaaS platform for asset management and compliance workflows. Eight findings identified including Stored XSS, high-severity CVEs, and session management flaws. One finding challenged by the client's AI reviewer — and successfully defended.

OSSTMM v3 Web App Pentest Session Management XSS CVE Analysis RAV Scoring
7.5
Highest CVSS
8
Total findings
88.9
RAV score
1
Finding defended vs AI

Scope, targets, and methodology

Plia is a SaaS platform serving asset management and compliance teams, providing questionnaire workflows, counterparty management, and reporting tools. The engagement covered external web application testing including authentication mechanisms, session management, API endpoint analysis, and profile management functionality, conducted under OSSTMM v3 methodology.

Primary target
app.plia.com
AWS ELB · eu-west-1
Methodology
OSSTMM v3 — External web application and authentication testing. RAV scoring and Attack Surface Security Matrix.
Technology stack
Rails backend, Nginx 1.26.3, React/Backbone/Marionette frontend, moment.js 2.9.0, underscore.js 1.6.0, AWS ALB, Cloudflare CDN.
Scope boundaries
Application layer only. No infrastructure, IP-level, cloud resource, or npm vulnerability testing conducted.

Initial discovery confirmed the application was fronted by an AWS Elastic Load Balancer with dynamically rotating IPs. No WAF integration was detected during testing. HSTS was enforced with a two-year max-age including subdomains. Zone transfer testing returned no vulnerabilities.

Security posture measured under OSSTMM

The RAV (Results, Actual Value) score measures the balance between operations, vulnerabilities, and controls — where 100rav represents perfect balance. An attack surface above 10% is considered problematic reactive security. Above 20% is considered likely already breached.

Current security
88.9
ravs
Attack surface
11.08%
problematic threshold
After all fixes
98.6
attack surface 1.01%

The current score places Plia just above the 10% problematic threshold — indicating the application has meaningful controls in place but carries compounding risk from the identified session management and injection vulnerabilities. Resolving all findings reduces the attack surface from 11.08% to 1.01%.

Eight vulnerabilities across authentication, sessions, and injection

High

CVE-2022-24785 — moment.js 2.9.0 path traversal

CVSS 7.5

The application uses moment.js version 2.9.0, which is affected by a path traversal vulnerability in its locale loading functionality. An attacker could manipulate locale parameters to access files outside the intended directory. While the specific exploit parameters were not identified as active in this application's implementation, the library version itself carries high-severity risk and should be updated immediately.

AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:N
High

CVE-2021-23358 — underscore.js 1.6.0 arbitrary code execution

CVSS 7.2

The application uses underscore.js version 1.6.0, which is vulnerable to arbitrary code execution via the template function when processing user-controlled input. Testing did not find evidence that underscore.template was called with user input in this application, meaning the CVE was not actively exploitable during the engagement. However, the outdated library version represents ongoing risk and the vulnerable code path exists in the codebase.

AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:H
Medium

Stored XSS in user profile management interface

CVSS 5.4

The Name field in the profile management interface at app.plia.com/#management/profile was not sanitized or encoded before rendering. An authenticated user could inject arbitrary JavaScript payloads via the Name field, which were then persistently stored and executed in the browser context of any other authenticated user who accessed their own profile management page.

A proof-of-concept payload was injected using Pete Herzog's test account. The injected payload loaded an external image and triggered a JavaScript alert displaying "st4rry" — confirmed executing in another authenticated user's browser session, demonstrating full cross-user script execution.

AV:N/AC:L/PR:L/UI:R/S:C/C:L/I:L/A:N — Cross-user payload execution confirmed.
Medium

Session authentication relies solely on reusable _session_id

CVSS 5.3

The application uses three cookies for session handling: plia.apiKey, plia.apiToken, and _session_id. Testing confirmed that only _session_id was required for authentication — arbitrary or invalid values for apiKey and apiToken did not affect authorization. The server returned authenticated user data based solely on _session_id, indicating the additional tokens provided no meaningful security benefit.

This means the application's apparent multi-factor session binding was not enforced server-side, reducing the effective authentication surface to a single reusable identifier.

AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N
Medium

Session re-hydration via /user_session.json with reused cookies

CVSS 5.3

An authenticated session cookie set was exported from a browser after login and imported into a fresh, unauthenticated browser instance. Without performing any authentication, the /user_session.json endpoint returned full authenticated user details and issued a new session identifier. This behavior persisted even after the original session should have expired, enabling session replay attacks using previously captured cookies.

AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N
Medium

User enumeration via differential API responses

CVSS 5.3

The /sso_request.json endpoint responded differently for valid versus non-existent users — returning 0 for valid accounts and null for non-existent ones. A simple Bash enumeration script was used to confirm valid account existence for multiple email addresses without any rate limiting or authentication required. The lack of response normalization and absence of rate limiting made this fully exploitable as written.

AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N
Low

Global session freeze on abuse detection — DoS risk

CVSS 3.7

When the application detected scanning or abusive activity from a session, it froze all active sessions associated with that user account for approximately 20 minutes — not just the abusing session. This overly broad control means an attacker could intentionally trigger the abuse detection mechanism against a target account, causing a denial of service for the legitimate user while the lockout persists.

AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:L
Info

SSO identifier decoupled from authentication flow

The SSO step at /sso_request.json did not perform authentication or identity validation. Both valid and invalid identifiers were accepted without affecting authentication outcomes, reducing the security value of the SSO step and indirectly contributing to the user enumeration surface. The SSO identifier was not bound to the subsequent authentication flow.

When the client's AI reviewer disagreed — and why it was wrong

After the report was delivered, the client reviewed the findings using an AI assistant. One finding — the session authentication weakness relating to the HMAC-based authentication design — was flagged by the AI as incorrect. The AI argued that the existing HMAC design using api_key and api_signature was secure by design, and that the pentest observation about cookie behavior was actually correct application behavior.

Rather than accepting the challenge or withdrawing the finding, the technical argument was laid out clearly and directly to the client's mentor.

AI Review

The HMAC-based authentication using api_key and api_signature is secure by design. The observation that plia.apiKey and plia.apiToken cookies don't affect authorization is correct behavior — the backend validates via query parameters using HMAC signatures, not cookies. No code changes needed.

Starry

The HMAC may be secured by hashing, but in this case: any of those values (api_key, api_signature, apiKey, apiToken) can be anything and still make a successful request as long as the session_id is correct. If the backend validates via the session_id alone and the other parameters are effectively ignored, that is a failure of resilience control — the combination requirements are not actually required. From the outside, if the system accepts a request as long as the right session_id is presented, that is a problem regardless of what the HMAC design intends.

Outcome

The developer reviewed the argument and confirmed: "The penetration tester is correct on both counts." The HMAC validation passes but the session_id alone effectively controls authorization. The finding was validated. The client passed the message to Pete Herzog: "Tell Starry good work."

What this moment represents

Security testing is not just about running tools. It is about understanding what findings mean at a systems level — well enough to explain and defend them under scrutiny, including against an AI reviewer backed by the client's own developer. The finding stood because the reasoning was sound.

Eight findings. All documented. All defended.

# Finding Severity CVSS
1 Session auth relies solely on reusable _session_id Medium 5.3
2 Session re-hydration via /user_session.json Medium 5.3
3 User enumeration via differential API responses Medium 5.3
4 Global session freeze on abuse detection — DoS risk Low 3.7
5 SSO identifier decoupled from authentication flow Info
6 CVE-2022-24785 — moment.js 2.9.0 High 7.5
7 CVE-2021-23358 — underscore.js 1.6.0 High 7.2
8 Stored XSS in user profile management interface Medium 5.4
Client validation

Following delivery and technical review — including a challenge from the client's AI reviewer that was successfully rebutted — the client confirmed the findings and passed direct feedback through Pete Herzog: "Tell Starry good work."

← Previous: Agentic Email System Get in touch →