ichirin2501's diary

いっちりーん。

気になった

CVE-2011-4885が気になったので調べてみました。
脆弱性対策情報データベースには

PHP は、ハッシュ衝突を想定して制限を行わずにフォームパラメータのハッシュ値を算出するため、
サービス運用妨害 (CPU 資源の消費) 状態となる脆弱性が存在します。
影響を受けるシステムは PHP5.3.9未満

実際にどのような処理が行われてるのか見てみる。
公式からPHP5.2.17のソースコードをダウンロードしてきて、
ハッシュ計算を行なっているを部分を探すと、
Zend/zend_hash.h

254 static inline ulong zend_inline_hash_func(char *arKey, uint nKeyLength)
255 {
256         register ulong hash = 5381;
257 
258         /* variant with the hash unrolled eight times */
259         for (; nKeyLength >= 8; nKeyLength -= 8) {
260                 hash = ((hash << 5) + hash) + *arKey++;
261                 hash = ((hash << 5) + hash) + *arKey++;
262                 hash = ((hash << 5) + hash) + *arKey++;
263                 hash = ((hash << 5) + hash) + *arKey++;
264                 hash = ((hash << 5) + hash) + *arKey++;
265                 hash = ((hash << 5) + hash) + *arKey++;
266                 hash = ((hash << 5) + hash) + *arKey++;
267                 hash = ((hash << 5) + hash) + *arKey++;
268         }
269         switch (nKeyLength) {
270                 case 7: hash = ((hash << 5) + hash) + *arKey++; /* fallthrough... */
271                 case 6: hash = ((hash << 5) + hash) + *arKey++; /* fallthrough... */
272                 case 5: hash = ((hash << 5) + hash) + *arKey++; /* fallthrough... */
273                 case 4: hash = ((hash << 5) + hash) + *arKey++; /* fallthrough... */
274                 case 3: hash = ((hash << 5) + hash) + *arKey++; /* fallthrough... */
275                 case 2: hash = ((hash << 5) + hash) + *arKey++; /* fallthrough... */
276                 case 1: hash = ((hash << 5) + hash) + *arKey++; break;
277                 case 0: break;
278 EMPTY_SWITCH_DEFAULT_CASE()
279         }
280         return hash;
281 }

DJBX33Aという名称のハッシュ値算出方法らしい。
上記のコードは地味に高速化されているけど、計算式は以下のようになっている。
hash(0) = 5381
hash(i) = hash(i-1) * 33 + str[i] (mod 2^32
2文字に絞って、ハッシュ値が同じになるものを全探索で調べてみると、

#!/usr/bin/env python
#! -*- coding:utf-8 -*-

import itertools

def calc(s): #str -> integer
    h = 5381
    mask = 2 ** 32 - 1
    for c in s:
        h = ((33 * h) + ord(c)) & mask
    h = ((33 * h) + 0) & mask
    return h

def main():
    a = ''.join([chr(x) for x in xrange(256)])
    d = {}

    for x in itertools.product(a, repeat=2):
        s = ''.join(x)
        h = calc(s)
        if d.has_key(h):
            d[h].append(s)
        else:
            d[h] = [s]

    print d
    print len(d)

if __name__ == "__main__":
    main()
$ python hashcoli.py
{193560576L: ['\xa1\xfa', '\xa2\xd9', '\xa3\xb8', '\xa4\x97', '\xa5v', '\xa6U', '\xa74', '\xa8\x13'], 
193593345L: ['\xbf\xfd', '\xc0\xdc', '\xc1\xbb', '\xc2\x9a', '\xc3y', '\xc4X', '\xc57', '\xc6\x16'], 
193626114L: ['\xde\xdf', '\xdf\xbe', '\xe0\x9d', '\xe1|', '\xe2[', '\xe3:', '\xe4\x19'],
193658883L: ['\xfc\xe2', '\xfd\xc1', '\xfe\xa0', '\xff\x7f'],
...
8671

2文字のパターンは256*255=65280あるはずなのに、
重複しないハッシュ値は8671パターンしかない。
この2文字の文字列を利用してハッシュ衝突を起こす異なる文字列が大量に生成できます。
同じハッシュ値の集合を一つ例に出すと、
193462599L: ['G\xfb', 'H\xda', 'I\xb9', 'J\x98', 'Kw', 'LV', 'M5', 'N\x14']
これらを使ったKwLVLV, LVKwM5, M5M5Kwなどの文字列は全て同じハッシュ値になります。
そもそも、ハッシュ値の算出方法からして
ある値xに対して、2文字の文字列をハッシュ計算に適応させると、
33 * {(33 * x + str[0]) % 2^32} + str[1] (mod 2^32
合同式の性質から(途中は端折ります)
= {(33^2 * x) % 2^32} + {(33 * str[0]) % 2^32} + {str[1] % 2^32} (mod 2^32
この式を見ると、文字の値は範囲[0,255]なので、
後ろ2つの項は値が変化しないことがわかります。
33 * str[0] + str[1] = 33 * str'[0] + str'[1]
この等式が成り立つ2文字の文字列の場合、
ある値xが同じなら必ず同じハッシュ値になります。
これでハッシュ衝突を起こす異なる文字列が大量に作れるというわけです。


GETやPOSTリクエストで送信するデータはわかりましたが、
PHPはハッシュテーブルの処理をどの段階で行なっているのかが気になりました。
そこで、ハッシュテーブルの処理を行なっているであろう、
Zend/zend_hash.c に定義されていた以下の関数
_zend_hash_add_or_update
zend_hash_find
に、適当に処理を追記してコンパイルし、動作確認を行いました。
当初の予想では、スーパーグローバル変数である$_GET, $_POSTが
呼ばれた段階で処理されるものだと考えていましたが、
PHPソースコードに$_GET, $_POSTが一切記述されていなくても、
ハッシュテーブルへの追加、更新、検索処理は行われていました。
PHPで動いているなら同様にDOS攻撃が行える、ということ…。
怖すぎ。


この脆弱性に対して、対策された最新版の5.3.9では、
Added max_input_vars directive to prevent attacks based on hash collisions. (CVE-2011-4885)
ということなので、パラメータ数に制限が加わったようです(ソースは未確認)
IPAの記事には回避策として、

・リクエストごとの処理時間を制限する
・POST リクエストのサイズを制限する
・リクエストごとのパラメータ数を制限する

とありますが、
HTTP1.1の仕様上、HTTPリクエストにサイズ制限はないので、
POSTだけじゃなくて、GETもサイズ制限するという記述が必要なのでは?

urllib2周辺は触りたくないですね

またpythonでネットワークプログラミングする機会があったのだけど、
urllib2付近の処理が面倒です。いい加減、処理をまとめようと思って作成してみた。
複雑なことは出来ないけど、簡単なことならこれで十分。
Cookieはデフォルトで機能します。
POSTするデータは辞書のまま渡す、わざわざurlencodeしなくてもいい。

# -*- coding: utf-8 -*-
# python 2.7
import urllib2
from cookielib import CookieJar
from urllib import urlencode

class request():
    def __init__(self, debug=None):
        self.opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(CookieJar()))
        if debug :
            for h in self.opener.handlers:
                if isinstance(h, urllib2.HTTPHandler):
                    h._debuglevel = 1
                elif isinstance(h, urllib2.HTTPSHandler):
                    h._debuglevel = 1

    def get(self, url, _headers={}):
        req = urllib2.Request(url, headers=_headers)
        return self.opener.open(req)

    def post(self, url, _data={}, _headers={}):
        req = urllib2.Request(url, data=urlencode(_data), headers=_headers)
        return self.opener.open(req)


header = {
    'User-Agent' : 'Mozilla/5.0 (X11; U; Linux i686; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.648.205 Safari/534.16'
}

url = "https://www.hatena.ne.jp/login"
r = request(debug=True)
res = r.post(url, {'name':'ichirin2501', 'password':'****'}, header)
print res.read()

セクションヘッダテーブルのサイズを変更してみた

ELF format のセクションヘッダテーブルのセクションサイズを
変更することで、アンチデバッグに応用できないかと考え、
IDA Pro demo 6.2(Linux), GDB(7.2-51.fc14),
objdump(2.20.51.0.7-8.fc14)の各3種の動作を確かめて見ました。


実験環境
Fedora14, カーネル2.6.35.14-103.fc14.i686
メモリ 2GB
もちろん32bit環境です

続きを読む

AVTokyo2011

今年も行ってきましたー。
※発表内容については特に触れていません。



受付時の画像です。
受付開始までは列に並べとのことでしたが、特に意味は無いw
そのときに、@さんと少し雑談。
私のコミュ障発動で申し訳ない感じにorz



無線LANの情報と、タイムテーブルが記載された紙。
当然、ユーザー名とパスワードはバラバラですw。
早速iPhoneで試しに接続したあと、持ってきたノーパソで
接続しようとしたら出来ないという状況に陥りました。



始まる前の2Fの様子。
ふらふらしていたら、
@さん、@さん、@さんが見えたので一言挨拶に。



始まる前の3Fの様子。
ここで、人間違いがきっかけで@さんとファーストコンタクト。
勉強会で人間違いに会ったのはこれで2回目ですw


その後、

以上のようなやり取りを経て、会合に成功。
喋ってると、@さんが近くに!
それからすぐに発表が開始。


結構うろうろしてましたが、私が聞いたのは、
2nd Floor 13:00〜14:00
Chip & PIN is Definitely Broken: Protocol and Physical Analysis of EMV POS Devices
by Andrea Barisani & Daniele Bianco
The Story Behind The Story of "Cyber Crime"
by Daiki Fukumori


4th Floor 15:30〜17:30(休憩時間も)
Benny K. & Itaru Kamiya (Part2:Hitchhikers' guide to honeypots)
Yoshitaka Kato(Android Application Obfuscation)
Yuichi HATTORI(Eidwinds)(Privacy in Sensor Data)
wakatono(Differences of the TSC behavior between The Virtual Machine and The Real Machine)
Ryan MacArthur(A Primer On Last Branch Recording)
Eli Jellenc(TBA)


2nd Floor 17:30〜18:15
APT DNA Clustering and Defense Kungfu
by Anthony LAI & Benson Wu & Birdman


こんな感じです。ちなみに、
スタッフ様の素晴らしい仕事っぷりで、英語のプレゼンには訳が記述されていました。
去年と違って英語セッションも大きな抵抗なく話を聞くことができます。
行動は主に、先輩である@さんと、@さんと共にしていました。
今回はマルチだったので、全部聞けなかったのが心残りですね。
どうやら、パネルセッションが熱かった様子…、ぐぬぬ
あと、人数が多すぎて、場所の確保が大変でした。


最後に、チャリティーオークションの様子。

かなりの盛り上がりでしたw、是非来年もやってほしいですね。
それと、3FのLTも聞きたかったのですが、如何せん人が多すぎて断念。
そのまま先輩と晩飯のためにAVTokyoを抜けることに。


という感じの充実した1日でした。
あ、それと今回購入したTシャツですw


AVTokyo2011関連の記事(随時更新予定
AVTokyo2011のつぶやきまとめ(#AVTokyo) - Togetterまとめ
http://matsutakegohan1.typepad.jp/blog/2011/11/avtokyo-2011-daikifukumori.html
avtokyo 2011 参加してみた | Exception

mayahu32さんのセキュリティ&プログラミングキャンプ2011感想文を読んだよー

http://d.hatena.ne.jp/mayahu32/20110816/1313505335
id:mayahu32さんのセキュリティ&プログラミングキャンプ2011感想文を読ませてもらいました。
以下ネタバレになります、ご注意ください。

続きを読む

Codegate CTF 2011 Binary 200

久しぶりの解析。
解析時には、IDA Pro 6.0 demo, OllyDbg 2.01 alpha3
を使って解析作業を行いました。プラグインは使用してません。
Freeで良いのあったら是非教えてください。

続きを読む