Zip Slipが大きく報道
日経にてZip Slip脆弱性が報道されています。
英セキュリティー企業のスニックセキュリティーは、複数のファイルを1つにまとめる「書庫ファイル」形式に深刻な脆弱性を発見したと発表した。この形式で作られた書類を展開すると、最悪の場合、システムを書き換えられたり、ウイルスプログラムを仕込まれたりするという。セキュリティー関係者は利用者に対し警戒を呼びかけている。
この記事だけでは不明だとおもいますので、スニックセキュリティーが公開したZip Snip脆弱性に対するホワイトペーパーの日本語訳を掲載します。
スニックセキュリティーによるホワイトペーパーの日本語訳
概要
Zip Slipは広く普及している重要なアーカイブ抽出の脆弱性です。攻撃者はシステムに任意のファイルを書き込むことができます。具体的な攻撃名はRemote Command Execution(リモートコマンド実行)です。Snyk Securityチームによって発見され一般2018年6月5日に責任ある形で公開されました。HP、Amazon、Apache、Pivotalなどのプロジェクトをはじめたくさんのプロジェクトに影響があります。
この脆弱性は、JavaScript、Ruby、.NET、Goなどの複数のエコシステムで検出されていますが、アーカイブ(zip)ファイルに対する高レベルの処理を提供するセントラルライブラリが存在しないJavaでは特に流行しています。 そのようなライブラリの欠如のため、脆弱なコードスニペットが手作りで作成され、StackOverflowなどの開発者コミュニティで共有されてしまいます。
この脆弱性は、ディレクトリトラバーサルファイル名(例:../../evil.sh)を保持する特別に細工されたアーカイブを使用して悪用されます。 Zip Slipの脆弱性は、tar、jar、war、cpio、apk、rar、7zなどの多数のアーカイブ形式に影響する可能性があります。
Zip Slipは、アーカイブからファイルを抽出することによって悪用できるディレクトリトラバーサルの一形式です。
ディレクトリトラバーサル脆弱性の前提は、攻撃者が、ターゲットフォルダの外にあるファイルシステムの部分にアクセスできることです。 その後、攻撃者は実行可能ファイルを上書きし、リモートから呼び出すか、システムまたはユーザーがそれらを呼び出すのを待って、被害者のマシン上でRemote Command Execution(リモートコマンド実行)を実行します。 この脆弱性は、構成ファイルやその他の重要なリソースを上書きすることによっても攻撃できる可能性があり、クライアント(ユーザー)のマシンとサーバーの両方で悪用される可能性があります。
悪用可能なアプリケーションフロー
この脆弱性を悪用するために必要な2つのパートは、妥当性チェックを実行しない悪質なアーカイブと抽出コードです。 これらを順番に見てみましょう。
まず、zipファイルの内容には、抽出されたときにターゲットディレクトリから逸脱する1つ以上のファイルが必要です。 下の例では、zipファイルの内容を見ることができます。 これには2つのファイルがあり、目的のディレクトリに展開されるgood.shファイルと、rootにヒットしてtmpディレクトリにファイルを追加するディレクトリツリーをたどるevil.shファイルの2つがあります。 ルートディレクトリからcd ..を実行すると、ルートディレクトリに自分自身が見つかるため、悪意のあるパスには多くのレベルの../が含まれている可能性があります。
5 Tue Jun 5 11:04:29 BST 2018 good.sh
20 Tue Jun 5 11:04:42 BST 2018 ../../../../../../../../tmp/evil.sh
(orangeitems注:"cd /"と実行したあと、"cd ../"と実行しても"/"(ルートディレクトリ)のため、攻撃者が" ../../../../../../../../"と指定することで"/"までさかのぼれるだろうという意味です。)
このzipファイルは手作業で作成する必要があります。 アーカイブ作成ツールでは、一般に、ユーザーがこれらのパスを持つファイルを追加することは許可されていません。 ただし、ある適当なツールを使用すると、これらのパスを使用してファイルを簡単に作成できます。
2番目に悪用できるのは、独自のコードまたはライブラリを使用してアーカイブを抽出する機能を持たせることです。 この脆弱性は、抽出コードがアーカイブ内のファイルパスに対する検証を省略した場合に発生します。 以下に、脆弱なコードスニペットの例(Javaで表示されている例)を示します。
1: Enumeration<ZipEntry> entries = zip.getEntries();
2: while (entries.hasMoreElements()) {
3: ZipEntry e = entries.nextElement();
4: File f = new File(destinationDir, e.getName());
5: InputStream input = zip.getInputStream(e);
6: IOUtils.copy(input, write(f));
7: }
4行目にe.getName()が検証されずにターゲットディレクトリであるdirと連結されています。 この時点で、私たちのzipアーカイブがevil.shに到達すると、zipエントリのフルパス(../を含む)がすべてターゲットディレクトリに追加され、evil.shがターゲットディレクトリの外に書き出されます。
Zip Slipの動作を確認するには、既知の脆弱性の多くを示すサンプルアプリケーションであるjava-goofアプリケーションを悪用している映像を閲覧してください。
あなたは脆弱ですか?
Zip Slipの脆弱性を含むライブラリを使用している場合、またはプロジェクトに直接脆弱なコードが含まれている場合、脆弱性があります。このコードは、ディレクトリトラバーサルの検証を行わずにアーカイブからファイルを抽出します。 Snykは、GitHubリポジトリを維持しており、Zip Slipの脆弱性が確認され、修正日時やバージョンを含む責任を持って開示されたすべてのプロジェクトをリストしています。 リポジトリは、最新の状態を保持するために、より広範なコミュニティからのコントリビューションに開放されています。
あなたはどんな行動を取るべきですか?
プロジェクトのコードの依存関係にZip Slipの脆弱性が含まれているかどうかを確認するために実行できる手順は次のとおりです。
1. 脆弱なコードをプロジェクト内で検索します。
各エコシステムのセクションでは、特定の脆弱性を強調するコードスニペットが表示されます。 付属の検証コードを脆弱なスニペットに追加して、ディレクトリトラバーサルをテストすることができます。 コード内で類似の抽出パターンを検索し、脆弱であると判明したアーカイブ処理ライブラリがFixされたバージョンを使用していることを確認する必要があります。
Java
前述のように、Javaエコシステムは、アーカイブファイルのハイレベルな処理を含むセントラルライブラリを提供していません。 頻繁に使用されている一般的なOracleおよびApacheのcommons-compress APIは、アーカイブのサポートを提供しますが、完全な抽出機能は公開していません。 これは、ユーザーがアーカイブ処理コードを手作業で作成するケースが増えていることに影響しています。 私たちは、Javaエコシステムには他のエコシステムよりも多くのアーカイブライブラリがあり、その多くは脆弱であることが判明しました。
脆弱性のあるコード:
1: Enumeration<ZipEntry> entries = zip.getEntries();
2: while (entries.hasMoreElements()) {
3: ZipEntry e = entries.nextElement();
4: File f = new File(destinationDir, e.getName());
5: InputStream input = zip.getInputStream(e);
6: IOUtils.copy(input, write(f));
7: }
バリデーションコードの例:
1: String canonicalDestinationDirPath = destinationDir.getCanonicalPath();
2: File destinationfile = new File(destinationDir, e.getName());
3: String canonicalDestinationFile = destinationfile.getCanonicalPath();
4: if (!canonicalDestinationFile.startsWith(canonicalDestinationDirPath)) {
5: throw new ArchiverException("Entry is outside of the target dir: " + e.getName());
6: }
Groovy
Javaと同様に、Groovyはさまざまなプロジェクトコードベースで脆弱なスニペットを持ち、脆弱なJavaアーカイブ処理ライブラリをすべて使用しています。
脆弱性のあるコードの例:
1: final zipInput = new ZipInputStream(new FileInputStream(self))
2: zipInput.withStream {
3: def entry
4: while(entry = zipInput.nextEntry) {
5: final file = new File(dest, entry.name)
6: file.parentFile?.mkdirs()
7: def output = new FileOutputStream(file)
8: output.withStream {
9: output << zipInput
10: }
11: unzippedFiles << file
12: }
13: }
バリデーションコードの例:
1: final canonicalDestinationDirPath = destinationDir.getCanonicalPath()
2: final destinationfile = new File(destinationDir, e.name)
3: final canonicalDestinationFile = destinationfile.getCanonicalPath()
4: if (!canonicalDestinationFile.startsWith(canonicalDestinationDirPath)) {
5: throw new ArchiverException("Entry is outside of the target dir: ${e.name}")
6: }
JavaScript
JavaScriptは、アーカイブから抽出する機能を提供するより多くのセントラルライブラリと、公開前に見つかった脆弱なライブラリを持つことから、メリットを得ています。 joinコマンドは、2つのパスパラメータを連結し、解決後に可能な最短パスを返すことに注意してください。
脆弱性のあるコードの例:
1: self.on('entry', function(entry) {
2: entry.pipe(Writer({
3: path: path.join(opts.path,entry.path)
4: }))
バリデーションコードの例:
1: var filePath = path.join(targetFolder, entry.path);
2: if (filePath.indexOf(targetFolder) != 0) {
3: return;
4: }
.Net
.Netエコシステムには抽出機能を実行するセントラルライブラリもあります。 実際、Zip Slipの脆弱性をチェックするコア.Netライブラリのコードはとても使いやすく、他のライブラリやエコシステムのリファレンスソリューションとしてその実装を使用しました。
脆弱性のあるコードの例:
1: public static void WriteToDirectory(IArchiveEntry entry,
2: string destDirectory,
3: ExtractionOptions options){
4: string file = Path.GetFileName(entry.Key);
5: string destFileName = Path.Combine(destDirectory, file);
6: entry.WriteToFile(destFileName, options);
7: }
バリデーションコードの例:
1: destFileName = Path.GetFullPath(Path.Combine(destDirecory, entry.Key));
2: string fullDestDirPath = Path.GetFullPath(destDirectory);
3: if (!destFileName.StartsWith(fullDestDirPath)) {
4: throw new ExtractionException("Entry is outside of the target dir: " + destFileName);
5: }
Go
Goエコシステムには、脆弱なライブラリが1つしかありませんが、私たちが問題を明らかにした2日以内に修正されました。 Joinコマンドは、2つのパスパラメータを連結し、解決後に可能な最短パスを返すことに注意してください。
脆弱性のあるコードの例:
1: func (rarFormat) Read(input io.Reader, dest string) {
2: rr := rardecode.NewReader(input, "")
3: for {
4: header := rr.Next()
5: writeNewFile(filepath.Join(dest, header.Name), rr, header.Mode())
6: }
7: }
バリデーションコードの例:
1: func sanitizeExtractPath(filePath string, destination string) error {
2: destpath := filepath.Join(destination, filePath)
3: if !strings.HasPrefix(destpath, destination) {
4: return fmt.Errorf("%s: illegal file path", filePath)
5: }
6: return nil
7: }
Ruby & Python
また、RubyとPythonのエコシステムを検証し、脆弱なコードスニペットやライブラリを見つけることができませんでした。 実はPythonのzipファイルは2014年に修正されるまで脆弱でした。Rubyには高水準の抽出APIの欠如のため。ここやここ、ここの以前のバージョンで修正されたいくつかの既存の脆弱性がありました。
2.アプリケーション構築パイプラインにZip Slipセキュリティテストを追加する
あなたが脆弱なライブラリを使用しているかどうかを判断するために直接的および推移的な依存関係(何百人もいる可能性が高い)を検索したくない場合は、Snykのような依存性脆弱性検索ツールを選択できます。 開発ライフサイクル段階(開発、CI、展開、生産など)にセキュリティテストを追加することをお勧めします。 自分のプロジェクト(上記のすべてのエコシステムがサポートされています)をテストして、Zip Slipに脆弱であるかどうかを判断できます。
その他の脆弱なプロジェクト
脆弱なプロジェクトには、上記のライブラリを使用するか、脆弱なコードを直接含むさまざまなエコシステムのプロジェクトが含まれます。同様の脆弱性のあるコードサンプルや脆弱なライブラリにアクセスした何千ものプロジェクトのうち、最も重要なものは次のとおりです。
Oracle, Amazon, Spring/Pivotal, Linkedin, Twitter, Alibaba, Jenkinsci, Eclipse, OWASP, SonarCube, OpenTable, Arduino, ElasticSearch, Selenium, Gradle and JetBrains.
Thank you!
Snykのセキュリティチームは、多くのエコシステムのプロジェクトにおける脆弱性の発見、修正、修正を支援したベンダー、プロジェクトオーナー、コミュニティメンバーに感謝したい。
Zip Slip 開示の時系列
この開示のタイムラインは、2018年4月15日の最初のプライベート開示からの私たちの行動を詳述しています。
注※この段は省略します。詳しくは原文をご確認ください。
関連情報
本件の関連情報リンクです。
各社によるニュース
Zipなどのアーカイブ展開処理を突いた脆弱性「Zip Slip」 ~任意のコードを実行可能 - PC Watch
アーカイブファイル関連の脆弱性「Zip Slip」、大手プロジェクト多数に影響 - ITmedia エンタープライズ
Snyk Security、アーカイブファイルの処理に関する重大な脆弱性「Zip Slip」を発見、多数のプロジェクトに影響 | Think IT(シンクイット)
JPCERT/CCによるお知らせ
アーカイブファイルの展開処理における脆弱性「Zip Slip」について|JPCERTCC
英スニックセキュリティー社によるZip Slipのレポート(英語)
英スニックセキュリティー社によるZip Slipのホワイトペーパー(英語)
※PDFファイル
体系的に学ぶ 安全なWebアプリケーションの作り方 第2版 脆弱性が生まれる原理と対策の実践 単行本 – 2018/6/21