一、nodejs 和 js 的区别
js(JavaScript)是一种高级的、解释型的编程语言;它是一门基于原型、函数先行的语言,是一门多范式的语言,它支持面向对象编程,命令式编程,以及函数式编程。
Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境,是一个让 JavaScript 运行在服务端的开发平台,它让 JavaScript 成为与PHP、Python、Perl、Ruby 等服务端语言平起平坐的脚本语言。
二、prototype 和 __proto__
function Foo() {
this.bar = 1
}
new Foo()
在js中,定义一个类就是定义它的构造函数
这里Foo类的构造函数就是Foo函数
这张图中,先定义了一个foo类,并使用了原型prototype定义了一个show属性,下面新实例的对象就具有了这个show属性
我们可以认为原型prototype是类Foo的一个属性,而所有用Foo类实例化的对象,都将拥有这个属性中的所有内容,包括变量和方法。比
如上图中的foo对象,其天生就具有foo.show()方法。
我们可以通过Foo.prototype来访问Foo类的原型,但Foo实例化出来的对象,是不能通过prototype访问原型的。
这时候,就要用到 proto
例如Foo类的对象foo:
Foo.prototype == foo._proto_
这里总结一下:
所有类都有一个prototype属性,实例化的对象就会拥有这个prototype中的属性和方法
而实例化的对象可以通过 __proto__属性,指向类的prototype属性
三、什么是原型链
上面说了,所有对象都可以通过__proto__属性来访问自身类的prototype
而js中,万物皆是对象,所以各个对象之间就有一条用__proto__连起来的的链条
下图就清晰易懂
下面这个例子讲解了prototype继承链的机制:
最终输出: Name:Melania Trump
首先将Son的prototype指向了Father类的对象,然后在输出son.last_name 时,js引擎先在son对象中查找,如果找不到就在son.__proto__上查找,直到null
这就是prototype继承链
四、原型链污染是什么?
上面解释了原型链,这里使用例子来解释原型链污染
可以看到,我们首先定义了一个foo对象,foo的prototype(原型)是object,当我们修改了object的属性后,再定义一个对象qoo时,可以发现它的原型多了一个 bar:2
,即qoo的原型链被污染了
所以,如果攻击者控制和修改了一个对象的原型,那么所有继承这个原型的对象都会拥有修改后的属性,这个攻击称为原型链污染。
五、实例
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]
}
}
}
可以看到merge方法中存在赋值操作:target[key] = source[key]
若这个key == _proto_ ,是不是就能造成原型链污染了呢?
这个例子中,首先定义了两个对象o1、o2,使用merge方法后,可以看到o1的值被成功覆盖,但是原型链并没有被污染
因为在定义的o2中,”_proto_”不被认为是一个键,而是直接代表了o2的原型
也就是说,这里只是更改了o2的原型,并不是将其污染,因此其他对象继承的object并没有被污染
下面将代码修改成这样:
let o1 = {}
let o2 = JSON.parse('{"a": 1, "__proto__": {"b": 2}}')
merge(o1, o2)
console.log(o1.a, o1.b)
o3 = {}
console.log(o3.b)
o3.b 也输出 2
因为在JSON.parse的作用下,”_proto_”也被认为是一个键,从而污染了原型链
实例之CTF题:
来自VNCTF2022的一道:newcalc0
给了源码
这里获取flag需要满足条件 Object.keys(Object.prototype).length > 0
,也就是要污染Object
而下面的foreach遍历将Object的原型中的数据删完了,因为在污染了原型链之后,除非重启,这个被污染的原型链将一直存在,所以需要遍历删除数据
官方playload:console.table([{a:1}],['__proto__'])
学习一下:
“Node.js 修复的第四个漏洞是低危漏洞 (CVE-2022-21824),可导致攻击者通过 console.table 属性污染原型。鉴于Console.table() 函数的格式化逻辑,以下做法并不安全:允许受用户控制的输入被传递给属性参数,同时将具有至少一个属性的对象当作第一个参数 (_proto_) 进行传递。 该原型污染漏洞产生的影响有限,因为它仅允许将空白字符串分配给对象原型的数字键。”
说实话,研究了半天,不是很理解这个是什么意思,但最终的结果就是能污染原型链。
- 本文链接:http://siii0.github.io/nodejs%E5%8E%9F%E5%9E%8B%E9%93%BE%E6%B1%A1%E6%9F%93/
- 版权声明:本博客所有文章除特别声明外,均默认采用 许可协议。