こんにちは、クラウドセキュリティアーキテクトの大島悠司です。
先日のAWS re:Invent 2022で、「Amazon InspectorがLambda関数の診断をサポート」が発表されました。
私も普段からこの機能を使わせてもらっているのですが、意図的に脆弱なパッケージを入れても検知しないことがあり、気になったので調査してみました。
以前の検証記事
まずは結論から
長文のため、全部読むお時間が無い方のために結論を述べます。
Amazon Inspectorは、一般的なパッケージマネージャーのアーティファクトを使用して、AWS Lambda関数内のパッケージを診断します。
そのため、アーティファクトから外れたものは検知できません。
以下、パターン別の検証です。
(1)バージョンが古いレイヤーを追加する
以前の検証記事でもご紹介した、お手軽な検証方法です。
関数「test-vulnerabirity-lambda-1」を作成します。
ランタイムはpython3.8です。
以降の検証も全てランタイムpython3.8で実施しています。
AWSから提供されている、レイヤー「arn:aws:lambda:ap-northeast-1:249908578461:layer:AWSLambda-Python38-SciPy1x:109」を追加します。
1件のnumpyがInspectorにより検知されました。
関数「test-vulnerabirity-lambda-1」で、「CVE-2021-34141 - numpy」が検知されています。
ファイルパス「python/lib/python3.8/site-packages/numpy-1.21.3.dist-info/METADATA」を検知したようです。
(2)pipコマンドで取得
(1)でレイヤーを追加しましたが、検知されたものと同じバージョンのnumpyをpipコマンドで導入してみます。
関数「test-vulnerabirity-lambda-2」を作成します。
最初なので丁寧に解説します。
当該関数から、「アクション」→「関数のエクスポート」を押下します。
「デプロイパッケージのダウンロード」を押下します。
zipファイルを取得できるので、解凍します。
中身は、Lambda関数を作成したときにデフォルトで作成される「lambda_function.py」のみです。
lambda_function.py
import json def lambda_handler(event, context): # TODO implement return { 'statusCode': 200, 'body': json.dumps('Hello from Lambda!') }
pipコマンドが使える環境を用意し、(1)で検知されたものと同じバージョンのnumpyをダウンロードします。
4つのフォルダを取得できます。
$ pip3.7 install -t . numpy==1.21.3 Collecting numpy==1.21.3 Downloading numpy-1.21.3-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl (15.7 MB) |????????????????????????????????| 15.7 MB 321 kB/s Installing collected packages: numpy Successfully installed numpy-1.21.3 $ ls bin numpy numpy-1.21.3.dist-info numpy.libs
これを、先ほど関数「test-vulnerabirity-lambda-2」をエクスポートして解凍したフォルダに入れて、再度zip化します。
このzipファイルをLambda関数にアップロードします。
関数「test-vulnerabirity-lambda-2」から、「アップロード元」→「.zipファイル」を押下します。
「アップロード」から先ほど作成したzipファイルを選択し、保存します。
アップロードが完了しました。
numpyの検知数が2に増えています。
関数「test-vulnerabirity-lambda-2」で、「CVE-2021-34141 - numpy」が検知されています。
ファイルパス「numpy-1.21.3.dist-info/METADATA」を検知したようです。
このファイルはいったい何なのでしょうか。
エクスプローラーから探してみると、同じファイルがありました。
このファイルにはバージョンやライセンスなどの情報が書かれており、どうやらInspectorはこのファイルを見ていそうです。
METADATA
Metadata-Version: 2.1 Name: numpy Version: 1.21.3 Summary: NumPy is the fundamental package for array computing with Python. Home-page: https://www.numpy.org Author: Travis E. Oliphant et al. Maintainer: NumPy Developers Maintainer-email: numpy-discussion@python.org License: BSD Download-URL: https://pypi.python.org/pypi/numpy Project-URL: Bug Tracker, https://github.com/numpy/numpy/issues Project-URL: Documentation, https://numpy.org/doc/1.21 Project-URL: Source Code, https://github.com/numpy/numpy Platform: Windows Platform: Linux Platform: Solaris Platform: Mac OS-X Platform: Unix Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Science/Research Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: BSD License Classifier: Programming Language :: C Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.7 Classifier: Programming Language :: Python :: 3.8 Classifier: Programming Language :: Python :: 3.9 Classifier: Programming Language :: Python :: 3.10 Classifier: Programming Language :: Python :: 3 :: Only Classifier: Programming Language :: Python :: Implementation :: CPython Classifier: Topic :: Software Development Classifier: Topic :: Scientific/Engineering Classifier: Typing :: Typed Classifier: Operating System :: Microsoft :: Windows Classifier: Operating System :: POSIX Classifier: Operating System :: Unix Classifier: Operating System :: MacOS Requires-Python: >=3.7,<3.11 It provides: - a powerful N-dimensional array object - sophisticated (broadcasting) functions - tools for integrating C/C++ and Fortran code - useful linear algebra, Fourier transform, and random number capabilities - and much more Besides its obvious scientific uses, NumPy can also be used as an efficient multi-dimensional container of generic data. Arbitrary data-types can be defined. This allows NumPy to seamlessly and speedily integrate with a wide variety of databases. All NumPy wheels distributed on PyPI are BSD licensed.
(3)WindowsでWebサイトから取得
Windowsで開発する人は、Webサイトからパッケージをダウンロードしてくることもあると思います。
Webサイトから同パージョンのnumpyをダウンロードして、Lambdaに導入してみます。
関数「test-vulnerabirity-lambda-3」を作成します。
以下のWebサイトから、「numpy-1.21.3.zip」をダウンロードします。
zipを解凍すると以下のようにたくさんのファイルがあります。
(2)で実施したのと同様に、関数「test-vulnerabirity-lambda-3」をエクスポートして、解凍します。
そこに先ほどダウンロードしたファイルを一式入れて、再度zip化します。
このzipファイルを、関数「test-vulnerabirity-lambda-3」にアップロードします。
InspectorはLambda関数を認識していますが、検知数は変化しません。
pipコマンドで取得したものとフォルダ構成が異なるため、METADATAを検知できなかったと考えられます。
「(2)pipコマンドで取得」を深堀りする
(2)のpipコマンドで取得した場合はInspectorの検知に成功します。
そのため、pipコマンドで取得したパッケージを前提として、特定のファイルを見ているのではないかと予想できます。
そこで、他のパターンを試してみました。
(2-2)pipコマンドで取得&METADATAファイル名を変更
検知されたファイル名を変更してみます。
関数「test-vulnerabirity-lambda-2-2」を作成します。
METADATAをMETADATAtestと変更し、再度zip化してアップロードします。
検知数は変化なしです。
ファイル名が変更されると検知できなくなるようです。
(2-3)pipコマンドで取得&METADATAファイルパスを変更
検知されたファイルパスを変更してみます。
関数「test-vulnerabirity-lambda-2-3」を作成します。
METADATAを一つ上の階層に移動し、再度zip化してアップロードします。
検知数は変化なしです。
ファイルパスが変更されると検知できなくなるようです。
(2-4)pipコマンドで取得&METADATAファイルを削除
検知されたファイルパスを削除してみます。
関数「test-vulnerabirity-lambda-2-4」を作成します。
METADATA削除し、再度zip化してアップロードします。
検知数は変化なしです。
ファイルが削除されると検知できなくなるようです。
(2-5)pipコマンドで取得&METADATAファイルを書き換える
検知されたファイルのバージョンを書き換えてみます。
関数「test-vulnerabirity-lambda-2-5」を作成します。
METADATAのVersionを「1.21.3」から「1.21.0」に書き換えます。
書き換え前
Metadata-Version: 2.1 Name: numpy Version: 1.21.3 Summary: NumPy is the fundamental package for array computing with Python. Home-page: https://www.numpy.org Author: Travis E. Oliphant et al. (省略)
書き換え後
Metadata-Version: 2.1 Name: numpy Version: 1.21.0 Summary: NumPy is the fundamental package for array computing with Python. Home-page: https://www.numpy.org Author: Travis E. Oliphant et al. (省略)
再度zip化してアップロードします。
検知数が2から3に増えています。
関数「test-vulnerabirity-lambda-2-5」で、「CVE-2021-34141 - numpy」が検知されています。
ファイルパス「numpy-1.21.3.dist-info/METADATA」を検知したようです。
ここで、検知されたバージョンを見てみると、「 (2)pipコマンドで取得」では「numpy 1.21.3」として検知されていたのに対し、今回の「(2-5)pipコマンドで取得&METADATAファイルを書き換える」では「numpy 1.21.0」として検知されています。
「 (2)pipコマンドで取得」では「numpy 1.21.3」
「(2-5)pipコマンドで取得&METADATAファイルを書き換える」では「numpy 1.21.0」
numpyの場合は、METADATA内のバージョンを参照して、脆弱化否かを判断されていると考えられます。
まとめ
以上の検証をまとめると、以下の表のようになります。
Amazon Inspectorでnumpyを様々なパターンで診断させた場合
検証番号 | Lambda名 | 条件 | 結果 |
---|---|---|---|
(1) | test-vulnerability-lambda-1 | バージョンが古いレイヤーを追加する | METADATAで検知 |
(2) | test-vulnerability-lambda-2 | pipコマンドで取得 | METADATAで検知 |
(2-2) | test-vulnerability-lambda-2-2 | pipコマンドで取得&METADATAファイル名を変更 | 検知無し |
(2-3) | test-vulnerability-lambda-2-3 | pipコマンドで取得&METADATAファイルパスを変更 | 検知無し |
(2-4) | test-vulnerability-lambda-2-4 | pipコマンドで取得&METADATAファイルを削除 | 検知無し |
(2-5) | test-vulnerability-lambda-2-5 | pipコマンドで取得&METADATAファイルを書き換える | METADATAで書き換えたバージョンとして検知 |
(3) | test-vulnerability-lambda-2-6 | WindowsでWebサイトから取得 | 検知無し |
仮説として、Amazon Inspectorはパッケージごとに特定のパスの特定のファイルを見ることで、脆弱か否かを診断しているものと考えられます。
おそらく、AWS側でそういったリストを所有しており、マッチングをかけているロジックのため、リストから外れるものは検知されないと考えられます。
また、コードはLambda関数作成時のデフォルトのものを使っており、importの定義はしていないので、importして実際に呼び出されている否かは関係ないようです。
ちなみに、この結果をもとにAWSサポートに問い合わせたところ、以下のような回答を頂きました。
- Amazon Inspector は、一般的なパッケージマネージャーのアーティファクトを使用して、サードパーティのソフトウェアパッケージを識別する
- パッケージマネージャーを使用せずに手動でインストールされたパッケージを検出できない
- 各パッケージについて具体的にどのような情報・ファイルを確認しているかについては、内部情報のため公開できない
やはり仮説の通りでした。
結論として、Amazon Inspectorは、一般的なパッケージマネージャーのアーティファクトを使用して、AWS Lambda関数内のパッケージを診断しています。
そのため、特殊な方法でインストールしたパッケージは検知することができません。
このような特性を理解したうえでAmazon Inspectorを活用していければ良いと思います。