Redis Lua scripting: several security vulnerabilities fixed

Salvatore Sanfilippo

RedisのLuaスクリプティング:複数のセキュリティ脆弱性を修正

原文は Salvatore Sanfilippo により に公開されました。 このブログを購読する

1ヶ月強前、Apple Information Securityチームからメールを受け取った。監査の過程で同チームがRedisのLuaサブシステム、具体的にはcmsgpackライブラリにセキュリティ上の問題を発見したのだ。このライブラリはLua本体の一部ではなく、私自身が書いたMessagePackの実装である。機能拡張を目的としたプルリクエストをマージする過程で、セキュリティ上の問題が混入してしまった。その後、同じチームがLuaのstructライブラリでも新たな問題を発見した。このライブラリもまた、少なくとも我々が使用しているLuaのリリースではLua本体の一部ではない。Redisユーザーに提供するLuaインタプリタに機能を追加するため、ソースコードをそのままLua実装内に組み込んでいたのだ。その後、私自身が同じstructパッケージで別の問題を見つけ、さらにAlibabaチームがcmsgpackやLua APIを使用する他のコードパスで多数の問題を発見した。こうして短期間のうちに、私はLua関連の脆弱性の山を抱えることになった。

これらの脆弱性が特に関係するのは、クラウド上でマネージドなRedisサーバーを提供しているケースだ。発見された脆弱性がRedisサーバーへの直接アクセスなしに悪用される可能性は極めて低いからである。多くのRedisユーザーはcmsgpackやstructパッケージ自体を使っておらず、使っている場合でも信頼できない入力を渡すことはまずないからだ。しかしクラウドプロバイダーにとっては話が違う。彼らはサービスを契約したユーザーに対してRedisインスタンスを公開しており、時にはマルチテナント構成で運用している。そのユーザーはそうしたRedisインスタンスに対して任意のデータを送信でき、脆弱性を突いてメモリを破壊し、Redisプロセスを侵害し、最終的にはプロセスを完全に乗っ取ることも可能になる。

例えば、このシンプルなPythonプログラムは、cmsgpackの脆弱性の1つを使ってRedisをクラッシュさせることができる[1]。

[1] https://gist.github.com/antirez/82445fcbea6d9b19f97014cc6cc79f8a

しかし、自分たちのインスタンスに何が送られるかを制御できている通常のRedisユーザーから見れば、リスクは限定的だ。信頼できないデータをstruct.unpack()のような関数に渡す場合、しかもフォーマット引数で特に危険なデコードフォーマット「bc0」を指定した場合に限られる。

アドバイザリの調整

Apple Information Securityチーム、私、そしてRedisのクラウドプロバイダーの間での協力的で友好的なコミュニケーションのおかげで、世の中の主要なRedisプロバイダーすべてに連絡を取り、バグが公開される前にシステムにパッチを当てられるよう、脆弱性情報の公開タイミングを調整しようと試みた。プロバイダーが簡単にシステムへ適用できるよう、単一のパッチを提供した。最終的に昨日から今日にかけて、セキュリティ修正を含むRedis 3、4、5の新たなパッチリリースを用意した。このブログ記事を読んでいる時点で、それらはすべてすでにリリースされている。残念ながら、小規模あるいは新興のクラウドプロバイダーには連絡を取ることができなかった。Redis Labs、Amazon、Alibaba、Microsoft、Google、Heroku、Open Redis、Redis Greenとのコミュニケーションをこなすだけでもすでに膨大な作業であり、情報共有の範囲をさらに広げればリークのリスクはさらに高まる(各社で多くの関係者がプロセスに関わっていたため)。もしあなたがRedisプロバイダーで、今日初めてこの脆弱性について知ったのだとしたら申し訳ない。最善は尽くしたつもりだ。

この問題に関する助言と支援について、Apple Information Securityチームと他のすべてのプロバイダーに感謝したい。

Luaにまつわる問題

率直に言って、RedisのLuaエンジンが設計されたとき、顧客対クラウドプロバイダーというセキュリティモデルは想定されていなかった。どちらかといえば、Redisサーバーを触る相手は信頼できるという前提があった。だから一般的に、Luaライブラリはセキュリティの観点から厳密に精査されてこなかった。当時の感覚としては、Redis APIにアクセスできるのであれば、そもそももっとひどいことだってできてしまう、というものだった。

しかしその後状況は変わり、クラウドプロバイダーはマネージドなRedisインスタンスを提供できるよう、顧客に公開するRedisのAPIを制限するようになった。とはいえ、CONFIGやDEBUGのようなコマンドは拒否できたとしても、EVALやEVALSHAの公開を避けることは現実的にできない。RedisのLuaスクリプティングは、コミュニティで最もよく使われている機能の一つなのだ。

こうして私自身も気づかないうちに、Redisがエンドユーザーに公開・提供される形態の変化に伴い、Luaライブラリも、本来Redis側で対処すべきセキュリティモデルにおける攻撃経路となっていった。前述のとおり、このモデルで影響を受けるのはRedisユーザーというよりマネージドなRedisの「クラウド」プロバイダーの方だが、いずれにせよ対処すべき問題であることに変わりはない。

Luaスクリプティングという特定の問題に関して、クラウドプロバイダーのセキュリティの現状を改善するために何ができるだろうか。今後数か月で取り組みたいことをいくつか挙げておく。

  1. Luaスタックの保護。Luaは、多少の速度低下と引き換えに、LuaスタックAPIの誤用が起こらないようにコンパイルできるようだ。公平に見て、Luaがスタックについて置いている前提は少々安易すぎると思う。ライブラリ開発者は、新しい値をプッシュするのに十分な空きがスタックにあるかを常にチェックしなければならないからだ。同じ抽象度にある他の言語では、このような問題のないC APIを備えている。そこで、Luaの低レベルC APIにより多くの安全策を加えた際の速度低下が許容範囲かどうかを検証し、可能であれば実装したい。
  2. セキュリティ監査とファズテスト。時間は限られていたものの、すでにLua structライブラリでファズテストを実施した。この分野で他のバグがないかチェックする活動を続けていく。まだまだ多くの問題が残っているはずで、今回見つかったのは特定のバグの集合に過ぎないのは、スクリプティングサブシステムを調査する時間がそれ以上なかったからにすぎない。だからこれは今後実施される重要な活動だ。今回と同様、活動の最後にはRedisベンダーと調整し、適時にパッチを適用できるようにする。
  3. Redisユーザーの観点からは、信頼できないデータをLuaエンジンに送る際に、データが改ざんされていないことを保証するためにHMACを使うことが重要だ。例えば、ユーザーの状態をユーザーのCookie自体に保存し、後でデコードするというよく使われるパターンがある。そうしたデータが後にRedis Lua関数の入力として使われることがある。これは、以前保存したものを確実に読み出していることを保証するために、HMACが絶対に必要となる例だ。
  4. より強力なLuaサンドボックス化。このトピックについては、文献やベストプラクティスが豊富にあるはずだ。我々はすでにいくらかのサンドボックス化を実装しているが、セキュリティに携わっていた頃の感覚からすると、サンドボックス化は結局のところ常にいたちごっこであり、完璧にやり遂げることはできない。例えばCPUやメモリの濫用は、Redisの目的からすると追跡するには複雑すぎるかもしれない。しかし少なくとも、違反が起きてもメモリ破壊を伴わずに「graceful」に中断されることは保証すべきだ。
  5. Luaエンジンをアップグレードする時期かもしれない。新しいバージョンのLuaがセキュリティの観点でより進歩しているかどうかは定かではないが、Luaをアップグレードすると古いスクリプトが動かなくなる可能性があるという大きな問題がある。Redisコミュニティにとっては非常に大きな問題であり、とりわけRedisユーザーが通常書くようなスクリプトにとっては、より新しいLuaバージョンの利点はごくわずかだからだ。

修正された問題

修正された問題は、以下のコミットに列挙されている:

  • ce17f76b Security: fix redis-cli buffer overflow.
  • e89086e0 Security: fix Lua struct package offset handling.
  • 5ccb6f7a Security: more cmsgpack fixes by @soloestoy.
  • 1eb08bcd Security: update Lua struct package for security.
  • 52a00201 Security: fix Lua cmsgpack library stack overflow.

最初のコミットは今回の取り組みとは無関係で、コマンドラインで長いホスト引数を渡した場合にのみ悪用可能なredis-cliのバッファオーバーフローに関するものだ。他の問題は、cmsgpackとstructパッケージで見つかったものだ。

問題を再現する2つのスクリプトは以下のとおりだ:

https://gist.github.com/antirez/82445fcbea6d9b19f97014cc6cc79f8a

そして

https://gist.github.com/antirez/bca0ad7a9c60c72e9600c7f720e9d035

どちらもApple Information Securityチームによるものだが、1つ目はより確実にクラッシュを再現できるよう私が修正を加えた。

影響を受けるバージョン

基本的に、Luaスクリプティングを備えたすべてのRedisが影響を受ける。

修正は、以下のGitHubタグとして入手可能だ:

  • 3.2.12
  • 4.0.10
  • 5.0-rc2

安定版リリース(4.0.10)は、通常どおりhttp://download.redis.ioでも入手できる。

リリースのtarballのハッシュは、こちらで入手できる:

https://github.com/antirez/redis-hashes

なお、リリースされたバージョンには他のさまざまなバグ修正も含まれているため、新バージョンに切り替えることで他に何がアップグレードされるのか、リリースノートにも目を通しておくことをお勧めする。

将来的に、RedisのLuaスクリプティングサブシステムについて計画されているセキュリティ監査の報告とともに、またブログ記事でお会いできればと思う。

この記事は「muse-spark-1.2-contributor」を使用して翻訳されました。

コメント