Skip to content
Field Detail
Platform PortSwigger Web Security Academy
Type Prototype Pollution / DOM XSS
Difficulty Practitioner
Objective Find an alternative URL-based pollution source using dot notation, identify an eval() gadget via the sequence property, and trigger alert()

DOM XSS via an Alternative Prototype Pollution Vector

Testing bracket notation first:

/?__proto__[tetoProperty]=miku
Screenshot

Console returned undefined — bracket notation was sanitized. Trying dot notation:

/?__proto__.tetoProperty=miku
Screenshot
Object.prototype
// Object { tetoProperty: "miku", … }

Dot notation worked. __proto__[x] and __proto__.x are semantically equivalent in JavaScript but parsed differently by URL parsers and sanitization code — if one notation is blocked, always try the other.

Browsing to searchLoggerAlternative.js in DevTools revealed an eval() gadget:

let a = manager.sequence || 1;
manager.sequence = a + 1;
eval('if(manager && manager.sequence){ manager.macro('+manager.sequence+') }');

manager only defines params and macro, not sequence. If sequence isn't an own property, the engine falls through to the prototype. And eval() with manager.sequence concatenated directly into the expression string is a direct code injection sink — whatever we inject into sequence via the prototype becomes arbitrary JavaScript.

The a + 1 line matters: let a = manager.sequence || 1 picks up our prototype value; manager.sequence = a + 1 creates an own property with our payload plus 1 appended (string concatenation when a is a string).

First attempt:

/?__proto__.sequence=alert(1)

A SyntaxError appeared — not an alert. Setting a breakpoint on the eval line showed the actual sequence value:

Screenshot
sequence: "alert(1)1"

The appended 1 broke the syntax: manager.macro(alert(1)1) is invalid. Debugging the injection context before tuning the payload is always faster than guessing — the breakpoint made the fix obvious immediately.

Arithmetic operators absorb appended characters into a valid expression. Using - to neutralize the trailing 1:

/?__proto__.sequence=alert(1)-

a + 1 now produces "alert(1)-1", and the eval string becomes:

eval('if(manager && manager.sequence){ manager.macro(alert(1)-1) }')

alert(1)-1 is valid JavaScript — alert(1) executes, then undefined - 1 evaluates to NaN. No syntax error.

Screenshot

Alert fired.

Lab solved

Resources