{"id":766,"date":"2025-04-09T16:40:32","date_gmt":"2025-04-09T14:40:32","guid":{"rendered":"https:\/\/www.cloudtango.net\/blog\/?p=766"},"modified":"2025-04-09T16:40:32","modified_gmt":"2025-04-09T14:40:32","slug":"from-stored-xss-to-account-takeover-how-vulnerabilities-combine-into-major-risks","status":"publish","type":"post","link":"https:\/\/www.cloudtango.net\/blog\/2025\/04\/09\/from-stored-xss-to-account-takeover-how-vulnerabilities-combine-into-major-risks\/","title":{"rendered":"From Stored XSS to Account Takeover: How Vulnerabilities Combine into Major Risks"},"content":{"rendered":"<p>Recently, during a web application penetration test, we uncovered a situation where a combination of seemingly small vulnerabilities led to a major security breach. We found a stored Cross-Site Scripting (XSS) flaw in the comments section of a support ticketing system. This, paired with weak session security, allowed us to hijack a user\u2019s account.<\/p>\n<p>While automated scanning tools typically miss these complex vulnerability chains, manual penetration testing like ours can reveal these hidden risks. In this article, we\u2019ll break down how the attack worked and offer some practical advice on how to protect your application from similar threats.<\/p>\n<h2>The Vulnerabilities in Play<\/h2>\n<p>Let\u2019s first understand the vulnerabilities involved:<\/p>\n<p><strong>1. Stored Cross-Site Scripting (XSS):<\/strong><\/p>\n<p>Stored XSS happens when a malicious script gets permanently stored on a web server, often through user-generated content like comments or messages. When other users view this content, the script runs in their browser, allowing attackers to execute unwanted actions or steal sensitive information remotely.<\/p>\n<p><strong>2. Session Tokens Exposed in URLs:&nbsp;<\/strong><\/p>\n<p>Session tokens help confirm user identity without forcing them to log in repeatedly. However, putting these tokens directly into URLs increases the risk of accidental exposure through browser history, logs, or even malicious interception. Attackers that capture these tokens can impersonate legitimate users, especially when they&#8217;re obfuscated using simple methods like Base64 encoding.<\/p>\n<p><strong>3. Session Hijacking:&nbsp;<\/strong><\/p>\n<p>Session hijacking is when an attacker steals a user&#8217;s login information (session token) to access their account without knowing their password.<\/p>\n<h3>Exploit Walkthrough:<\/h3>\n<p>Here\u2019s how we combined these vulnerabilities to take over an account, along with the evidence we provided to the client:<\/p>\n<p><strong>Step 1 \u2013 Stored XSS identification via Support Comments&nbsp;<\/strong><\/p>\n<p>While testing the support ticket feature, I tried some basic XSS payloads to see if the system properly validated inputs. Initially, input validation mechanisms stripped certain special characters required for typical HTML-based XSS payloads.<\/p>\n<p>However, by using obfuscation and bypass techniques, specifically the JavaScript function&nbsp;<strong>String.fromCharCode()<\/strong>, I successfully bypassed the filters, confirming the presence of stored XSS.<\/p>\n<p><strong>Step 2 \u2013 Base-64 Encoded Session Token in URL identification&nbsp;<\/strong><\/p>\n<p>Next, I noticed URLs associated with viewing support tickets included an unusual base64-encoded parameter. After decoding, it became clear that this encoded string was actually the user&#8217;s PHP session token \u2014an important piece of information that could grant access to the user\u2019s account.<\/p>\n<p>The image below displays the identified Base-64 encoded value being decoded to present the PHPSession cookie value utilised to identify the active session.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.cybaverse.co.uk\/hs-fs\/hubfs\/Base-64%20encoded%20value%20being%20decoded%20to%20present%20the%20PHPSession%20cookie%20value-png.png?width=724&amp;height=312&amp;name=Base-64%20encoded%20value%20being%20decoded%20to%20present%20the%20PHPSession%20cookie%20value-png.png\" alt=\"Base-64 encoded value being decoded to present the PHPSession cookie value\" width=\"724\" height=\"312\"><\/p>\n<p><strong><span data-contrast=\"auto\">Step 3 &#8211; Using Stored XSS to Capture the Token<\/span><\/strong><span data-ccp-props=\"{}\">&nbsp;<\/span><\/p>\n<p><span data-contrast=\"auto\">Knowing the session token was embedded in the URL, I crafted an XSS payload that sent the victim\u2019s document location (the URL) to a server I controlled. When the victim clicked on the ticket containing the malicious code, it sent their session token straight to my server.<\/span><\/p>\n<p><span data-contrast=\"auto\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone\" src=\"https:\/\/www.cybaverse.co.uk\/hs-fs\/hubfs\/Session%20Token-png.png?width=735&amp;height=218&amp;name=Session%20Token-png.png\" alt=\"Session Token\" width=\"735\" height=\"218\"><\/span><\/p>\n<p><strong><span data-contrast=\"auto\">Step 4 \u2013 Decode Captured Token&nbsp;<\/span><\/strong><span data-ccp-props=\"{}\">&nbsp;<\/span><\/p>\n<p><span data-contrast=\"auto\">Once I had the session token, I simply decoded the Base64 string to retrieve the user&#8217;s valid session cookie. This gave me full access to the victim&#8217;s session without needing their password.<\/span><span data-ccp-props=\"{}\">&nbsp;<\/span><\/p>\n<p><span data-ccp-props=\"{}\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.cybaverse.co.uk\/hs-fs\/hubfs\/Users%20Valid%20Session%20Cookie-png.png?width=541&amp;height=258&amp;name=Users%20Valid%20Session%20Cookie-png.png\" alt=\"Users Valid Session Cookie\" width=\"541\" height=\"258\"><\/span><\/p>\n<p><strong><span data-contrast=\"auto\">Step 5 \u2013 Hijacking the Session<\/span><\/strong><span data-ccp-props=\"{}\">&nbsp;<\/span><\/p>\n<p><span data-contrast=\"auto\">Once I had the stolen session token, I simply replaced my session cookie with it, giving me full access to the victim&#8217;s account. Since there was no re-authentication required, I could do things like disable Multi-Factor Authentication (MFA), change the registered phone number, update the email address, and reset the password.<\/span><span data-ccp-props=\"{}\">&nbsp;<\/span><\/p>\n<p><span data-contrast=\"auto\">Ultimately, this permitted me to fully hijack the account, locking out the original user entirely, as demonstrated by the image below.&nbsp;<\/span><\/p>\n<p><span data-contrast=\"auto\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.cybaverse.co.uk\/hs-fs\/hubfs\/Account%20fully%20hijacked-png.png?width=589&amp;height=321&amp;name=Account%20fully%20hijacked-png.png\" alt=\"Account Fully Hijacked\" width=\"589\" height=\"321\"><\/span><\/p>\n<h3><strong><span data-contrast=\"auto\">Real-World Impact<\/span><\/strong><\/h3>\n<p><span data-contrast=\"auto\">You might be wondering what the real-world impact would be if this issue wasn\u2019t identified and addressed within a controlled testing environment prior to public release. In short, this vulnerability could result in the complete compromise of any high-level or administrative account accessing support tickets. Attackers exploiting this could gain unrestricted access to sensitive customer data, perform unauthorised account modifications, escalate privileges, and potentially cause severe financial, regulatory, and reputational damage to the affected organisation.<\/span><span data-ccp-props=\"{}\">&nbsp;<\/span><\/p>\n<h3><strong><span data-contrast=\"auto\">Mitigation Considerations<\/span><\/strong><span data-ccp-props=\"{}\">&nbsp;<\/span><\/h3>\n<p><span data-contrast=\"auto\">To prevent attacks like this, here are a few key things to keep in mind:<\/span><\/p>\n<p><strong><span data-contrast=\"auto\">1. Strict Input Validation, Sanitisation, and Output Encoding<\/span><\/strong><\/p>\n<ul>\n<li><span data-contrast=\"auto\">Validation ensures that inputs meet expected formats.<\/span><\/li>\n<li><span data-contrast=\"auto\">Sanitisation removes harmful characters or code before storing them.<\/span><\/li>\n<li><span data-contrast=\"auto\">Properly encode or escape special characters to avoid injection attacks during output rendering.<\/span><\/li>\n<li><span data-contrast=\"auto\">Implement Content Security Policy (CSP) to mitigate XSS vulnerabilities.<\/span><\/li>\n<\/ul>\n<p><strong><span data-contrast=\"auto\">2. Secure Session Handling<\/span><\/strong><\/p>\n<ul>\n<li><span data-contrast=\"auto\">Never expose session tokens in URLs\u2014whether they are encoded or not.<\/span><\/li>\n<li><span data-contrast=\"auto\">Use secure, randomly generated tokens and store them in HttpOnly cookies.<\/span><span data-ccp-props=\"{}\">&nbsp;<\/span><\/li>\n<\/ul>\n<p><strong><span data-contrast=\"auto\">3. Session Management Best Practices<\/span><\/strong><\/p>\n<ul>\n<li><span data-contrast=\"auto\">Regularly invalidate session tokens after logout or periods of inactivity.<\/span><\/li>\n<li><span data-contrast=\"auto\">Use secure, industry-standard frameworks to manage authentication and session states.<\/span><span data-ccp-props=\"{}\">&nbsp;<\/span><\/li>\n<\/ul>\n<p><strong><span data-contrast=\"auto\">4. Regular Penetration Testing<\/span><\/strong><\/p>\n<ul>\n<li><span data-contrast=\"auto\">Automated tools are useful but can miss complex vulnerability chains. Regular, manual penetration testing can uncover these hidden risks.<\/span><\/li>\n<li><span data-contrast=\"auto\">Educate your development team on how to recognise and prevent vulnerabilities during coding.<\/span><\/li>\n<\/ul>\n<h2>Final Thoughts<\/h2>\n<p><span data-contrast=\"auto\">Even vulnerabilities that might seem small\u2014like stored XSS or insecure session token handling\u2014can combine to create major security risks. Manual penetration testing is crucial for uncovering these hidden flaws and strengthening your overall security posture. It\u2019s also important to remember that even reflected XSS can bypass protections such as anti-CSRF measures, allowing attackers to execute unintended actions through carefully crafted payloads. By staying ahead of these risks, your organisation can avoid costly breaches and protect sensitive data from falling into the wrong hands.<\/span><span data-ccp-props=\"{}\">&nbsp;<\/span><\/p>\n<p><span data-contrast=\"auto\">At CybaVerse, our award-winning penetration testing services go beyond automated scans, proactively identifying risks before attackers can exploit them. If you\u2019d like to keep your applications secure, don\u2019t hesitate to get in touch and see how we can help.<\/span><span data-ccp-props=\"{}\">&nbsp;<\/span><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Recently, during a web application penetration test, we uncovered a situation where a combination of seemingly small vulnerabilities led to a major security breach. We found a stored Cross-Site Scripting (XSS) flaw in the comments section of a support ticketing system. This, paired with weak session security, allowed us to hijack a user\u2019s account. While[\u2026] <a class=\"read-more\" href=\"https:\/\/www.cloudtango.net\/blog\/2025\/04\/09\/from-stored-xss-to-account-takeover-how-vulnerabilities-combine-into-major-risks\/\">Read<svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" enable-background=\"new 0 0 24 24\" height=\"16px\" viewBox=\"0 0 24 24\" width=\"16px\" fill=\"#091926\"><rect fill=\"none\" height=\"16\" width=\"16\"\/><path d=\"M14.29,5.71L14.29,5.71c-0.39,0.39-0.39,1.02,0,1.41L18.17,11H3c-0.55,0-1,0.45-1,1v0c0,0.55,0.45,1,1,1h15.18l-3.88,3.88 c-0.39,0.39-0.39,1.02,0,1.41l0,0c0.39,0.39,1.02,0.39,1.41,0l5.59-5.59c0.39-0.39,0.39-1.02,0-1.41L15.7,5.71 C15.32,5.32,14.68,5.32,14.29,5.71z\"\/><\/svg><\/a><\/p>\n","protected":false},"author":3,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[7],"tags":[],"class_list":["post-766","post","type-post","status-publish","format-standard","hentry","category-cybersecurity"],"_links":{"self":[{"href":"https:\/\/www.cloudtango.net\/blog\/wp-json\/wp\/v2\/posts\/766","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.cloudtango.net\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.cloudtango.net\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.cloudtango.net\/blog\/wp-json\/wp\/v2\/users\/3"}],"replies":[{"embeddable":true,"href":"https:\/\/www.cloudtango.net\/blog\/wp-json\/wp\/v2\/comments?post=766"}],"version-history":[{"count":2,"href":"https:\/\/www.cloudtango.net\/blog\/wp-json\/wp\/v2\/posts\/766\/revisions"}],"predecessor-version":[{"id":768,"href":"https:\/\/www.cloudtango.net\/blog\/wp-json\/wp\/v2\/posts\/766\/revisions\/768"}],"wp:attachment":[{"href":"https:\/\/www.cloudtango.net\/blog\/wp-json\/wp\/v2\/media?parent=766"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.cloudtango.net\/blog\/wp-json\/wp\/v2\/categories?post=766"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.cloudtango.net\/blog\/wp-json\/wp\/v2\/tags?post=766"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}