[SuSeC CTF 2020]web 0
打开题目得到源码:
function sha1(s) {
return crypto.createHash("sha1")
.update(s)
.digest("hex");
}
app.post("/flag", (req, res) => {
const {first, second} = req.body;
const salt = "pepper";
if (!first || !second || first.length !== second.length) {
res.send("bad input");
return;
}
if (first !== second && sha1(salt + first) === sha1(salt + second)) {
res.send(flag); // have some flag
return;
}
res.send("access denied");
});
可以看到,首先利用 crypto 模块自定义了一个 sha1()
函数,之后对 post 接收到的 first 和 second 进行加盐哈希,需要满足三个条件:
- first 和 second 都不能为空
- first 和 second 不相等
- 它们的加盐后 sha1 值相同
一个很典型的矛盾条件,而且可以看到 sha1 判断是强相等,不存在弱类型比较 bypass 的情况。
NodeJS 弱类型
js 中也存在弱类型:
[0]==0 // true
[0]=="0" // true
[0]==="0" // false
"0" !== [0] // true
所以传first = "0"
和second = [0]
是不相等的。
NodeJS 加运算机制分析
在 Node.js (JavaScript) 中,两个字符串相加得到新的字符串 (字符串拼接),但是其他情况则有所不同:
- String + Number:将数字转为字符串再拼接;
- String + Object:字符串拼接
[object Object]
; - String + Array:字符串拼接数组里面的值用逗号分隔;
- String + 其他:直接将其他类型的整体转换为字符拼接;
- 非String + 非String:除数字、布尔两个相同的运算以外,其他都返回字符串
example:
123+"hello" // '123hello'
[123]+"hello" // '123hello'
"123"+"hello" // '123hello'
可以看到,字符串与数组相加,是字符串与数组中的元素相加后返回字符串,123+"hello"
和 [123]+"hello"
加运算得到"123"+"hello"
相同的字符串,
因此这里传first = "0"
和second = [0]
在加盐之后返回的字符串是相等的,那进行了SHA1运算之后的值必然相等。
NodeJS 中 length 机制分析
首先,对于数字没有 length 属性,字符串和数组有:字符串的 length 就是字符串长度,数组的 length 表示数组元素个数。
因此"0".length == [0].length
是成立的。
最终payload:
POST:applicaion/json
{"first": "0", "second": [0]}