原型链污染漏洞(⼀)
0x01 深⼊了解JavaScript
对象与类
JavaScript⼀切皆对象,所以先来了解了解对象
创造⼀个最简单的js对象如:
var obj = {};
创建obj这个对象时,并没有赋予他任何属性或者⽅法,但是他会具有⼀些内置属性和⽅法,像__proto__,constructor,toString等.
为了探究这些内置属性是怎么来的,接下来需要看⼀下JavaScript中类的⼀些机制
JavaScript中的类从⼀个函数开始:
函数对象:
function MyClass() {
console.log("lonmar");
}
var inst = new MyClass();
//以上代码创建了⼀个MyClass函数,同时MyClass也是⼀个类,可以像别的语⾔中那样为这个类实例化⼀个对象inst
观察以上代码执⾏结果可以发现,在实例化inst的时候,MyClass()也同样执⾏了.
这可以联想到构造函数,构造函数在的特性就是在new⼀个对象的时候执⾏.
所以MyClass()函数与MyClass这个类的关系就很明显了,前者是后者的构造函数
通过constructor这个属性可以查看对象的构造函数
下⾯了解下 __proto__与prototype
先抛出结论
prototype是⼀个类的属性,所有类对象在实例化的时候将会拥有prototype中的属性和⽅法
⼀个对象的__proto__属性,指向这个对象所在的类的prototype属性
类在运⾏程序运⾏时是可以修改的
再看实例:
下⾯这段代码通过prototype属性,为Person类添加了getInfo()这个属性.
其实例化对象也都具有这个属性
function Person(name,age) {
this.name = name;
this.age = age;
console.log("hello,I am",this.name);
}
console.log("I am Person Class");
}
Info = function(){
return this.name + "," + this.age;
}
var lonmar=new Person('lonmar',10);//I am Person Class
下⾯其实是JavaScript中继承的⼀种写法,通过修改原型链来继承
Student类继承了Person类,但也可以看出只是继承部分属性,如constructor就没有被继承
然后通过prototype修改的属性也被继承了!
function Student(){
console.log("I am Student Class")
}
Student.prototype = new Person();//I am Person Class;erson { name: undefined, age: undefined, greed: [Function] } Student.prototype.age = 10;//10
Student.prototype.name = "lonmar";//lonmar
var stud = new Student();
通过下⾯的实例⼜可以看出,Student类的prototype实际上指向⼀个Person的实例化对象
stud的__proto__也指向⼀个对象,并且stud.__proto__ =Student.prototype
也可以依次向前查__proto__属性,可以发现奇妙的关系.(最终是null的)
最后,总结⼀下:
JavaScript是⼀个神奇的语⾔,⼀切皆对象.
对象都有⼀个__proto__属性,指向它的类的``prototype`
类是通过函数来定义的,定义的这个函数⼜是这个类的constructor属性值
每个构造函数constructor 都有⼀个原型对象prototype
JavaScript使⽤prototype链实现继承机制
⼦类是可以通过prototype链修改其⽗类属性,以及爷爷类的属性值的
0x02 什么是原型链污染
做⼀个简单的实验,其实也是对前⾯的⼀个总结
// 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)
zoo.bar的结果是2;
因为前⾯修改了foo的原型foo.__proto__.bar = 2,⽽foo是⼀个Object类的实例,所以实际上是修改了Object这个类,给这个类增加了⼀个属性bar,值为2。
后来,⼜⽤Object类创建了⼀个zoo对象let zoo = {},zoo对象⾃然也有⼀个bar属性了。
那么,在⼀个应⽤中,如果攻击者控制并修改了⼀个对象的原型,那么将可以影响所有和这个对象来⾃同⼀个类、⽗祖类的对象。这种攻击⽅式就是原型链污染。0x03 哪些情况下原型链会被污染
1. 最显然的情况
obj[a][b] = value
obj[a][b][c] = value
如果控制了a,b,c及value就可以进⾏原型链污染的攻击,
可以控制a=__proto__
2.利⽤某些API来进⾏攻击
Object recursive merge
merge (target, source)
foreach property of source
if property exists and is an object on both the target and the source
merge(target[property], source[property])
else
target[property] = source[property]
这种情况下,__proto__必须被视为key才能成功
对于
function merge(target, source) {
for (let key in source) {
if (key in source && key in target) {
} else {
target[key] = source[key]
}
}
}
//1.
let o1 = {}
let o2 = {a: 1, "__proto__": {b: 2}}
merge(o1, o2)
console.log(o1.a, o1.b)
o3 = {}
console.log(o3.b)//undefined
//2.
let o1 = {}
let o2 = JSON.parse('{"a": 1, "__proto__": {"b": 2}}')
merge(o1, o2)
console.log(o1.a, o1.b)
o3 = {}
console.log(o3.b)//2
1和2两种情况是不⼀样的.
因为前⾯代码中 __proto__已经代表o2的原型了 ,没有被看成⼀个key
后⾯的代码中经过JSON.parse解析,__proto__就代表了⼀个key
详情可参考www.leavesongs/PENETRATION/javascript-prototype-pollution-attack.html#0x04 Property definition by path
theFunction(object, path, value)
如果攻击者可以控制path,如path=__proto__.myValue.就可以进⾏污染
Object clone
function clone(obj) {
return merge({}, obj);
}
3. 出易受攻击的API
在github/HoLyVieR/prototype-pollution-nsec18这个项⽬的paper⾥⾯,作者给了⼀个易受攻击API的脚本. node xx.js library-name
var process = require('process');
//check是否收到污染
function check() {
if ({}.test == "123" || {}.test == 123) {
delete st;
return true;
}
return false;
}
function run(fnct, sig, name, totest) {
// Reinitialize to avoid issue if the previous function changed attributes.
BAD_JSON = JSON.parse('{"__proto__":{"test":123}}');
try {
fnct(totest);
} catch (e) {}
if (check()) {
console.log("Detected : " + name + " (" + sig + ")");
}nodejs字符串转数组
}
var BAD_JSON = {};//NULL OBJ
var args = process.argv.slice(2);//node xx.js param1 param2 parma3获取所有参数返回数组[param1,param2,param3] //忽略异常
<('uncaughtException', function(err) { });
var pattern = [{
fnct : function (totest) {
totest(BAD_JSON);
},
sig: "function (BAD_JSON)"
},{
fnct : function (totest) {
totest(BAD_JSON, {});
},
sig: "function (BAD_JSON, {})"
},{
totest({}, BAD_JSON);
},
sig: "function ({}, BAD_JSON)"
},{
fnct : function (totest) {
totest(BAD_JSON, BAD_JSON);
},
sig: "function (BAD_JSON, BAD_JSON)"
},{
fnct : function (totest) {
totest({}, {}, BAD_JSON);
},
sig: "function ({}, {}, BAD_JSON)"
},{
fnct : function (totest) {
totest({}, {}, {}, BAD_JSON);
},
sig: "function ({}, {}, {}, BAD_JSON)"
},{
fnct : function (totest) {
totest({}, "__proto__.test", "123");
},
sig: "function ({}, BAD_PATH, VALUE)"
},{
fnct : function (totest) {
totest({}, "__proto__[test]", "123");
},
sig: "function ({}, BAD_PATH, VALUE)"
},{
fnct : function (totest) {
totest("__proto__.test", "123");
},
sig: "function (BAD_PATH, VALUE)"
},{
fnct : function (totest) {
totest("__proto__[test]", "123");
},
sig: "function (BAD_PATH, VALUE)"
},{
fnct : function (totest) {
totest({}, "__proto__", "test", "123");
},
sig: "function ({}, BAD_STRING, BAD_STRING, VALUE)" },{
fnct : function (totest) {
totest("__proto__", "test", "123");
},
sig: "function (BAD_STRING, BAD_STRING, VALUE)" }]
if (args.length < 1) {
console.log("First argument must be the library name"); exit();
}
try {
var lib = require(args[0]);
} catch (e) {
console.log("Missing library : " + args[0] );
exit();
}
var parsedObject = [];
function exploreLib(lib, prefix, depth) {
if (depth == 0) return;
if (parsedObject.indexOf(lib) !== -1) return;
parsedObject.push(lib);
for (var k in lib) {
if (k == "abort") continue;
if (k == "__proto__") continue;
if (+k == k) continue;
console.log(k);
if (lib.hasOwnProperty(k)) {
for (p in pattern) {
if (pattern.hasOwnProperty(p)) {
run(pattern[p].fnct, pattern[p].sig, prefix + "." + k, lib[k]); }
}
exploreLib(lib[k], prefix + "." + k, depth - 1);
}
}
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论