前言
以前一直以为原型链污染是后端污染一下那些渲染的中间件什么的,看了google ctf那题,发现前端也可以用这种思路
有几章我跳过了,因为感觉都是些历史漏洞,就不太想再发文章去记录,但是也是很有知道的必要
Universal XSS
Mutation XSS
原型链
总感觉自己讲不清楚,还是去看别人写的吧1
2
3
4
5
6
7let o1 = {}
let o2 = JSON.parse('{"a": 1, "__proto__": {"b": 2}}')
merge(o1, o2)
console.log(o1.a, o1.b)
o3 = {}
console.log(o3.b)
当用JSON.parse可以把proto当为纯键值。
调试分析了一下,发现在第二层的时候会判断__proto__
是否在对象中,对于target来说是原型链存在,对于source来说是普通的键值,就是{'b':2}
当然也可以用1
let o2 = JSON.parse('{"a": 1, "constructor": {"prototype":{"b": 2}}}')
在控制台输入1
2
3obj = {}
obj.__proto__ == obj.constructor.prototype
//true
constructor 和 prototype
这时候会引出一个问题,就是经常会看到,一会是constructor.prototype
,一会又是prototype.constructor
,这时候就很容易搞混
可以参考这篇文章,我感觉我自己都有点迷糊
https://wangdoc.com/javascript/oop/prototype1
2
3
4
5
6
7function Animal(name) {
this.name = name;
}
Animal.prototype.color = 'white';
var cat1 = new Animal('大毛');
var cat2 = new Animal('二毛');1
Animal.constructor.prototype==cat1.constructor.prototype//false
同样是constructor.prototype
,Animal是Function,cat1是Objectcat1.constructor
指向他的构造函数,也就是Animal
Animal.constructor
也是指向他的构造函数,但是是Function
,所以下面的就会是true1
2
3
4Animal.constructor.prototype == Function.prototype
true
cat1.constructor.prototype == Animal.prototype
true
__proto__
可以用来访问对象的原型,也就是prototype
1
2
3
4cat1.__proto__.__proto__ == Object.prototype
true
cat1.__proto__ == Animal.prototype
true
原理这些还是自己去找资料吧,感觉我讲的也不清晰
Prototype pollution script gadgets
https://github.com/BlackFan/client-side-prototype-pollution
防御
过滤字符串过滤
1 | if (key === "constructor" || key == '__proto__' || key == "prototype") { |
创建一个空对象
1 | var obj = Object.create(null) |
冻结prototype
1 | Object.freeze(Object.prototype) |
不会报错,只是不能被修改,可能会增加debug的难度
nodejs
通过--disable-proto
关掉prototype
官方文档
隐形的前端gadget
可以通过更改前端的API的一些变量1
2
3
4
5Object.prototype.body = 'a=1'
Object.prototype.method = 'POST'
fetch('https://example.com', {
mode: 'cors'
})
例如上面这种,就会把本来应该发送GET请求的代码,变成发送POST
有时候有些条件可以让你直接把fetch
给覆盖了,这样他的代码都会走你的假fetch
,这也是爬虫里面经常用到的技巧
例如下面的代码,参考 https://mp.weixin.qq.com/s/H4oQvqZmWS4af1VF1nAzkA
1 | (function() { |
1 | (function () { |
把原来的API备份一份,然后在把原来的API给覆盖了