以下斜め読んだ内容

pseudo translation of useful posts, book reviews, remarks,etc. twitter: feeddict

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プロダクトで使われる
  • このバグを攻撃者が利用すると。。。
    • DoSアタックができる
    • ターゲットはnetwork serviceやアプリ
    • リモートから、しかも手軽にできる
  • このブログ書いてる日にappleからパッチがでた

  • 最初に断っておく

    • バグをみつけた自分(Kenton)
      • Adam Langley先生(ImperialVioletの人)レベルの人間じゃない
    • 見つけたバグ
      • HeartbleedとかShellshock級の深刻なバグとかではない
      • 攻撃者がこのバグを突いてできることはしれてる
        • 一時的なサービス利用停止くらい
  • 全てのセキュリティバグはそれぞれ全容を書かれるべき
    • というのが持論
    • それぞれのバグから自分らが学べるように
    • 今回のバグについてのappleの記述は素っ気ない代物
      • ここからは何かを学ぶのは無理
  • それとは別の今回のバグの話なネタとしていい線いってる

  • eventドリブンなI/Oを利用するためのインターフェース

    • OSによって特色がある
    • この辺りをリサーチしてるときに発見した
      • このインターフェースを例えるならこんな感じ
    • 「これとこれと・・・がいま自分が開いてるコネクションです。こいつらからメッセージが飛んできたら起こしてくれ。それまで寝る」
  • これに対するOSの反応はさまざまで、5タイプくらいある
    • LenuxはepollBSDkqueue、winは、・・・(略)
    • メカニズムが違うだけでなく、それぞれがカバーしてるユースケースの範囲も違う。
  • ユーザは特定のメカニズムを選ばないといけない
  • 自分ら開発してるCap’n Protoのために、このosごとの違いを抽象化する仕組みを作ろうとしてた
    • Cap’n Proto
    • これ作るために各osのインターフェースの特色をきちんと理解しようと色々やってた
  • man読み比べて気づいたこと

    • OOB(Out of Band)データの扱い
    • 別名緊急データ(urgent data)
    • このデータのハンドリングについて記述は有るものもないものある、という感じ
    • tcp接続では頻度小ながら使われる機能
      • tcp接続ではデータはキューに入って順番に送られる
      • この順番待ちをスキップして送ることのできるデータ
    • OOBのことは知らなくて当然
      • telnet使うとき以外だと、はユーザは利用する機会のない機能
      • 接続先のアプリが入力を処理してないときに"ctrl+C"押すみたいなことを通知する方法が必要だから
  • ほぼ全ての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が定義されてる
    • 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データが残る読み取り前データとして残る
          • ずっと受信してるかのように振る舞う
          • 同じイベントを繰り返して発火する
        • こうして無限ループに入り込む
  • 緊急データをtcpパケットで受けとっただけでほぼ全てのイベントドリブンなネットワークアプリが無限ループに嵌るという話?
    • んなアホな?と思ってた
    • 試してみた
    • ホンマやった
      • chrome、node、nginx調べた
    • chrome
    • node
    • nginx
      • セーフ。無限ループにならなかった
      • 新しいデータを受信したときだけ意図してないイベントを受け取るように動く
      • 何度も利用できるデータがあったとしてもイベントを何度も受け取らない
        • edge-triggeredモードでkqueueが使われてるおかげ
    • nginxは無事だったが、chorme, nodeでは無限ループを生んだ
      • chromeもnodeといった有名どころが影響受けてる
  • 今回のバグはAPI設計レベルの問題
    • kernelにバグがあるわけでない
    • インターフェースが混乱気味
      • さらにちゃんとドキュメントに載っていないせいでもある
      • インターフェースのまずさのせいで複数アプリで同じバグが再現する羽目に
  • このバグを直すやり方は二手にわかれる
    • このバグの影響の出るアプリ全てを直す
      • 今後出るだろうアプリも含めて
    • APIそのものを変えて、このAPIの挙動に依存したアプリは無視
      • つまりクラッシュさせる変更をする(数は相当少ないだろうが)
  • 自分からみて正しいと思うやり方でappleはこのバグに対応してる
    • appleはインターフェース修正した
      • TCPでOOBデータ受信したときにEVFILT_READが発火しないようにした
    • appleのこの件の記述が全くようわからん
      • なんでこの問題が"state inconsistency issue"と表現できるのか
    • パッチ適用後のmacで自分でもOOBデータ送信テストしたがバグは消えた
      • OOBデータはスルーされるようになった
  • このバグの教訓
    • 混乱気味のAPIはセキュリティ問題になる
    • もしあなたのAPIを利用する多くのユーザがセキュリティバグを生むようなやり方をしてるなら、それは彼らのコードにバグがあるのではなく、APIにバグがある