気になった
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()
AVTokyo2011
今年も行ってきましたー。
※発表内容については特に触れていません。
受付時の画像です。
受付開始までは列に並べとのことでしたが、特に意味は無いw
そのときに、@ghetto2199さんと少し雑談。
私のコミュ障発動で申し訳ない感じにorz
無線LANの情報と、タイムテーブルが記載された紙。
当然、ユーザー名とパスワードはバラバラですw。
早速iPhoneで試しに接続したあと、持ってきたノーパソで
接続しようとしたら出来ないという状況に陥りました。
始まる前の2Fの様子。
ふらふらしていたら、
@yasulibさん、@nao_pcapさん、@R0zetさんが見えたので一言挨拶に。
始まる前の3Fの様子。
ここで、人間違いがきっかけで@kumagiさんとファーストコンタクト。
勉強会で人間違いに会ったのはこれで2回目ですw
その後、
ミスって前に並んでたwwwww 俺きめえ
@nekonyaaaaaaaan 特徴教えてw
2011-11-12 12:04:49 via Twitter for iPhone to @nekonyaaaaaaaan
@ichirin2501 赤いチェックのマスクです!!
2011-11-12 12:06:04 via twicca to @ichirin2501
@nekonyaaaaaaaan 私はださい服装です。手提げの紺色の鞄と、白と紺の縦縞の服装。
2011-11-12 12:17:52 via Twitter for iPhone to @nekonyaaaaaaaan
喋ってると、@a4lgさんが近くに!
それからすぐに発表が開始。
結構うろうろしてましたが、私が聞いたのは、
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
こんな感じです。ちなみに、
スタッフ様の素晴らしい仕事っぷりで、英語のプレゼンには訳が記述されていました。
去年と違って英語セッションも大きな抵抗なく話を聞くことができます。
行動は主に、先輩である@kmuroiさんと、@nekonyaaaaaaaanさんと共にしていました。
今回はマルチだったので、全部聞けなかったのが心残りですね。
どうやら、パネルセッションが熱かった様子…、ぐぬぬ。
あと、人数が多すぎて、場所の確保が大変でした。
最後に、チャリティーオークションの様子。
かなりの盛り上がりでした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感想文を読ませてもらいました。
以下ネタバレになります、ご注意ください。