一道nodejs污染+jwt爆破的题

Nodejs命令执行

又是复现其他师傅博客上的题目的一天~

这题的整体结构还是比较简单的,但是做的过程还是踩了不少的坑。

看起来需要获得admin权限

抓个包看一下

coockie部分看起来有点像JWT,理由是由三部分加密组成,且由点进行连接。

到jwt.io进行解密

尝试将guest改成admin再传入,然而还是不行。

可以推测这里是使用了密钥进行加密,这里对jwt弱密钥进行爆破,编写如下脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#jwtj加密key值爆破脚本
import jwt
#在这里输入jwt token
jwt_str=''

f=open('keys.txt')#在这里指定字典文件
for i in f:
try:
jwt.decode(jwt_str, verify=True, key=i, algorithms='HS256')
print('the key is '+i)
break
except (jwt.exceptions.ExpiredSignatureError,jwt.exceptions.InvalidAudienceError,jwt.exceptions.InvalidIssuedAtError,jwt.exceptions.InvalidIssuedAtError,jwt.exceptions.ImmatureSignatureError):
print('there are something wrong,but the key is' + i)#数据部分预定义字段错误,但是key是正确的
break
except jwt.exceptions.InvalidSignatureError:
continue
else:
print('found no key')

重新进行编码成功进入/source页面,里面是如下源码

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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
const express = require("express");
const jwt = require("jsonwebtoken");
const app = express();
const bodyParser = require("body-parser");
const path = require("path");
const jwt_secret = "toor";
const cookieParser = require("cookie-parser");
const putil_merge = require("putil-merge");
app.use(cookieParser());
app.use(bodyParser.urlencoded({ extended: true })).use(bodyParser.json());

var Super = {};

var safecode = function (code) {
let validInput =
/global|mainModule|constructor|read|write|_load|exec|spawnSync|stdout|eval|stdout|Function|setInterval|setTimeout|var|\+|\*/gi;
return !validInput.test(code);
};

app.all("/code", (req, res) => {
res.type("html");
if (req.method == "POST" && req.body) {
putil_merge({}, req.body, { deep: true });
}
res.send("welcome to code");
});

app.get("/source", (req, res) => {
res.type("html");
var auth = req.cookies.auth;
jwt.verify(auth, jwt_secret, function (err, decoded) {
if (decoded.user === "admin") {
res.sendFile(path.join(__dirname + "/app.js"));
} else {
res.end("you are not admin");
}
});
});

app.all("/root", (req, res) => {
res.type("html");
code = req.body.code;
console.log(req.body.key);
if (!req.body.key || req.body.key === undefined || req.body.key === null) {
res.send("please input key");
} else {
if (Super["userid"] === "Superadmin" + req.body.key) {
if (!safecode(code)) {
res.send("forbidden!");
} else {
res.send(eval(code));
}
} else {
res.send("You are not the Super");
}
}
});

app.get("/", (req, res) => {
res.type("html");
var token = jwt.sign({ user: "guest" }, jwt_secret, { algorithm: "HS256" });
res.cookie("auth ", token);
res.end("Only admin can get source in /source");
});

app.listen(3000, () => console.log("Server started on port 3000"));

/root路由下有一个eval函数可以执行命令,然而要进入eval函数的判断前提是Super["userid"] === "Superadmin"+req.body.keykey值不为空

在往上在code路由下有一个putil_merge函数进行merge操作,可以推断这里要使用原型链污染。

这里现在code处污染userid的值,然后再在root下传入keycode的值便可以进行命令执行了。

原先我不知道req.body.key 这个参数可以用POST或者json直接传参赋值,还在想要如何污染才能绕过判断,所以在这里卡了一会。。。

其实在程序最上面那一部分就说明了可以使用json或者POST传参

1
app.use(bodyParser.urlencoded({ extended: true })).use(bodyParser.json());

卡住我的第二个点是我不知道Super["userid"]其实就相当于Super.userid所以只要污染原型链上的userid就够了,在头几行也定义了说Super是一个空对象。这搞得我也想了一段时间要怎么去污染Super["userid"]这个参数。。。

还有就是Super["userid"] === "Superadmin" + req.body.key中的"Superadmin" + req.body.key其实就是简单的字符串拼接,当时也不知道自己是脑袋抽了还是怎么了,想不过来这个要怎么处理。。。

该清楚上面那些原理后,接着可以构造发包了。

首先是code路由下的构造

这里需要注意的是需要在请求头中加入Content-type:application/json,我一开始没有注意,把它加到Accept头里去了,整了半天才发下加错地方了。。。。

接着是root路由下的构造

key值传入zzzSuperadmin拼接通过判断,同时code进行命令执行。

这里使用的是POST直接传参,所以要写成Content-type:application/x-www-form-urlencoded

在源码处我们有注意到code其实是进行了正则匹配过滤,可以使用一些常见bypass进行绕过

1
2
3
4
5
6
16进制编码
unicode编码
加号拼接
模板字符串
concat函数连接
base64编码

在这里我是用的是16进制编码。另外我发现只有进行同步进程创建才可以成功执行命令。

最后在记录一个大坑,今天这个洞其实是个CVE,影响版本从1.0.0 到 3.6.6。而我一开始安装的putil-merge是3.10.10的版本,已经修复了该反序列化漏洞,卡了我一下午。。。。后面才发现是因为版本原因,下次也要多注意一点了。。

参考文章

nodejs中代码执行绕过的一些技巧-安全客 - 安全资讯平台 (anquanke.com)

哈哈,骗你的!ヾ(゚∀゚ゞ)

https://www.jianshu.com/p/acbb936e87df


一道nodejs污染+jwt爆破的题
https://sammylingsj.github.io/2023/07/01/一道nodejs污染+jwt爆破的题/Nodejs命令执行/
作者
s4mmy
发布于
2023年7月1日
许可协议