Injecting a uniquely named property and observing it appear in a second, unrelated request is the confirming artifact - no gadget chain runs during the probe.
Prototype pollution is the JavaScript bug where writing to one object quietly rewrites the defaults for all of them. An attacker-controlled key reaches Object.prototype, and suddenly every object in the process inherits a property it never set. Confirming it does not require running a gadget or crashing anything - a property oracle shows the pollution took, and a second, unrelated request proves it stuck.
The usual entry points are recursive merge utilities - lodash.merge, jQuery.extend, defaults-deep - that copy attacker JSON into a target object key by key. If the input contains __proto__ (or constructor.prototype) and the merge does not guard against it, the write lands on the shared prototype instead of the instance.
Confirmation uses a benign marker, not an exploit. Send a request whose merge path carries a uniquely named property on __proto__, then make a second, unrelated request and check whether that property is now present where it has no business being. Its appearance is the confirming artifact; no privileged flag is set, no gadget runs.
# 1) pollute with a uniquely named, harmless property POST /api/profile {"__proto__": {"_apNonce_62615533": true}} # 2) a DIFFERENT request whose response reflects object defaults GET /api/settings 200 {... "_apNonce_62615533": true ...} # the nonce leaked across requests == confirmed
Pollution on its own is a primitive; the number comes from what reads the polluted property in the running app. A property that flips a boolean checked by an authorization function is privilege escalation. One that lands in a template option or a command-builder default can reach XSS or command execution. One that nothing sensitive ever reads is low. We rate the confirmed sink, not the write.
| Polluted property reaches | Consequence | Rating |
|---|---|---|
| An authorization boolean | Privilege escalation | Critical |
| A template / render option | Server-side or DOM XSS | High - Critical |
| A child-process default | Argument or command injection | Critical |
| Nothing security-relevant | Inert default change | Low |
Server-side pollution in Node must surface in an HTTP response or a measurable side channel - a debug endpoint, a serialized default, a timing shift - because there is no DOM to read. Client-side pollution shows up in the page. The engine picks the oracle that fits the runtime rather than assuming a browser is present.
Freeze prototypes, reject __proto__ keys at the merge boundary, or use a null-prototype target. The confirming nonce disappears the moment the merge stops walking into the prototype.