zer0pts CTF 2020 writeup
2020年3月7日9時~3月9日9時に開催されたze0pts CTF 2020に参戦しました。
ここ最近は卒論だったり一日中バイトだったりでCTFの大会に参加するのはかなり久しぶりでした。
期間が空いて色々忘れていることも多そうだったので1問も解けないのではないかと不安な部分もありましたが、4問解いて88位という結果を残すことが出来ました。個人的には健闘したのかなと思います。
まぁwelcomeとsurveyは入力するだけなので、解いたのは実質2問ですね。
とはいえ、全体として比較的難易度が高そうな大会の中で2問解くことが出来たという事実は私の中ではそれなりに大きなことです。
ということで、今後の自分とCTF初心者に向けてwriteupを残しておきます。
LOCKED KITKAT
とりあえずAutopsyで開いてみます。
とは言いつつも何も分からないので1つ1つ見ていくと「locksettings.db」というファイルを発見しました。
「lock_pattern_visible_pattern」とか、いかにも怪しい。
しかし、それに関連するファイルは見つかりません。そこでパターンロックに絞ってggりまくります。
2時間以上調べて以下の論文にたどり着きました。
オッフェンブルク応用科学大学の論文です。
Android Security Creation Of A Virtual Learning Environment
https://d-nb.info/1154866548/34
28ページに以下の説明文がありました。
Android saves passwords and PINs as a concatenation of both, a SHA-1 as well as a MD5 digest. Before generating the digests, the password gets salted. This means, a random alphanumeric string is appended to the PIN or password. In order to verify a typed-in PIN or password, Android stores this salt under the lockscreen.password_salt key in the secure table of the system’s setting provider in Android versions < 4.2 or in the dedicated database /data/system/locksettings.db in Android versions starting from 4.2 and above. After successfully generating the concatenated hashes of the salted PIN or password, it is saved within the file /data/system/password.key.
Patterns are saved in a more simple way and without using the additional security feature of salting. They are only hashed once using the SHA-1 algorithm and then directly saved within the file /data/system/gesture.key.
みらい翻訳に投入します。
AndroidはパスワードとPINを、SHA-1ダイジェストとMD5ダイジェストの両方を連結して保存します。ダイジェストを生成する前に、パスワードはソルトされます。つまり、ランダムな英数字ストリングがPINまたはパスワードに付加されます。入力されたPINまたはパスワードを確認するために、Androidはロック画面の下にこのソルトを保存する。Androidバージョン4.2以前のシステムの設定プロバイダ、または専用のデータベース/データ/システム/ロック設定のsecureテーブルにあるpassword_saltキー。db 4.2をサポートしている。ソルトされたPINまたはパスワードの連結ハッシュが正常に生成されると、ファイル/データ/システム/パスワード内に保存されます。ボタンを使用します。
パターンはより単純な方法で保存され、塩漬けという追加のセキュリティー機能は使用されません。これらはSHA-1アルゴリズムを使用して一度だけハッシュされ、ファイル/data/system/gesture内に直接保存されます。ボタンを使用します。
普通のパスワードとPINではソルト付きハッシュで保存するものの、パターンロックのときはソルトは使わずにSHA-1で一回だけハッシュ化して保存するようです。
また、ハッシュ値は/data/system/gestureに保存されるようです。
Autopsyで/data/system/gestureを確認します。
/data にはありませんでしたが、/system/gesture.keyというファイルがありました。
しかし中身は見えません。
ここで先程の論文を見つける前にスルーしたサイトの存在を思い出しました。
https://infosecaddicts.com/bypass-pattern-locks-android/
まずは普通にマウントしてローカルにgesture.keyを取ってきます。
$ mkdir mnt $ sudo mount -t ext4 -o loop android.4.4.x86.img mnt $ cp mnt/system/gesture.key gesture.key
次にパターンとSHA-1の組み合わせ一覧をありがたく頂戴します。
$ git clone https://github.comAndroidGestureBreaker.git
あとはgrepでgesture.keyと一致するものを探すだけです。
grep -i `xxd -p gesture.key` AndroidGestureBreaker/AndroidGestureSHA1.txt
実行結果。
どうやら 432675 だったようです。
これを提示されていたページで入力してあげるとFlagが出てきました。
Flag:zer0pts{n0th1ng_1s_m0r3_pr4ct1c4l_th4n_brut3_f0rc1ng}
CAN YOU GUESS IT?
ソースを確認してみると、入力した値とサーバーがテキトーに作った値のハッシュ値が一致するとFlagを貰える模様。
一瞬DDOS的にやるのかとも思いましたがそれはないはずです。現実的じゃないですし。
次にguess変数でsecret変数を参照させることが出来ればハッシュ値を一致させることが出来そうだと考えましたが、POSTでどうあがいても文字列としてみなされてしまいます。
そして結局一番最初の処理まで戻ります。
preg_matchです。
どうやらPHP_SELFの末尾がconfig.phpだと弾かれるようです。
とりあえず末尾を変えて入力してみるとpreg_matchを回避することは出来ました。
当然ながらconfig.phpは表示されません。
しかし、存在しないファイルを開こうとしてエラーが発生しているため、ここに脆弱性があることは分かりました。
末尾がconfig.phpでなければいいので、次はファイルの場所まで迂回してみました。
しかし、index.php/config.php/hoge/../?source のようにしてみても、結局最短経路に補正してくれてしまうのでうまくいきません。
さらに試行錯誤していくと、ついにbasename関数の存在に気付きました。
とりあえずググって徳丸先生のもとにご案内されます。
https://blog.tokumaru.org/2015/02/phpbasename.html
円記号U+00A5は、basenameの処理対象の文字ではない
なんと...処理対象ではない....
てことでconfig.phpの後ろに /%A5 を入れてみます。
index.php/config.php/%A5?source
Flagが表示されました。
Flag:zer0pts{gu3ss1ng_r4nd0m_by73s_1s_un1n73nd3d_s0lu710n}
つまり
config.php/hoge → hoge
config.php/%A5 → config.php
%A5(¥)は処理出来ないからその次のパスをベースとするわけですね。
感想
webのnotepadにもそれなりの時間をかけて挑戦していましたが、結局解くことは出来ませんでした。
しかしながらCTFが終わった今、ほかの方のwriteupを見て方針は合っていたことが分かったので、もう少し粘ってみればよかったかな~と思っているところです。
結局解けた問題は2問だけでしたが、1年ぐらい前の自分だった1問も解けていなかったかもしれません。
少なからず成長しているはずだと自分を鼓舞しつつ、就職までの残り約3週間を大切に過ごしたいと思います。