本文内容:
- 原型链污染的概念以及原理
- 如何利用原型链污染
- 例子分析
概念
本文可能会边学边写,思维可能会有些跳跃,但我尽力写明白
在JavaScript
当中,如果函数用来创建新的对象,则这个函数称为对象的构造函数
比如:
这里其实是构造了一个名叫a
的类,他有个属性test
,值为1
在JavaScript中,每个函数都有一个prototype属性,这个属性指向函数的原型对象。
所有引用类型(函数,数组,对象)都拥有
__proto__
属性(隐式原型
所有类拥有
prototype
属性(显式原型)
原型链是javascript的实现的形式,递归继承原型对象的原型,原型链的顶端是Object的原型。
在JavaScript中,声明一个函数A的同时,浏览器在内存中创建一个对象B,然后A函数默认有一个属性prototype指向了这个对象B,这个B就是函数A的原型对象,简称为函数的原型。这个对象B默认会有个属性constructor指向了这个函数A。
其中类生成的对象的__proto__
和类本身的prototype
是等价的
所以说,这里你如果修改了对象的__proto
属性,同时会印象到这个原型类的属性,这里举个例子
这里只是修改了test
这个对象的属性,但由于修改的是对象的原型,那么实际上整条原型链被污染了,它实际上是修改了Object
这个类的属性值,从而导致新的继承自Object
类的新对象的值也被污染修改了
这里总结一下:
- 每一个构造函数都有一个原型对象
- 对象的
__proto
属性指向原型对象prototype
- 在调用一个对象的属性的时候,如果在这个对象中没有这个属性,会向
__proto
寻找,如果没有的话,继续往__proto__.__proto__
中寻找,一直找到null
为止
如何利用原型链污染呢?
在能控制数组的键值时,可以将其修改为__proto__
从而实现原型链污染
- 对象merge
- 对象clone(其实内核就是将待操作的对象merge到一个空对象中)
举例
function escape(input) { |
这里对config.source
进行了delete
处理,而且我们要输出的src
也是config.source
,那能不能通过原型链污染来把他的值给覆盖成#" onerror=prompt(1) />
呢?
这里要满足删除条件是满足这个正则
if (/[^\w:\/.]/.test(config.source)) { |
给source
传过去一个#
即可
这里直接传的话,会发现src
无法闭合
这里找了一下,网上建议是用正则的匹配规则
所以这里通过$`来实现把前面<img src="
重复输出来闭合src
这时候实际上src="<img src="
Payload:
{"source":"#","__proto__": {"source":"$`onerror=prompt(1) />"}}