深圳做网站-龙华信科,wordpress免费图床,宁波 手机网站建设,外包公司怎么赚钱原型链 大部分面向对象的编程语言#xff0c;都是通过“类”#xff08;class#xff09;实现对象的继承。传统上#xff0c;JavaScript 语言的继承不通过 class#xff0c;而是通过“原型对象”#xff08;prototype#xff09;实现 1、prototype 属性的作用
JavaScri…原型链 大部分面向对象的编程语言都是通过“类”class实现对象的继承。传统上JavaScript 语言的继承不通过 class而是通过“原型对象”prototype实现 1、prototype 属性的作用
JavaScript 规定每个函数都有一个prototype属性指向一个对象
function f() {}
typeof f.prototype // object
函数f默认具有prototype属性指向一个对象
js中类的建立
js 中定义一个类需以定义“构造函数”的方式来定义
function Foo() {this.bar 1;
}new Foo();
解析
Foo函数的内容就是Foo类的构造函数this.bar就表示Foo类中的一个属性
为简化编写js的代码ECmAScript6 后增加了class语法但class其实只是一个语法塘
js中的类中方法的建立
一个类中必然有一些方法类似属性this.bar也可将方法定义在构造函数内部
function Foo() {this.bar 1;this.show function() {console.log(this.bar);}
}(new Foo()).show() // 1
解析
出现问题新建Foo对象时this.show function()... 就会执行一次这个show方法实际上是绑定在对象上的而不是绑定在“类”中
js中原型prototype的引用
在创建类时只创建一次show方法需要使用原型prototype
function Foo() {this.bar 1;
}Foo.prototype.show function show() {console.log(this.bar);
}let foo new Foo();
foo.show();
解析
原型prototype是类Foo的一个属性所有用Foo类实例化的对象都有这个属性的所有内容包括变量和方法。foo对象天生具有foo.show()方法
此时Foo.prototype访问Foo类的原型但是Foo实例化出来的对象不能够通过prototype访问原型
2、__proto__ 是 JavaScript 中一个对象的内部属性它指向该对象的原型。原型是另一个对象包含共享的属性和方法对象可以通过原型继承这些属性和方法。 js 中__proto__的引用
一个 Foo 类实例化出来的 foo 对象可通过foo.__proto__属性来访问Foo类中的原型 prototype和__proto__的定义
prototype一个类的属性所有类对象在实例化的时候会拥有prototype中的属性和方法__proto__一个对象的__proto__属性指向这个对象所在的类的prototype属性
3、原型链继承 所有类对象在实例化的时候将会拥有 prototype 的属性和方法这个特性被用来实现 js 中的继承机制 function Father() {this.first_name Donald;this.last_name Trump;
}function Son() {this.first_name Melania;
}Son.prototype new Father();let son new Son();
console.log(Name:${son.first_name} ${son.last_name})
// Name:Melania Trump
解析
Son类继承了Father类的last_name属性
主要流程
在对象son中寻找last_name无法找出则在son.__proto__中寻找last_name如果仍然无法找到则继续在son.__proto__.__proto__中寻找last_name依次寻找直到null结束。如object.prototype的__proto__就是null
js 中的这个查找机制被运用在面向对象的继承中被称为是prototype继承链
PS
每个构造函数constructor都有一个原型对象prototype对象的__proto__属性指向类的原型对象prototypejs 使用prototype链实现继承机制
4、原型链污染
实例
foo.__proto__指向的是Foo类的prototype。若修改foo.__proto__中的值就可修改Foo类?
// foo是一个简单的JavaScript对象
let foo {bar:1};// foo.bar此时为1
console.log(foo.bar);// 修改foo的原型即object
foo.__proto__.bar 2;// 查找顺序原因foo.bar仍然是1
console.log(foo.bar);// 此时用objecr创建一个空的zoo对象
let zoo {};// 查看zoo.bar
console.log(zoo.bar);解析
修改 foo 原型foo.__proto__.bar 2而 foo 是一个object类的实例所以实际上是修改了object这个类给这个类增加了一个属性bar值为2
后来用object类创建了一个zoo对象let zoo {}zoo对象自然也有一个bar属性了
原型链污染定义 如果攻击者控制并修改了一个对象的原型那将可以影响所有和这个对象来自同一个类、父类的对象这种攻击方式就是原型链污染 哪些情况原型链会被污染
哪些情况可以设置__proto__的值找到能够控制数组对象的“键名”的操作即可
使用megre测试
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__,是不是就可以原型链污染呢
使用如下代码进行测试
let o1 {};
let o2 {a:1,__proto__:{b:2}};
merge(o1,o2);
console.log(o1.a,o1.b);o3 {};
console.log(o3.b);结果合并成功原型链未被污染
解析
js 创建 o2 的过程let o2 {a:2,__proto__:{b:2}}中__proto__代表o2的原型了此时遍历o2的所有键名拿到的是[a,b]__proto__并不是一个key也不会修改object的原型
修改代码
let o1 {};
let o2 JSON.parse({a:1,__proto__:{b:2}});
merge(o1,o2);
console.log(o1.a,o2.b);o3 {};
console.log(o3.b); 解析
JSON解析的情况下__proto__会被认为是一个真正的“键名”不代表原型所以在遍历o2的时候存在这个键
新建的o3对象也存在b属性说明object已被污染
实例
案例一
const express require(express)
var hbs require(hbs);
var bodyParser require(body-parser);
const md5 require(md5);
var morganBody require(morgan-body);
const app express();
//目前user并没有admintoken
var user []; //empty for nowvar matrix [];
for (var i 0; i 3; i){matrix[i] [null , null, null];
}function draw(mat) {var count 0;for (var i 0; i 3; i){for (var j 0; j 3; j){if (matrix[i][j] ! null){count 1;}}}return count 9;
}app.use(express.static(public));
app.use(bodyParser.json());
app.set(view engine, html);
morganBody(app);
app.engine(html, require(hbs).__express);app.get(/, (req, res) {for (var i 0; i 3; i){matrix[i] [null , null, null];}res.render(index);
})app.get(/admin, (req, res) { /*this is under development I guess ??*/console.log(user.admintoken);if(user.admintoken req.query.querytoken md5(user.admintoken) req.query.querytoken){res.send(Hey admin your flag is bflag{prototype_pollution_is_very_dangerous}/b);} else {res.status(403).send(Forbidden);}
}
)app.post(/api, (req, res) {var client req.body;var winner null;if (client.row 3 || client.col 3){client.row % 3;client.col % 3;}matrix[client.row][client.col] client.data;for(var i 0; i 3; i){if (matrix[i][0] matrix[i][1] matrix[i][1] matrix[i][2] ){if (matrix[i][0] X) {winner 1;}else if(matrix[i][0] O) {winner 2;}}if (matrix[0][i] matrix[1][i] matrix[1][i] matrix[2][i]){if (matrix[0][i] X) {winner 1;}else if(matrix[0][i] O) {winner 2;}}}if (matrix[0][0] matrix[1][1] matrix[1][1] matrix[2][2] matrix[0][0] X){winner 1;}if (matrix[0][0] matrix[1][1] matrix[1][1] matrix[2][2] matrix[0][0] O){winner 2;} if (matrix[0][2] matrix[1][1] matrix[1][1] matrix[2][0] matrix[2][0] X){winner 1;}if (matrix[0][2] matrix[1][1] matrix[1][1] matrix[2][0] matrix[2][0] O){winner 2;}if (draw(matrix) winner null){res.send(JSON.stringify({winner: 0}))}else if (winner ! null) {res.send(JSON.stringify({winner: winner}))}else {res.send(JSON.stringify({winner: -1}))}})
app.listen(3000, () {console.log(app listening on port 3000!)
})
解析
获取 flag 的条件是传入的querytoken要和user数组本身的admintoken的MD5值相等且二者都要存在全文没有对user.afmintoken进行赋值理论上这个值不存在但存在以下赋值语句
matrix[client.row][client.col] client.data;
其中data、row、col都是post传入的值都是可控的。所以可构造原型链污染
本地测试