yamory Blog

TensorFlowだけじゃない!安全でないデシリアライゼーション in Python

Googleによって開発されていたTensorFlowに任意のコード実行に繋がる脆弱性(CVE-2021-37678)が報告されました。

この脆弱性は安全でないデシリアライゼーションと呼ばれる脆弱性で、多くのプログミング言語に存在するバイト列等の表現で直列化されたデータを元のオブジェクトに変換する処理で発生する脆弱性です。

報告されたものはTensorFlowの脆弱性ですが、TensorFlowと同様の実装がされているソフトウェアにも影響があり、GitHubで検索をしただけでも多くプロジェクトで同様の実装が見られます。

今回は、TensorFlowの脆弱性を例に、PythonのPyYAMLにおける安全でないデシリアライゼーションについて解説します。

また、この記事を書くための調査の中で、新たな脆弱性を発見し、CVE-2021-43811(Amazon Translateでも利用されている機械翻訳フレームワーク Sockeyeの脆弱性)とCVE-2021-41078(Pythonのマイクロサービスフレームワーク namekoの脆弱性)を取得しました。

「安全でないデシリアライゼーション」概要

安全でないデシリアライゼーション(Insecure Deserialization)とは、多くのプログミング言語に存在するバイト列等の表現で直列化されたデータを元のオブジェクトに変換する処理で発生する脆弱性です。

攻撃者がこの脆弱性を悪用することにより、DoS や状態の改ざん、最悪の場合は任意のコード実行を引き起こす可能性があります。

2021年のOWASP Top 10でも8番目に含まれているものになっています。

「シリアライゼーション」は、アプリケーション内のメモリ上のオブジェクトなどをバイト列やJSON、YAML等の形式に変換する処理を示します。

「デシリアライゼーション」は、シリアライゼーションによってオブジェクトをバイト列などの表現に変換したデータをアプリケーションで取り扱うことができるようにオブジェクトに再構築する処理を示します。

以下の記事で、Javaにおける安全でないデシリアライゼーションを解説していますのでこちらの解説も参考になります。

実践!安全ではないデシリアライゼーションの攻撃手法

今回は、最近公開されたTensorFlowの任意のコード実行にも繋がる恐れのある脆弱性(CVE-2021-37678)を例にPythonのPyYAMLにおける安全でないデシリアライゼーションについて解説していきたいと思います。

TensorFlowの安全でないデシリアライゼーションの脆弱性

2021年8月12日、TensorFlowに任意のコード実行に繋がる脆弱性(CVE-2021-37678)が公開されました。

この脆弱性は、YAMLフォーマットからKerasモデルへとデシリアライズする時に任意のコードが実行されるというものです。

TensorFlowのセキュリティアドバイザリには以下の脆弱性を再現することが可能なコード(PoC)も提示されています。

from tensorflow.keras import models

payload = '''
!!python/object/new:type
args: ['z', !!python/tuple [], {'extend': !!python/name:exec }]
listitems: "__import__('os').system('cat /etc/passwd')"
'''
  
models.model_from_yaml(payload)

このコードを実行すると、os.system('cat /etc/passwd')が実行され、/etc/passwdファイルの中身が表示されます。

payloadに書かれているものがmodels.model_from_yamlで読み込まれると、内部ではPyYAMLのyaml.unsafe_loadが呼び出されています。

unsafe_loadは名称からわかるように信頼できないデータを読み込むことで簡単に悪用されてしまうものであり、公式のWikiでも注意書きされています。

このように、今回のCVE-2021-37678の脆弱性はTensorFlowの脆弱性ではありますが、PyYAMLのyaml.unsafe_loadを利用しているソフトウェアであれば同様の影響を受ける恐れのあるものだとわかります。

また、このTensorFlowの脆弱性に対する修正方法として、修正が困難だったためYAML形式の対応を辞める、という対応が行われました。

GitHubで yaml.unsafe_load を検索すると・・・

PyYAMLのyaml.unsafe_loadを利用していると先ほど説明したTensorFlowの脆弱性と同様の影響が受ける恐れがあることがわかりました。

unsafe_loadという名称からわかるように、明らかに安全ではないものはあまり使われることはなく、TensorFlowのようなケースは滅多にないように思われるかもしれません。

しかし、GitHubで yaml.unsafe_load を検索してみますと、Pythonの言語だけで絞ると執筆時点では644コード見つけることができました。

また、unsafe_loadを直接利用するのではなく、loadを利用する時にLoaderとしてUnsafeLoaderを呼ぶケース(Loader=yaml.UnsafeLoader)も考えられます。

その場合も検索してみますと、484コード見つけることができました。

傾向として、TensorFlowのような機械学習に関連するOSSでYAML形式のconfigファイルをUnsafeLoaderで読み込むものが多いように思われます。

また、コード内でUnsafeLoaderを使うことのリスクを表示しているものや、以前までloadを使っていたがアップデートで利用できなくなってしまったため、UnsafeLoaderに切り替えた、というものもあります。

信頼できないconfigファイルを読み込む、というユースケースはほぼないとは思われますが、OSSの開発側はリスクを認識していても利用者側が悪意のあるファイルを読み込むとこのような脆弱性の影響を受ける、ということを認識していない場合もありますので注意が必要です。

調査の中で新たな脆弱性を発見

上記の方法でのGitHubの検索結果の中には、世の中で広く使われているようなものはあまり多くないように思われましたが、2つほど気になるプロジェクトがあったため調査を行いました。

調査の中で、Amazon Web Services Labsが管理し、Amazon Translateなどの機械翻訳に利用されているSockeyeとPythonのマイクロサービスフレームワークのnamekoにも影響があることがわかりました。

TensorFlowと同様にYAML形式のconfigファイルを読み込む時に任意のコード実行に繋がる脆弱性を発見し、メンテナーに脆弱性を再現するためのPoCと共に報告を行いました。

その結果、修正が行われ、CVE-2021-43811(awslabs/sockeye)とCVE-2021-41078(nameko/nameko)として公開されました。

yamoryとしては初のCVEの取得となりました。

脆弱性の発見から調査、報告、CVEの取得までの詳細に関しては別途記事を書きたいと思います。

load()を使う場合も注意が必要

unsafe_loadは安全ではないことがわかりましたが、デフォルトのloadを使えば大丈夫なのでしょうか?

公式のWikiを確認してみると、「PyYAML yaml.load(input) Deprecation」と書かれています。

これはバージョン5.1以前では、以下のようにload()を利用していても任意のコード実行に繋がってしまう脆弱性(CVE-2017-18342)があったためです。

python -c 'import yaml; yaml.load("!!python/object/new:os.system [echo EXPLOIT!]")'

その後も脆弱性(CVE-2020-1747)が発見され、修正されましたが、再度パイパスできる脆弱性(CVE-2020-14343)が発見されたため、現状のデフォルトのload(FullLoader)は今後なくなる可能性があり、非推奨となっています。

そのため、バージョン5.4以前のPyYAMLを利用している場合には、デフォルトのloadを使っている場合にも脆弱性を受ける恐れがあります。

間接的にPyYAMLを使う場合にも影響

PyYAMLでunsafe_loadやデフォルトのload()を使う場合には注意が必要だと解説しました。

また、TensorFlowでも内部でPyYAMLのunsafe_loadが使われていたために脆弱性が報告された例も紹介しました。

自分たちは(直接)PyYAMLを利用していないから大丈夫と思わずに、間接的にPyYAMLを使っていないかも把握することが大切です。

GitHubでyaml.loadを検索してみますと、174,250コード見つけることができます。

すべてのコードが脆弱であるということではありませんが、2021年1月20日に公開された5.4以前のバージョンを使っている場合にはリスクがあります。

OSSの中には、開発者がPyYAMLの脆弱性を把握できておらず、アップデートできていない場合やそもそもメンテナンスがされていないものも存在します。

自分たちが間接的に利用しているライブラリとそのバージョンの把握まで行うことは大変ではありますが、このような推移的(間接)依存のライブラリによる脆弱性の影響も無視することはできません。

おわりに

今回は、TensorFlowの脆弱性(CVE-2021-37678)の解説から、TensorFlow以外でもPyYAMLを利用している場合には影響があることを説明しました。

また、調査の中で新たにTensorFlowと同様の任意のコード実行に繋がる脆弱性を発見・報告し、CVE-2021-43811(awslabs/sockeye)とCVE-2021-41078(nameko/nameko)の脆弱性として公開されました。

(報告した脆弱性が修正され、公開されるまでこの記事の公開を止めていたため、4ヶ月ほど前の少し古い話題のtensorflowの脆弱性の記事となってしまいました。)

PyYAMLを利用する場合には、最新のバージョンを採用し、safe_loadを利用することをおすすめします。

また、直接PyYAMLを利用していない場合でも、利用しているソフトウェアにPyYAMLが組み込まれているときには影響を受けることがあります。

自分たちが利用しているソフトウェアに関しては、直接利用しているものだけでなく、間接的に利用しているもの含めて把握することが大切です。

yamoryでは、利用しているアプリケーションライブラリのソフトウェアの一覧を取得し、その中に含まれる脆弱性の把握・管理を行うことができるサービスです。

アプリケーションライブラリの脆弱性だけでなく、GPL、MITライセンスなどのライセンス情報の把握も行うことができます。

また、これまではアプリケーションライブラリのみに対応していましたが、サーバのミドルウェア・OSのソフトウェア・脆弱性管理も行うことができるようになりました。

無料でトライアルもできますので、ぜひ一度お試しください。

エンジニア・セキュリティエンジニアを募集しております。こちらより応募お待ちしております。

参考文献

オープンソース脆弱性管理ツール yamory

  • 利用中のOSSを抽出し
    脆弱性を自動スキャン
  • 脆弱性への対応優先度を自動で分類
  • 組織規模に合わせたプランを選択可能
お問い合わせ

お気軽にお問い合わせください。
担当者よりご連絡いたします。