結果
スコア800で、880チーム中528位でした。目標は3問正解だったので、かなり満足いく結果になりました。そのうち4問のwriteupを書きたいと思います。
環境は以下のとおりでした。
windows
・VSCode ソースコード閲覧
・Chrome 課題ページへのアクセスと調べもの
kali-linux
・コマンドでのアクセス
Writeup
skipping(web) [Beginner]
示されたURLにアクセスすると、以下の画面になります。そのまま/flag
をクリックすると、403 forbidden
を返されてしまいます。
index.js
に以下の記述を見つけました。
const check = (req, res, next) => {
if (!req.headers['x-ctf4b-request'] || req.headers['x-ctf4b-request'] !== 'ctf4b') {
return res.status(403).send('403 Forbidden');
}
next();
}
これを見ると、x-ctf4b-request
ヘッダにctf4b
という値がセットされていないと403 Forbidden
が返されてしまうことがわかります。
問題文にも「curlなどを利用して」と書かれていたので、curl
でヘッダを設定しアクセスします。
ctf4b{y0ur_5k1pp1n6_15_v3ry_n1c3}
フラグが取得できました。
log-viewer(web) [Easy]
示されたURLにアクセスすると、access.log
とdebug.log
が確認できるサイトに飛びます。?file=
にどちらかのファイル名を指定して閲覧するようです。
http://log-viewer.challenges.beginners.seccon.jp:9999/?file=access.log
access.log
の下から五行目に、?file=access.log
でも、debug.log
でもないのに200 OK
を返しているログを見つけました。
[21/June/2025:10:42:53 +0900] "GET /?file=../../proc/self/environ HTTP/1.1" 200
../
を使ってディレクトリトラバーサルを成功させた記録だと考えられるので、自分でもアクセスしてみます。
指定したファイルの内容が確認できる状態になっているようです。
ここから少し詰まったのですが、debug.log
の方にもヒントがありました。(2行目)
2025/06/21 10:40:02 DEBUG Parsed command line arguments flag=ctf4b{this_is_dummy_flag} port=8000
このログから、コマンドライン引数にフラグがありそうです。
?file=../../proc/self/cmdline
とすると、フラグが確認できました。
ctf4b{h1dd1ng_1n_cmdl1n3_m4y_b3_r34d4bl3}
url-checker(misc) [Beginner]
指定されたホストにnc
コマンドでアクセスしてみます。
allowed_hostname = "example.com"
とあるので、http://example.com/
と入力すると、以下の画面になりました。
入力されたURLが適切かチェックするツールのようです。ソースコードのmain.py
を見てみます。
allowed_hostname = "example.com"
user_input = input("Enter a URL: ").strip()
parsed = urlparse(user_input)
urlparse()
は、引数のURLを部分文字列に分割するメソッドです。(参考: https://ja.pymotw.com/2/urlparse/index.html)
parsed = urlparse('http://netloc/path;parameters?query=argument#fragment')
とすると、以下に分割してくれます。
parsed.scheme | http |
parsed.netloc | netloc |
parsed.hostname | netloc |
parsed.path | /path |
parsed.params | parameters |
parsed.query | query=argument |
parsed.fragment | fragment |
if parsed.hostname == allowed_hostname:
print("You entered the allowed URL :)")
elif parsed.hostname and parsed.hostname.startswith(allowed_hostname):
print(f"Valid URL :)")
print("Flag: ctf4b{dummy_flag}")
else:
print(f"Invalid URL x_x, expected hostname {allowed_hostname}, got {parsed.hostname if parsed.hostname else 'None'}")
elif
ブロックに入ればフラグを取得できそうです。elif
の条件は、parsed.hostname == allowed_hostname
ではないが、parsed.hostname
がallowed_hostname
で始まるということなので、http://example.commmmm/
と入力すると、フラグが確認できました。
ctf4b{574r75w17h_50m371m35_n07_53cur37}
url-checker2(misc) [Easy]
url-checkerの改良版のようです。中盤にコードが追加され、後半も改良されています。
#中盤(追加部分)
input_hostname = None
if ':' in parsed.netloc:
input_hostname = parsed.netloc.split(':')[0]
#後半
if parsed.hostname == allowed_hostname:
print("You entered the allowed URL :)")
elif input_hostname and input_hostname == allowed_hostname and parsed.hostname and parsed.hostname.startswith(allowed_hostname):
print(f"Valid URL :)")
print("Flag: ctf4b{dummy_flag}")
else:
print(f"Invalid URL x_x, expected hostname {allowed_hostname}, got {parsed.hostname if parsed.hostname else 'None'}")
またしてもelif
ブロックに入るのを目指すようです。当たり前ですがurl-checkerで成功したURLはもう通らなくなっています。
改めて調べてみると、urlparse()
メソッドはRFC3986
のURL構文要素に基づいて分割を行っているようです。RFC3986
ドキュメントを確認します。(参考:https://datatracker.ietf.org/doc/html/rfc3986.html#section-3.2)
authority = [ userinfo "@" ] host [ ":" port ]
urlparse()
のparsed.netloc
はauthority
に対応し、parsed.hostname
はhost
に対応しています。つまり、実はparsed.netloc
はさらに分割されており、そのうち@
の右側かつ:
の左側の部分がparsed.hostname
ということですね。(@
と:
はなくてもいい)
parsed=urlparse('http://user:pass@NetLoc:80/path;parameters?query=argument#fragment')
だと、以下に分割されます。(参考: https://ja.pymotw.com/2/urlparse/index.html)
parsed.scheme | http |
parsed.netloc | user:pass@NetLoc:80 |
parsed.hostname | netloc |
parsed.path | /path |
parsed.params | parameters |
parsed.query | query=argument |
parsed.fragment | fragment |
ソースコード中盤を見ると、parsed.netloc
のうち:
で分割した一番左側をinput_hostname
としています。
ソースコード後半にある目標のelif
条件を箇条書きにしてみます。
・parsed.hostname != allowed_hostname
・input_hostname == allowed_hostname
・parsed.hostname.startswith(allowed_hostname)
これらの条件より、http://example.com:@example.commmmm/
と入力すると、フラグを確認できました。
ctf4b{cu570m_pr0c3551n6_0f_url5_15_d4n63r0u5}
感想
初参加ながら、とても楽しく問題に取り組めました。
来年はwebに絞って全完を目指したいです!