Kenton Varda「OSXのカーネルバグを使ってChromeとNode.jsを無限ループに嵌らせる」
sandstorm.ioの2015.4.8のブログエントリ
- Sandstorm News: Remotely send Chrome and Node.js into infinite loops via this one weird OSX kernel bug
- osx/iosの非同期i/oを捌く
kqueue
のバグを見つけた件について - 見つけた経緯、振舞いの詳細、バグを使ってできる攻撃、影響を受けるアプリ、書いた理由(アップリのdescriptionは素っ気なさすぎ)、学んだこと、各所パッチ出したからこのネタ解禁できる、等々
kqueue
を使ってるos/software全てが影響受けるわけでないこと(Darwinのみ、nginxはセーフとか)もさらっと書かれてて勉強になる
以下斜め読んだ内容
- 数ヶ月前に発見したDarwin kernelのバグ
- セキュリティバグ
- Dawrin kernel自体はほぼ全てのappleプロダクトで使われる
- このバグを攻撃者が利用すると。。。
このブログ書いてる日にappleからパッチがでた
- ってことで、このネタでようやくブログが書ける
- About the security content of OS X Yosemite v10.10.3 and Security Update 2015-004 - Apple Support
- 「CVE-2015-1105」をリンク先のページで検索してくれると俺とsandstorm.ioが載ってる
最初に断っておく
- バグをみつけた自分(Kenton)
- Adam Langley先生(ImperialVioletの人)レベルの人間じゃない
- 見つけたバグ
- HeartbleedとかShellshock級の深刻なバグとかではない
- 攻撃者がこのバグを突いてできることはしれてる
- 一時的なサービス利用停止くらい
- バグをみつけた自分(Kenton)
- 全てのセキュリティバグはそれぞれ全容を書かれるべき
- というのが持論
- それぞれのバグから自分らが学べるように
- 今回のバグについてのappleの記述は素っ気ない代物
- ここからは何かを学ぶのは無理
それとは別の今回のバグの話なネタとしていい線いってる
eventドリブンなI/Oを利用するためのインターフェース
- OSによって特色がある
- この辺りをリサーチしてるときに発見した
- このインターフェースを例えるならこんな感じ
- 「これとこれと・・・がいま自分が開いてるコネクションです。こいつらからメッセージが飛んできたら起こしてくれ。それまで寝る」
- これに対するOSの反応はさまざまで、5タイプくらいある
- ユーザは特定のメカニズムを選ばないといけない
- 自分ら開発してるCap’n Protoのために、このosごとの違いを抽象化する仕組みを作ろうとしてた
- Cap’n Proto
- これ作るために各osのインターフェースの特色をきちんと理解しようと色々やってた
man読み比べて気づいたこと
ほぼ全てのevent通知APIでは、通常データとOOBデータは違うイベントを発火する
poll()
と後継のepoll
の場合- 通常データは
POLLIN
イベント - OOBデータは
POLLPRI
イベント
- 通常データは
アプリがOOBデータ受信を想定していない場合
- アプリはOOBデータのイベント通知について問い合わせをしない
- kernelはOOBデータのイベントをスルーするか、通常データストリームに含めるかのいずれか
各os別の
kqueue
- BSD
- OOBデータハンドリングについて曖昧
- FreeBSD`
- kqueue(2)
- OOBデータハンドリングについて記載ゼロ
- 調べた限りだと、OOBデータイベントがサポートされてない
- DragonflyBSD
- OOBデータイベント
EVFILT_EXCEPT
が定義されてる
- OOBデータイベント
- DragonFly On-Line Manual Pages : kqueue(2)
- Darwin(OSX/iOS)
- ドキュメントにはOOBデータのことが書いてないが実装されてる機能あり
EVFILT_READ
イベント- 通常の(つまりin-band)データ受信で発火
- どうやらOOBデータ受信した時も
EVFILT_READ
イベントを発火してる- ただフラグ(
EV_OOBAND
)をつけてイベントを発火してる
- ただフラグ(
- 通常の流れ
- 通常データ受信
recv()
コールして受信データ読み込み- それでおしまい
- 再びイベントループへ戻る
level-triggered
モードでkqueue
使ってる場合- 簡単なのでたいていの人が使うモード
- osは読み取りした後も、OOBデータが残る読み取り前データとして残る
- ずっと受信してるかのように振る舞う
- 同じイベントを繰り返して発火する
- こうして無限ループに入り込む
- BSD
- 緊急データをtcpパケットで受けとっただけでほぼ全てのイベントドリブンなネットワークアプリが無限ループに嵌るという話?
- んなアホな?と思ってた
- 試してみた
- ホンマやった
- chrome、node、nginx調べた
- chrome
- oobバイトをレスポンスとして返すhttpサーバーにアクセス
- アプリ全体がハングアップ
- アクセスしたタブだけ、とかでない。chrome全部がハングアップ
- 強制終了するしかなくなる
- この実験から知ったこと
- セキュリティissueとしてcloseされた
- Issue 437642 - chromium - Security: OSX: TCP OOB data will hang main process event loop - An open-source project to help move the web forward. - Google Project Hosting
- node
- クライアントからOOBデータ受けとったサーバも無限ループに突入
- 他のネットワークのハンドリングもハングアップする
- このissueもfix済
- nginx
- セーフ。無限ループにならなかった
- 新しいデータを受信したときだけ意図してないイベントを受け取るように動く
- 何度も利用できるデータがあったとしてもイベントを何度も受け取らない
edge-triggered
モードでkqueue
が使われてるおかげ
- nginxは無事だったが、chorme, nodeでは無限ループを生んだ
- chromeもnodeといった有名どころが影響受けてる
- nginxは無事だったが、chorme, nodeでは無限ループを生んだ
- 今回のバグはAPI設計レベルの問題
- kernelにバグがあるわけでない
- インターフェースが混乱気味
- さらにちゃんとドキュメントに載っていないせいでもある
- インターフェースのまずさのせいで複数アプリで同じバグが再現する羽目に
- このバグを直すやり方は二手にわかれる
- 自分からみて正しいと思うやり方でappleはこのバグに対応してる
- このバグの教訓