GYCTF2020-Ez_Express
[GYCTF2020]Ez_Express
前言
看了一下有注册有登录,于是试了一下www.zip发现有源码泄露:
审计的是thinkphp6.0,看半天看不出个所以然,于是看wp了
js原型链污染
原型链特性:
当我们调用一个对象的某属性时
1.对象(obj)中寻找这一属性 |
以上机制被称为js的prototype的继承链。和node.js沙盒逃逸的原理似乎是一样的
举例:
function Foo() { |
理解:
Foo是一个类,prototype是Foo的属性,Foo中所有的实例化后对象都将拥有这个属性,我们在prototype属性中定义了一个方法show,接下来所有实例化后的对象如foo都将直接拥有foo方法。
原型链污染定义:
如果攻击者控制并修改了一个对象的原型,那么将可以影响所有和这个对象来自同一个类、父祖类的对象。这种攻击方式就是原型链污染 |
例:
let foo={bar:1}//foo是一个对象 |
哪些情况下原型链会被污染?
在含有能够控制数组(对象)的“键名”的操作即可,一般是以出现:
merge和clone对象
不安全的对象递归合并
以merge,因为merge执行的就是递归,为例,先构造一个简单的merge函数
const merge = (target, source) => { |
这里解释一下,merge将这几个键值作为一个属性合并到newperson这个对象中了,merge函数执行的其实可以认为是
Person.job.title=...... |
这个时候job的原型是Person,所以Person的值就发生了改变。
按路径定义属性
有些JavaScript库的函数支持根据指定的路径修改或定义对象的属性值。通常这些函数类似以下的形式:theFunction(object, path, value),将对象object的指定路径path上的属性值修改为value。如果攻击者可以控制路径path的值,那么将路径设置为_proto_.theValue,运行theFunction函数之后就有可能将theValue属性注入到object的原型中。
实战
然后开始审计
const merge = (a, b) => { |
在index.js找到merge和clone,接下来看看有哪里调用了他们,发现是在/action这里,并且需要user为admin
router.post('/action', function (req, res) { |
那么就需要先登录,在登录这列发现
**其中 'user':req.body.userid.toUpperCase()
这一句中toUpperCase()
函数是为了将字符串中的小写字符转换成大写,但因为javascript的特性,函数存在分险(下图),所以假如我们传入admın
的话,经过toUpperCase()
的处理后,会变成ADMIN
**关于node-js题目的尝试/9.png)
在javascript中有几个特殊的字符需要记录一下
对于toUpperCase():
字符"ı"、"ſ" 经过toUpperCase处理后结果为 "I"、"S"
Copy对于toLowerCase():
字符"K"经过toLowerCase处理后结果为"k"(这个K不是K)
所以我们可以注册用户名为admın
然后进行登录即可,接下来在去可以污染的参数那边看看该如何构造payload
首先可以可以看到clone在login那边对body进行操作,那么我们的原型链传入的值也应该在那里
router.post('/action', function (req, res) { |
再往下看,我们outputoFunctionName被渲染了,那么其实思路就很明确了,通过clone原型链污染outputoFunctionName,导致其值被改变,渲染拿到flag
router.get('/info', function (req, res) { |
payload:
{"__proto__":{"outputFunctionName": "test;clearTimeout.constructor.constructor('return process')().mainModule.require('child_process').execSync('cat /flag > /app/public/reader').toString();var test1"}} |
从语法的角度解释一下这个payload:我们通过污染outputFunctionName的原型,通过constructor访问其向上原型链,获得主环境的function,2然后通过(‘return process’)()获得主环境process变量,通过process.mainModule.require导入child_process模块,实现命令执行,最后将flag输出至/reader中,由于这个outputFunctionName是未定义的,所以我们后面访问它,就会直接赋值为我们定义的这些属性(和上面的原理是一样的)
要记得吧传输的内容格式改为json的,然后登陆/info让其被渲染,然后再登录/reader将flag下载下来即可小结
由于语法还是有很多不懂,但还好学沙盒逃逸的时候有掌握一点语法,所以原理还是可以看懂的,但是构造语句还是不太行
总结一下js的原型链污染
1.寻找merge或者clone函数
2.寻找使用这两个函数的对象,查看其是否可控,能渲染
https://www.freebuf.com/articles/web/275619.html
https://www.cnblogs.com/LEOGG321/p/13448463.html
js的字符:https://www.leavesongs.com/HTML/javascript-up-low-ercase-tip.html