漏洞点:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function merge(target, source) {
for (let key in source) {
if (key in source && key in target) {
// 如果target与source有相同的键名,则让target的键值为source的键值
merge(target[key], source[key]);
} else {
// 如果target与source没有相同的键名,则直接在target新建键名并赋给键值
target[key] = source[key];
}
}
}

let o1 = {};
let o2 = JSON.parse('{ "a": 1, "__proto__": { "b": 2} }');
merge(o1, o2);
console.log(o1.a, o1.b);

let o3 = {};
console.log(o3.b);

1.prototype原型

当谈到继承时,JavaScript 只有一种结构:对象。每个实例对象(object)都有一个私有属性(称之为 proto )指向它的构造函数的原型对象(prototype)。该原型对象也有一个自己的原型对象(__proto__),层层向上直到一个对象的原型对象为 null。根据定义,null 没有原型,并作为这个原型链中的最后一个环节。
几乎所有 JavaScript 中的对象都是位于原型链顶端的 Object 的实例。
尽管这种原型继承通常被认为是 JavaScript 的弱点之一,但是原型继承模型本身实际上比经典模型更强大。例如,在原型模型的基础上构建经典模型相当简单。

1
2
3
4
5
6
7
function Foo(name,age){this.name=name;this.age=age;}Object.prototype.toString=function(){
console.log("I'm "+this.name+" And I'm "+this.age);}var fn=new Foo('xiaoming',19);
fn.toString();
console.log(fn.toString===Foo.prototype.proto.toString);
console.log(fn.proto===Foo.prototype)
console.log(Foo.prototype.proto===Object.prototype)
console.log(Object.prototype.proto===null)

图片

2.原型链污染原理

在一个应用中,如果攻击者控制并修改了一个对象的原型,那么将可以影响所有和这个对象来自同一个类、父祖类的对象。这种攻击方式就是原型链污染。

1
2
3
4
5
// foo是一个简单的JavaScript对象let foo = {bar: 1}// foo.bar 此时为1
console.log(foo.bar)// 修改foo的原型(即Object)
foo.proto.bar = 2// 由于查找顺序的原因,foo.bar仍然是1
console.log(foo.bar)// 此时再用Object创建一个空的zoo对象let zoo = {}// 查看zoo.bar,此时bar为2
console.log(zoo.bar)

3.原型链污染配合RCE

有原型链污染的前提之下,我们可以控制基类的成员,赋值为一串恶意代码,从而造成代码注入。

1
2
3
4
5
let foo = {bar: 1}
console.log(foo.bar)
foo.proto.bar = 'require(\'child_process\').execSync(\'open /System/Applications/Calculator.app/\');'
console.log(foo.bar)let zoo = {}
console.log(eval(zoo.bar))