ciliのブログ

差し障りない範囲で思い出の形作りのために既知の脆弱性とか、脆弱性じゃないと強く信じられているものについて語ります。脆弱性と分かってると問題ですからね!

なんとなくZip Slipについて語る

この記事はZip Slipが公開されてから3ヶ月近くも後に書かれたお話です。

そうだ、旅に出よう

ディレクトリトラバーサルパストラバーサル)は、格式張って語ると、ユーザ入力のバリデーションが不十分で親ディレクトリにトラバースするってことです。

トラバースって横断って訳されるけど、マウントな隣の山に移るみたいな意味もあるような無いような。とりあえず意識高く語るなら、このディレクトリはリスペクトできなかった、俺はビッグになるためにパレントディレクトリへのトラバースにチャレンジした。です。

名前付き脆弱性(Branded Vulnerabilities)

脆弱性だろうがなんだろうが、名前を付けるってのは悪くないアイデアです。脆弱性の原因が〇〇だった場合、それが初めてって劇レアケースならいいんだけど、多くは原因と影響を説明しても「で?なに?」って言われちゃいます。それって美味しいの?ぐらいの反応すら返ってきません。 

そこで、2014年の命名Heartbleedさんはうまい方法をとりました。①影響がイメージできるペットネームをとる②ドメインとる③アイコンをつくる。これで勝利です!

そう、商品ではなくストーリーごと売り込めです。こういった感じに、印象的なお名前の付いたバグ(Bug With An Impressive Name)はBWAINって呼ばれます。

Zip Slipのご紹介

Zip Slipはぶっちゃけ前述の名前付き脆弱性の一つでBWAINです。Zipがズレちゃった、てへぺろって感じですね。Zip Slipでは、Zipファイルを展開するときにディレクトリトラバーサルが起きるライブラリを一斉摘発しました。github検挙者リストと対策状況を公開しています。ライブラリの脆弱性じゃなくて仕様で、呼び出し側ツールの脆弱性なんじゃね?って意見もありますが、その議論はスルー。 

※Zipper(チャック)を開き閉めするとき噛んだりズレたり引っかかったりすると困りますが、そんな不具合をまず起こさないYKKの高性能なジッパーが普及して、昭和末期には一度は幸せになったんですが、最近は安いジッパーが服に使われていることもあって、それ原因で服が寿命に…。残念。 

そんな対策で大丈夫か?

Zip Slipの脆弱性説明文

脆弱性の説明はこうなってます。 

The vulnerability is exploited using a specially crafted archive that holds directory traversal filenames (e.g. ../../evil.sh). The Zip Slip vulnerability can affect numerous archive formats, including tar, jar, war, cpio, apk, rar and 7z. If you’d like the information on this page in a downloadable technical white paper, click the button below. 

説明文から受ける印象

ああ、".."に起因するディレクトリトラバーサルのBWAINね。そう思うと生き残れないかもしれません、あなたは頓死に一歩近づいています。脆弱性があるって指摘されたからには対策すると良いわけですが、そこで原因をどう思うかが重要。

ディレクトリトラバーサル

× ".."に起因する

です。".."だと思うと、なんと情弱だったのでしょう!と自己批判に迫られる事態に陥ります。

推奨している対策(.NET編)

snykは、親切にも脆弱性のあるコードの例とその対策を公開しています。以下のコードは.NETのサンプルです。どのコードも引用です。

脆弱性のあるコード:

ヤバいコードの例です。destDirectoryがなんだろうが、fileが相対パスだったら親ディレクトリにさかのぼることも可能だし、ましては絶対パスだったりすると、どこでもフリーアクセスです。豪快ですね。 

.NETの脆弱性サンプル

ちなみに、Path.Combineは第二引数(の正規化評価結果)が絶対パスだと、第一引数は無視して第二引数を使う仕様です。第三引数があるパターンもやっぱり後ろが優先されます。言われてみると「そういう仕様はあり得るな」でしょうが、単純に引数を連結してから正規化だと思ってると大変なことになります。

正しいコード:

対策も公開しています。「まったく簡単だ」です。標準ライブラリにパスを正規化するAPIがあるので、正規化した後のものが期待したパスから始まっているかチェックすればいいんです。簡単なお仕事です。

.NETの推奨対策サンプル

 さて、本題

対策済みリストが出ているからといって、信じていいんでしょうか?

友達は疑い深いのでリストをチェックしたそうです。前述のように.NETでは単純明快な対策がでているので、まずは.NETのライブラリを見てみると良さそうってこともあり、

DotNetZip.Semverdってのを見てみたんだそうです。ほら、対策済みになってますよね。 

Zip Slipのリスト

親切にgithubの修正差分へのリンクもついてます。便利ですねー。コメント文にきちんと対策内容も書いてます。ご立派。

けど、あれ? if (dir == "..") って呪文が!?

修正内容うーん。もちろん、ディレクトリデリミタを"\"を"/"で正規化するときに、Linuxでは有効だとしてもWindowsでは無視される余分なスペース省いているんですよね。知らないけど絶対そう。

.NETのWindows世界の常識というか定説というか

さて、.NETのWindows世界の高校の屋上で以下の会話があったとしましょう。

A「親ディレクトリって".."だけですよね? 」

B「えっ、なんだって」

別にラノベ突発性難聴主人公ではありません。そうです。.NETのWindows世界ではDotDotこと".."だけが親ディレクトリではありません。DotDotの後ろにいくつかスペースを加わってても、親切なWindowsは華麗にスルーしてくれます。そうです。DotDotSpacesはDotDotと等価なのです。

※下位フレームワークというか言語のランタイムによっては、WindowであってもDotDotとDotDotSpacesが異なる可能性もゼロではないです(SE的危険表現)。 

推奨している対策(Java編)

たまたま見たライブラリが.NETのWindowsの有名な地雷を踏んでいるように見えるので(呼び出し元でチェック済みな可能性も微レ存)、ついでに疑り深くJavaも見てみましょう。同じく、どのコードも引用元です。

脆弱性のあるコード:

これまた分かりやすい例ですね。destnationDirがなんだろうと、Enumerationキーに相対パス絶対パスが入っていると困ったことになります。

Javaの脆弱性サンプル

正しいコード:

これの対策の例示も明快ですね。標準ライブラリのAPIを信頼してgetCanonicalPathで正規化した後のものが期待したパスから始まっているか確かめます。

Javaの推奨対策サンプル

サンプルはサンプルであって(以下略)

getCanonicalPathはシンボリックリンクをフォローする仕様だけど、destinationfileもgetCanonicalPathしているから比較しているので、展開先ディレクトリがシンボリックリンクであってもうまく動作するサンプルコードになっていて素晴らしいです。

なお、存在しないパスを指定するとIOExceptionが出て、そのままメッセージを表示すると原因パスが懇切丁寧に表示される事があるので、必要に応じてキャッチしましょう。作業ディレクトリにあたるdestinationfileが漏れた場合は脆弱性とみなされるシチュエーションプレイがあります。この辺りは常識の範囲だし、問題ないこともあるのでサンプルでは省略してるのでしょう。 

おまけ

アタッシェケースという暗号ソフトはv3.3.0で、ATCファイル(独自の暗号化ファイルフォーマット)のディレクトリトラバーサル対策が強化されました。けど不十分だったということで2018年8月末にv3.4.0が公開され、対策をブラックリスト方式から、展開先パスを確認するホワイトリストっぽい方式に変更したそうです。これって、ZipSlipの対策でも紹介されている定番のアプローチです。一方、ブラックリスト方式ってのはDotNetZip.Semverdの例で紹介したような、これが入ってるとダメのアプローチです。完全に使いこなせば問題ないですが、ブラックリストアプローチはなかなか難しいものです。脆弱性追加修正の謝辞にでてくる、対処方法を指導したJPCERT/CCさんグッジョブですね。

魔法の呪文

いかがでしたか、いかがでしょうか、いかがでしたでしょうか、調べてみました。