NodeJs原型链污染
最后更新时间:
文章总字数:
预计阅读时间:
NodeJs原型链污染
为了学这个知识点,特意又回顾了下js知识
js对象相关
对象创建
首先了解一下js对象创建的三种方法,js对象其实就是大括号加键值对。
普通创建:直接用大括号加键值对
1
2var person = {name:'zhangsan',age:11};
var person = {} // 创建空对象使用构造函数:先写一个里面含this的普通函数(使用this给键赋值),再用new来创建对象,记得加new!
1
2
3
4
5
6function person(name,age){
this.name = name;
this.age = age;
}
var person1 = new person("zhangsan",11);
console.log(person1.name);直接使用Object构造函数,需要什么键值对再自己赋值即可,这里需要注意的是在js中,如果访问了对象中不存在的键,就会自动添加该键并赋值。
1
2var o4 = new Object();
o4.name = "lisi";
对象继承
由于js中并没有class这个概念(构造函数近似class,而函数其实也相当于对象,也就是说js中一切都是对象),所以继承不能够像java和cpp一样基于class,只能采用原型继承。
原型继承是对象与对象之间的继承,继承关系形成了一条原型链。
而函数的prototype属性就指向了自己的原型对象,原型对象是一个普通的JavaScript对象,它包含着一些属性和方法,这些属性和方法可以被该函数的所有实例共享。
当使用new
操作符来创建一个实例对象时,实例对象会继承它所属的构造函数的原型对象中的属性和方法。实例对象可以通过原型链访问原型对象中的属性和方法。如果实例对象需要访问的属性或方法在自身上找不到,它就会沿着原型链向上查找,直到找到或者到Object的原型(Object的原型是null)返回nodefined为止
prototype和__proto__的区别
在 JavaScript 中,每个对象都有一个 __proto__
属性,它指向该对象的原型。原型是一个对象,也可以有自己的原型,这样就形成了一个原型链。同时,每个函数也有一个 prototype
属性,它是一个对象,当该函数作为构造函数创建实例时,实例对象的 __proto__
属性会指向该构造函数的 prototype
属性,这样就可以实现属性和方法的继承。
区别在于:
prototype
属性是函数所独有的,而__proro__
属性是每个对象都有的(再强调一次,函数也是js对象)prototype
属性指向一个对象,它是用来存储属性和方法,这些属性和方法可以被该函数的实例对象所继承。而__proto__
属性指向该对象的原型,它是用来实现对象之间的继承。简单来说就是functionName.prototype===varName.__proto__
,都可以访问到对象的原型。
nodejs原型链污染
概念
一句话概括原型链污染:如果修改了一个对象的原型,那么会影响所有来自于这个原型的对象,这就是原型链污染。
原型链污染通常出现在对象,数组的键名或者属性名可控,同时是赋值语句的情况下 (简单来说就是键名和键值都可控情况下),将键名设置为__proto__
就可以利用赋值语句修改原型对象,进而实现原型链污染,常见的危险函数有merge和clone。
1 | function merge(target, source) { |
通过断点调试可以明显看到污染的流程,__proto__
当成键时,source和target都有,就递归调用merge,递归调用时的target就是target.__proto__
即访问到了原型,而source就是我们一开始设置的{"b":2}
,于是在原型中加入了新的键。
利用
在ctf比赛中,最常见的原型链污染利用方法就是修改admin密码实现越权。
一次比赛中的代码:
1 | router.post("/DeveloperControlPanel", function (req, res, next) { |
1 | { |
成功往req.session.knight原链中加入mypasswd:111,之后再传key为mypasswd,由于Admin没有这个键,就会往上找,找到111,获得flag。
参考链接:
【WEB】nodejs原型链污染 | 狼组安全团队公开知识库 (wgpsec.org)