前言
由于最近要给学弟们做个小测试,因此要出一个web题,由于最近学习了js原型链的污染,因此像把它作为一个思路来出题。
0x01 Prototype pollution attack
这里我们需要使用原型污染(Prototype pollution attack)的攻击方法。
说原型污染前我们先了解一下JS里的原型继承
的原理。
1 | 当谈到继承时,JavaScript 只有一种结构:对象。 |
1 | 当我们o = new F 创建一个实例o的时候,会给o添加一个proto属性,通过protp会找到 F.prototype,也就是所属类的原型。 |
上面两段是截取网上感觉说得比较好的解释。对于JS的原型链我们可以用c/c++里的继承辅助理解,但不同的是js是单继承的,所以只能形成链状,这不同于C/C++的多继承。
我们可以通过下面的例子理解一下:
1 | a = {} |
可以看到通过修改b["__proto__"]
的属性可以为a增加一个叫admin
的属性。这里可以简单的把b["__proto__"]
理解为b(a)的父类,那么通过b["__proto__"]["admin"] = true
为父类增加了一个属性,在使用a["admin"]
的使用首先会从自身的属性里查找admin
,如果没有则向上级类查找,从而在父类中得到admin的值。这跟c++的继承原理颇为相似。
我们在这个题目环境中,在helper.js
里发现有个clone函数:
1 | clone: function(obj) { |
他会对传入的对象取出key,value,然后clone出一个新的object返回。根据代码,它实行的是深度拷贝(deep clone),使用了for循环(keys.length)将所以的属性都拷贝一次(递归拷贝)。
所以我们可以尝试污染掉inputUser = {...}
的上级父类(proto)。
题目中是newUser = helper.clone(JSON.parse(inUser))
这样调用clone的,而JSON.parse跟__proto__
会产生危险的反应,先上个例子:
1 | >const plainObj = { |
可以看出在JSON.parse的时候把proto当成了属性处理,并没有过滤这个属性。所以我们可以通过这个方式来把我们需要的值添加到该对象的原型链上。
这里注意我们需要直接使用字符串,而不是构造好{},再用JSON.stringify()得到字符串,因为在stringify的时候会忽略__proto__
。如:
1 | const inputUser = { |