RubyやRubyのOSSの脆弱性を見つけた話
この記事はRuby Advent Calendar 2018 - Qiitaの23日目です。
今年はRubyやOSSの脆弱性をいろいろ探していたので、その感想を。
まずはRubyから
Ruby (Cruby)
昨年公開された↑の@mametterさんの記事を見て、ちょっと調べてみようと思い今年のはじめから調査を開始。
はじめに見つけたものがWEBrickのhttp header injectionで、これは影響度低いのでは…?と思いながら報告してみたのですが結果は重複(CVE-2017-17742: WEBrick における HTTP レスポンス偽装の脆弱性について)。逆にこれぐらいでも脆弱性として扱われるかと思い、いろいろ調べてみることにしました。手法としては過去にRubyに報告された脆弱性を確認し、泥臭くドキュメントにあるメソッドをひとつずつ動作を試し続けました。
Tmpでのディレクトリトラバーサル
CVE-2018-6914: Tempfile および Tmpdir でのディレクトリトラバーサルを伴う意図しないファイルまたはディレクトリ作成の脆弱性について
hackerone.com
これって脆弱性なのと時々突っ込まれるやつです。ですよね、と自分でも思います。
これは同じ問題であったDir.mktmpdir
のほうがわかりやすくて
[vagrant@localhost ~]$ ls . [vagrant@localhost ~]$ irb irb(main):001:0> require 'tmpdir' => true irb(main):002:0> Dir.mktmpdir(['../../home/vagrant/', 'gold']) => "/tmp/../../home/vagrant/20180104-6544-1atequhgold" irb(main):003:0> Dir.mktmpdir('../../home/vagrant/silver') => "/tmp/../../home/vagrant/silver20180104-6544-1822fif" irb(main):004:0> `ls` => "20180104-6544-1atequhgold\nsilver20180104-6544-1822fif\n"
とするとシステムが提供するtmp以外の場所に作られたうえに、Rubyも自動で削除しないのでテンポラリじゃないという問題がありました。PHPのサーバなんかが一緒に動いてるとまずそうですね。
あと、ドキュメント上ではパスではなくprefixとされているのでユーザが安全と思ってしまいそうということで 、Pythonでも似たような話がされていますね。 https://bugs.python.org/issue35278
この辺の話をどこかに書こうと思っていたんでしたが忘れてました。
DirでのNUL文字の扱いの問題
CVE-2018-8780: Dir において NUL 文字挿入により意図しないディレクトリにアクセスされうる脆弱性について hackerone.com 以前FileでNUL文字の問題があったので、一応調べてみたらDirのメソッドの一部に残っていたもの。
TmpfileとDirの問題は書いてしまうことがありそうな気がするのですが、いまだ影響しているところを見たことがないですね…。
UNIX ドメインソケットでのNUL文字
CVE-2018-8779: UNIX ドメインソケットにおいて NUL 文字挿入により意図しないソケットにアクセスされうる脆弱性について hackerone.com NUL文字二つ目。UNIX ドメインソケットは普段使わないので、これが脆弱性として扱われるかよくわからなかったのですがとりあえず報告してみたら通りました。ここに任意の文字列が入る時点ですでに不味そうなので実際の影響は殆どなさそうです。
Ruby 2.6での挙動変更
Dir[]
でのNUL文字について
知らない人も結構多そうなんですがDir[]
とDir.glob()
ではNUL文字が区切り文字になります。
$ irb irb(main):001:0> Dir["/tmp\0/home"] => ["/tmp", "/home"] irb(main):002:0> Dir["/tmp"] => ["/tmp"] irb(main):003:0> Dir["/tmp/home"] => []
このため固定した文字列 + ユーザ入力の文字列
の文字列を渡すと全く別の場所が探索される可能性があります。
これはドキュメントに書かれている挙動であり仕様です。*1
singleton method Dir.[] (Ruby 2.6.0)
[PARAM] pattern: パターンを文字列で指定します。 パターンを "\0" で区切って 1 度に複数のパターンを指定することもで きます。 パターンの区切りには "\0" のみ指定できます。 配列を指定することで複数のパターンを指定できます。
しかし、対策しているコードは今のところ見たことがありません。
これはちょっと使う側として辛いので、twitterでusaさんにRuby3で廃止できないか提案してみたところ、チケットを作ってくださって採用されました。Feature #14643: Remove problematic separator '\0' of Dir.glob and Dir.[] - Ruby trunk - Ruby Issue Tracking System
今月リリースのRuby 2.6からNUL文字が渡されると警告が出るようになります。(ruby/NEWS at v2_6_0_rc2 · ruby/ruby · GitHub)
Rubygems
rubygems
2月のリリースのものと似たものを報告していて重複に。
3/28に公開されたRubyの脆弱性情報についてのポエム的解説 - pixiv insideと見解が違うのが、サーバ側だけでなく普通の利用者も危険なはずという点です。gemのinstallとかunpackで問題がでるので。
65534倍効率的なブルートフォース
rubygems.orgでAPIキーの確認時に渡されるパラメータの型をチェックしておらず、配列を投げてやるとそれがそのままモデルのfindに渡されるため、どれか一つでも正しいものがあれば認証が通ってしまっていました。rubygems.orgではrack-attackが入ってたんですが、これを使うと1つのリクエストで効率的にブルートフォースができるのですり抜けます。型のチェックがゆるい言語で、認証系を独自に実装しているようなところではたまにありそうな問題です。
どれぐらい渡せるか限界を探ると、65534倍まで効率的なブルートフォースが可能でそれ以上の値はエラーとなりました。この場合はAPIキーがSecureRandom.hex(32)
で生成されていたので、65534倍効率的な程度ではほぼ影響はなかったと思われます。SecureRandom.hex(8)
ぐらいではまずかったかもしれません。
Rails 5.2.2からは渡されるパラメータが多すぎるときにbindしなくなったらしいので、より大きな配列を投げられるかもしれませんがまだ確認していません。
minitarでのKernel.open*2
input,outputにKernel.open
が使われていたのでissueで書いてみたところ意図的なものとのこと。ただ、gemを使う側が気にしてにないと危ないのは確かなのでreamdeに追記されました。
geminabox
Avoid XSS vulnerability on /reindex · geminabox/geminabox@50da77f · GitHub エラーからのXSS。異常系からのXSSはたまに見かけますね。
Discourse
hackerone.com いくつか報告を出して通ったのですが、公開扱いにはなっていないので詳細については省略。 報告に書いた説明がいくつか変だったり調査不足だったところがあったので、もっとちゃんと書けばもっと報奨金もらえたのではというのが反省点。
Heroku
bugcrowd.com 会社で公開しているOSSにも報奨金を設定している企業はいくつかありまして、Herokuもそのうちの一つです。 RubyのOSSのものにいくつか出しました。これも公開されていないので詳細は省略。
Rails
pgでのNUL文字
これはCVEが出てないので知らない人も多そうです。 内容としてはこれもまたNUL文字で、文字列中にNUL文字があるとそこで切られたクエリが送信されていました。
> Article.where("title = :title", {title: "test title\0suffix"}) Article Load (0.4ms) SELECT "articles".* FROM "articles" WHERE (title = 'test title') LIMIT $1 [["LIMIT", 11]] => #<ActiveRecord::Relation [#<Article id: 1, title: "test title", text: "dummy", created_at: "2018-08-13 13:31:37", updated_at: "2018-08-13 13:31:37">]>
RailsであればNUL文字は保存ができないので影響があるのは検索するときだけなのですが、文字列をブラックリストやsuffixでバリデーションしてるとすり抜けます。これは書いてる場所がたまにあると思います。*3
Rails 4.xではpg1.xに対応してないようなので、まだRails5に上げていない場合は頑張って諸々のバージョンを上げるかパッチでなんとかするかしか……
ちゃんと調べてなかったのでRailsのプログラムに報告してしまったのですが、正確にはpg gemの問題でした。ですが、pgのチームに連絡してもらった上で報奨金ももらえました。
Active StorageでのXSS
Rails5.2で追加されたActive Storageではファイルアップロードを実装するときにおこる問題をいろいろ踏んでいました。結構頑張って調べたんですがこれも重複。
報奨金
RubyのOSSでの報奨金は合計で$7,584となりました。
感想
Rubyに詳しくなった
Rubyに限らない話なんですが脆弱性調査をしていると、マニアックな挙動を調べるモチベーションが高くなります。ドキュメントのメソッドの挙動をひたすら確認していったのですが、途中これは一体何の修行なんだろうなどと思っていました。どんな物があるのか去年よりだいぶ詳しくなったと思います。 と言いつつ未だにmapの引数の順番で迷いますが。*4
脆弱性としての判定
普段はweb周りのXSSなどを調べていてそれ以外の用途には疎いため、脆弱性に当たるかどうかの判定に悩む場面が何度かありました。仕様なのかバグなのか微妙だけども影響あるならば報告出しておいたほうがいいか、という思考で迷う場合にはとりあえず報告していました。そのほうがシステムの安全性としては高くなるのでしょうがないかなとも思います。報告を受ける側の負荷は高そうですが……
自分が使うものを安全にする
クリティカルな脆弱性を見つけられないのはバグハンターとしては辛いものですが、利用者としては幸いです。 YAML.safe_yamlをなんとかバイバスできないかひたすら探していたり、rubygems.orgでRCEできないかなど結構調べていたのですが見つけることができず、悔しい一方利用者としては安心感が増します。