warmupだけ解いたので解法を残す。
個人的難易度について
個人用として、CTF初級者の私の立場で見た難易度を記載している。上に行くほど簡単。
- 瞬殺:問題を見てすぐに解法が思いつく
- 弱:数分で解ける
- 中:1時間以内に解ける
- 強:数時間かけてやっと解ける
DiNo.1 (misc, warmup)
個人的難易度:弱
No Internet
インターネットに接続されていません
Challenge: http://34.84.32.127:80
Chromeでオフライン時に遊べる恐竜ゲームを模したWebサイトが与えられる。
スコアを5000点以上取るとflagが得られるらしい。ゲームオーバー時のコードを見てみよう。
function gameOver() { isGameOver = true; if (score >= clearscore) { gameClearElement.style.display = 'block'; function a0_0x3ccb(_0x32efe4,_0x3fcc98)(注:難読化されたコード。長いので省略) } else { gameOverElement.style.display = 'block'; } cancelAnimationFrame(animationId); }
見ての通り、スコアが規定以上であれば難読化されたコード(おそらくflagを表示するコード)が実行される。
そのため、スコアを5000点以上に設定したうえでこの関数を直接呼び出せばflagが表示される。
具体的にはF12を押して開発者ツールを起動し、コンソール画面でscore=9999;gameOver()と入力するとゲームクリアした扱いとなり、flagが得られる。
IERAE{In_f4ct,th3_4uth0r's_h1gh_sc0r3_1s_4b0ut_5000}
※なお、難読化されたコードについてはde4jsを用いても解除できなかったので、解読は諦めた
Warmdown (web, warmup)
個人的難易度:強(この手の問題が初だったため)
Warmdown = Warmup + Markdown
Challenge: http://34.146.192.216:3000
Admin bot: http://34.85.61.65:1337
入力内容をMarkdown形式でレンダリングし、そのプレビューとHTMLソースを表示するWebサイトChallengeが与えられる。
さらに、任意のWebサイトにアクセス可能なAdmin botも与えられる。
1. XSS脆弱性を見つける
まずChallengeを確認する。
Markdownのパース処理においてURLのサニタイズが行われていないため、不正なMarkdownを入力することでXSSが可能である。
今回は画像の表示機能を悪用する。
Markdownではと記述すると<img src="URL" alt="alt-text">というHTMLソースに変換される。
ここでと入力すると、<img src="存在しないURL" alt="alt-text" onerror="任意のjsコード">に置換されるから、任意のjsコードが実行可能となる。
これを利用し、cookieを適当なサイト(例:webhook.site)に送りつけてやれば、そこからcookieを取得することができる。
以下が実際に使用したペイロードである。

このペイロードをChallenge側でRenderすると、以下のURLに遷移し、プレビューとHTMLソースが確認できるようになる。
このURLは次の手順で利用するため、メモしておく。
2. XSS脆弱性を含むリンクをbotに踏ませる
Admin botのソースコードbot.jsを見ると、このbotがcookieにflagをセットしたうえで入力URLにアクセスすることがわかる。
そのため、先ほどのURLをこのbotにクロールさせれば、webhook.siteにクエリ文字列としてcookie(=flag)が届く。
FLAG=IERAE{I_know_XSS_is_the_m0st_popular_vu1nerabili7y}
※ここで注意点として、cookieのドメインはwebと指定されている。
つまりwebというドメインにしかcookieが送信されないので、先ほどのURLのドメイン名をweb:3000に変える必要がある。
rev rev rev (rev, warmup)
個人的難易度:瞬殺
Reverse it.
与えられた暗号化スクリプトの処理を逆順に行うだけ。
solverを以下に示す。
z = [-246, -131, -204, -199, -159, -203, -201, -207, -199, -159, -204, -158, -155, -205, -211, -206, -201, -206, -205, -211, -158, -159, -207, -202, -211, -199, -206, -155, -206, -211, -204, -200, -200, -200, -203, -208, -159, -199, -133, -187, -191, -174, -187, -183] y = [~i for i in z] x = [i^0xff for i in y] x.reverse() flag = [chr(c) for c in x] print("".join(flag))
IERAE{9a058884-2e29-61ab-3272-3eb4a9175a94}
Length Calculator (pwn, warmup)
個人的難易度:瞬殺(原因究明には数時間かかった)
This is my first C code. Why is this language so bothering when handling strings???
nc 34.146.219.32 33334
なんとなく0を入力したらflagをゲットしてしまった・・・
IERAE{Th3_5h0rt35t_3v3r_07a972c0}
これでは不完全燃焼なので、ちゃんと原因を確認する。
原因究明
今回のバイナリのソースを見ると、SIGSEGVを出せばflagを得られることがわかる。
これを踏まえたうえで、バイナリの主な処理の流れを見てみよう。
- ユーザ入力を
unsigned int sizeに代入(0~4294967295が入る) char *buf = mmap(NULL, size, ...)で領域確保(確保に失敗すればMAP_FAILED= -1が入る)if (!buf)でエラーチェック(← 本当は-1かどうかのチェックをすべき。意味のないチェック)- ユーザ入力を
bufに代入したのち、buf[strcspn(buf, "\n")] = '\0';(← bufのアドレスが不正ならここでSIGSEGVが発生)
つまり、mmap()で確保に失敗した際にSIGSEGVが発生するとわかる。
確保に失敗するケースは以下の2つが考えられるから、このいずれかを実行すればflagが得られる。
- sizeが非常に大きく、メモリを確保できない
- sizeが0である(この場合はエラーが出ると仕様で決まっている)