CTFはかじったことはあったけどどんな解き方があるかとか全然わからなかったので参加してきた。
2016.seccon.jp
講義のおかげか、運良くぽんぽん問題が解けて3位になれました。
講義も演習も楽しく取り組むことができました。運営の皆様お疲れ様でしたm( )m
Write up
自分が解けて覚えてるものだけ書いておきます。
解けなかったのはファイル持ち帰ったので時間あるときに取り組んでまたWrite up書きたいと思います。
解けた問題はこんな感じ。
Misc 100 Welcome
解くだけ。サービス問題。
Misc 200 CountUp Game
問題文の案内通り、サーバに接続するとゲームが始まる。
お互い、3つまで数字を言って21を言ったら負けなゲーム。
ただし必ず相手が先攻で始まる。
問題数が多かったらプログラムで自動化しないと無理だったけど10問だったので手動で解いた。
基本的に相手に5, 9, 13, 17を言わせるようにすれば必ず勝てるのでその通りに解いていく。
Web 100 Classical Injection
ユーザ名とパスワードを入れるテキストフォームが表示される。
データベース系と見てシングルクォーテーションを入れるとエラーが表示される。
そこで以下のようなSELECT文が実行されると仮定してみる。
$sql = "SELECT * FROM users WHERE loginid = '${loginid}' AND password = '${password}'";
そこでユーザ名のテキストフォームに' OR 1 = 1;--
と打ち込んで後半の条件文を無視するようにインジェクションするとログインができてflagが表示される。
Web 100 May the extensions be with you.
問題忘れてしまいました…誰かWrite up頼むorz
Web 200 もぐもぐ(・~・)
アプリにアクセスすると検索フォームが表示される。
適当に文字を打って検索すると検索結果がないと言われる。
SQLインジェクションを疑いシングルクォーテーションを入れるとPHPのエラーが表示される。
エラーから読み取るにSQLの実行結果からループで結果を取って表示しようとしてるっぽい。(たしかfetchArray()
でエラーがでてた)
とりあえず講義で教わったようにテーブルスキーマを盗めないか試す。
以下の文字列を検索フォームに入れる。
' UNION SELECT null, null, null, null; #
データベースが何かの特定はエラー分からは推測できないのでMySQL, PostgreSQL, SQLiteの可能性を順番に試した結果、MySQLぽかった。
ここでnullの値を変えていくと(確か)4つでページにデータが表示された。
ので次に以下でテーブル一覧を取る。
' UNION SELECT null, table_name, null, null FROM INFORMATION_SCHEMA.COLUMNS; #
これによりsecret_umasugi
というテーブルがあることがわかる。
ちなみにtable_name
を2番めにしてる理由は、1番目だと画面に描画されなかったからです。(もしかしたらソースにはあった?)
というわけで以下のテキストで検索してフラグゲット。
' UNION SELECT * FROM secret_umasugi; #
ちなみにたまたまカラム数が一致したので通ったけどもしこれで通らなかったらカラムの名前を調べたりGROUP_CONCAT()
あたりで頑張ることになりそう。
Web 300 Easy SQL Injection
ページにアクセスするとLogin ID, Display name, Passwordを入力できる。
また、ログインボタンと登録ボタンが存在する。
とりあえず普通に登録してみるとログインできることがわかる。
問題名が問題名なのでひとまずシングルクオーテーションを入れてみるとDisplay nameのときだけバリデーションエラーのメッセージが微妙に違うことがわかる。
そこでこんなSQLが書かれていると仮定する。
$sql = "INSERT INTO users VALUES ('${loginId}', '${displayName}', '${password}')";
そこでこんな文字列をDisplay nameのフォームに入れて登録を試す。
', 'pass'); --
これで登録が通り、パスワードがパスワードのテキストフォームに入れたものでなく、'pass'になる。(確か)
ちなみにこの時にコメントアウトを#
にして試せばMySQLでないことがわかるのでSQLiteかPostgreSQLのどちらかに絞れる。
ここにSQLインジェクションできることはわかったので次にこのDisplay nameにデータベースから抜いた情報を入れられないか試す。
テーブル情報を引き抜くのを講義資料を元にSQLite, PostgreSQL両方試し、結果的にSQLiteだった。
こんな感じ。
'||(SELECT sql FROM SQLITE_MASTER), 'pass'); --
こうしてログインが成功すると最初はDisplay nameに何もでないがもう一回ログインし直すとusersテーブルのスキーマ情報が抜ける。
あとはそれを元にもう一度インジェクションし、ログインし直して情報を抜いてフラグゲット。
ユーザ名をadminにした理由は勘なのでこれが外れたらLIMIT, OFFSET句あたりを使って一行ずつ抜いていくとかになるんですかね。
'||(SELECT password FROM users WHERE loginid='admin'), 'pass'); --
Forensics 100 みつけてみよう
pcapファイルなので中身をWireSharkで開くとHTTP通信を何回かしている。
とはいえ回数多くて全部読むのがめんどくさくてstrings for100.pcap | grep ctf4b
したら1つだけ中身のあるフラグがあった。
Forensics 200 漏洩した情報を特定せよ
この問題が個人的には面白かった。
まずpcapファイルをWiresharkで開き、ざっと見てみる。
特に怪しいものはなかったのでHTTP通信にフィルタするとこんな感じになる。
通信の流れを見るとクライアントが
GET /c2/xor_cipher_key/
POST /c2/leak_info/
としてるのがわかる。問題のシナリオを推測すると情報を漏洩させた人は
- 情報を他社にリークする
- その際、情報を暗号化して送信した
- 暗号化の手段はXOR演算
と推測できる。
まずGET /c2/xor_cipher_key/
のレスポンスを見るとこんな感じ。
ff
とだけある。これがXOR演算のためのキーだと仮定する。
その後のPOST /c2/leak_info/
のリクエストボディを見る。
���˝��ϊ������̛̛��ύ��̜�����ϑ��
というURLエンコードされた値を送信していることがわかる。
先程のシナリオから逆算するとこの値は
- 情報に
ff
を用いてXOR演算する
- その値をURLエンコードする
ことによって得られたと仮定できる。
なのでこれを読み解くには逆のことをすればいい。
超適当スクリプトを書いて実行する。
<?php
$data = '���˝��ϊ������̛̛��ύ��̜�����ϑ��';
$bytes = explode('%', $code);
$answer = '';
foreach ($bytes as $b) {
$num = hexdec($b);
$const = hexdec('ff');
$result = $num ^ $const;
$answer .= '%'.dechex($result);
}
echo $answer;
これで元のデータをURLエンコードした文字列が得られるのであとはこんな感じでコマンドを叩いてフラグゲット!
なぜかcが抜けてたけど足してSubmitしたら通った。
Binary 100 HiddenFlag
これを終了3分前に焦って解いて、結果的に3位になれた。
3分で解いたという時点でお察しで、こんなコマンド叩いて解きました。。。
strings bin100_1 | grep ctf4b
stringsコマンド便利すぎた。
参加してみて
CTF4Bはすごく前から参加してみたくて、今回予定も合って参加できてよかった。
短い時間の中で講義も丁寧にやってもらって資料も充実していて、演習ではそれが生かせる問題ばかりだった。
とはいえまだまだビギナーの身なので大会か何かあればちょろっと参加してみたい気持ちです。
参加した皆様、お疲れ様でした!