こんにちは、脆弱性対応チームの石川です。7/9(金)で最終出社です。
突然ですがバグバウンティって知っていますか?脆弱性・バグを報告することで企業や団体から報奨金が貰える制度のことです。ここで誤解の無いように述べておきますが、手当り次第どのサイトでも脆弱性を探して良いわけではありません。あくまでも脆弱性の報告を受け付けていると公言している企業に限りますし、企業によって許可されている操作、その他条件が異なります。
前置きはこのくらいにして、今回は私がHackerOneでRailsの脆弱性を報告して報奨金を貰った話を紹介します。
HackerOneとは
HackerOneはバグバウンティのプラットフォームです。企業や団体はここに登録をし、脆弱性の報告を受け付けます。そして報告された脆弱性が妥当な内容であれば報告者には報酬が支払われます。個人としての登録は無料ですので気軽に登録してみてください。
脆弱性を見つけるに至った経緯
きっかけ
弊社では今年の初め頃に脆弱性対応チームが誕生しました。私もその一員です。下記の記事では同チームの取り組みを紹介していますので興味があればぜひご覧ください。 https://engineer.crowdworks.jp/entry/2021/03/02/120000
上記の記事と一部重複しますが、同チームが発足してからはコードの静的解析で分かる範囲の脆弱性対応をしていました。一通りの対応を終えた頃、ふと「クラウドワークス自体は安全に近づいたけど、Rails自体は本当に安全なんだろうか」という疑問が湧きました。そういうわけで週末にRailsの脆弱性を探してみることにしました。
どんな風に探したか
効率の良い探し方など知らなかったので片っ端からRailsのソースコードを読むことにしました。工程は至ってシンプルで、ソースコードを手元にクローンしてきてファイルを開けては読んでを繰り返します。土日の2日間で10時間くらいは使ったでしょうか。観点も至ってシンプルで「どこにユーザー入力が入りうるか」この1点に意識を注ぎながら読んでいきました。そして読み進めるうちに目ぼしいパターンを一つ見つけました。それは正規表現でした。
ReDoS(Regular Expressions DoS)
ReDoSという脆弱性を知っていますか?脆弱な正規表現に特定の入力を与えてマシンに膨大な計算を強いることで、リソースを食い尽くすことができます。下記を読むと更にイメージが明瞭になるかもしれません。
https://qiita.com/prograti/items/9b54cf82a08302a5d2c7
正規表現は難しいです。時に実装者本人でもどんなパターンを許容するか把握しきれていない場合があります。Railsにも当然のことながら正規表現がたくさん使われています。この複雑な正規表現のどれか1つはきっと脆弱性があるはずだ、という信念の元コードを読んでいきました。
問題の箇所を発見
ついに問題の箇所を発見しました。それは以下のコードです。
_raw_params = auth.sub(TOKEN_REGEX, "").split(/\s*#{AUTHN_PAIR_DELIMITERS}\s*/)
各定数はこうなっています。
TOKEN_REGEX = /^(Token|Bearer)\s+/ AUTHN_PAIR_DELIMITERS = /(?:,|;|\t+)/
それに対し以下の入力を与えると計算に大量の時間を要することに気づきました1。
"Token ." + " " * N + "." # Nは充分大きな正の整数
原因の一端はAUTHN_PAIR_DELIMITERS内に \t+
が含まれているためでした。実際、\t+
を \t
に変えると直ちに計算が終わります。
補足ですがHTTPヘッダは任意の文字数を入力できるわけではありません。validなリクエストとして受け付ける上限はwebサーバによって異なります。手近な検証環境ではpumaを使っていたため1024 * 80文字が上限であることを確認しました。
https://github.com/puma/puma/blob/3-stable/ext/puma_http11/org/jruby/puma/Http11.java#L27
その上で1024 * 80文字の細工したヘッダで検証し、やはり計算に多くの時間を要することが確認できました。
報告から解決まで
以前Railsチームに報告されたレポートを参考にしつつ翻訳ソフトをフル活用して慣れない英文をしたためました2。最低限要約、再現手順、影響の3点があれば良いでしょう。ドキドキしながら返事を待ちました。
約1ヶ月後に正式に脆弱性である旨の判断がスタッフより下されました。修正案はありますか?と尋ねられたため頭を捻って案を考えました。
問題の正規表現を再掲します。
AUTHN_PAIR_DELIMITERS = /(?:,|;|\t+)/
上述の通りAUTHN_PAIR_DELIMITERS内の \t+
がボトルネックになることは実験の結果から分かっていました。また、\t+
を \t
に変えても想定通りのパースができることが確認できたため上記の修正を提案しました。
提案から半月ほど経って修正案が認められ、更に1ヶ月ほど経って修正されたバージョンがリリースされました。修正コミットは以下です。 https://github.com/rails/rails/commit/d861fa8ade353390c4419b53a6c6b41f3005b1f2
今回得たもの
もろもろの知見
正規表現・webサーバの仕様、Railsの認証の仕組み、脆弱性報告の作法など多くの学びがありました。
達成感
報告者として名前が残ります。
自己肯定感が満たされました。なかなかユニークなアウトプットではないでしょうか。面接等で話のネタになりそうです。
お金
報奨金として500ドル貰えました。UberEats25回分くらいでしょうか。普通に嬉しいです。
タイムライン
日時 | イベント |
---|---|
2021/02/11 | 脆弱性を報告 |
2021/03/10 | スタッフによるトリアージ |
2021/03/12 | 修正の提案 |
2021/05/05 | 修正のリリース |
2021/05/18 | 報奨金の振り込み |
まとめ
いかがでしたか?お金目当てで脆弱性を探すにはあまりに費用対効果が良くないので、余程スキルが高い人でない限りオススメしません。脆弱性を見つけてOSSに貢献したい、脆弱性のスキルの腕試しをしたい、普通に暇、という方には結構オススメです。報告してくれてありがとね、と言われるだけでも結構嬉しいですよ。それでは良いバグバウンティライフを〜。
We're hiring!!!!