webメールサービスのリンク確認画面にあった反射型XSS
数年前、EUC-JPなwebメールサービスでたまたま反射型のXSSを見つけたのでIPAに届けていました。
当時eメールからのリンクをクリックしたときの動作を調べていたのですが、その際に変な挙動に突き当たりました。適当なリンクを自分自身に送信した後、画面内に表示されたリンクをクリックすると外部リンクであることの確認画面に飛ばされます。そこまではいいのですが、なぜかリンクが途中で切れていました。
webメールの安全性は結構重要でして、webサービスではパスワードリセットする際にはメールで確認しているケースが多いため*1、ここに脆弱性があると他のサービスにも影響を受ける可能性が高まります。というわけで、よくよく調べてみると、確認画面のクエリパラメータに載っているurl=
の文字の一部が画面側には出ていません。
このあたり記憶が曖昧なのですが xss に繋がりそうな文字種があると画面に表示する際には消されていたり、WAFでブロックされたような表示になっていた覚えがあります。詳細については念のため省略しますが、パラメータに細工をすることで対策された部分を乗り越えることができました。
chromeでは反射型XSSはフィルタリングによってブロックされるので単純には実行できませんでしたが、デフォルトでは組み込まれていないFireFoxでは実行できました。
ユーザが悪意のあるリンクをクリックするのを防ぐためのセキュリティ対策と思われますが、実装に脆弱性があるケースでした。*2
IPAを通じて届け出たのですが思いの外対応が早く、最初のメール送信から受理と通知までに3日ほど、修正までに一週間ほどでした。*3どこまで影響があるか調べるよりも先に届け出るのを優先したため、実際にどこまで影響があるのかを確認していなかったのですが、おそらくメールの読み取りなどはできたのではないでしょうか。
この手の反射型のXSSに対する対策としては、基本的にフレームワークに使われているエスケープ関数を使えば大抵の場合問題ありません。使っていない場合はフレームワークを入れるかライブラリを探すか、徳丸本を読みましょう。*4
PHPのescapeshellcmdの問題について、sendmail以外に影響を受けそうなコマンド
正月休み中にtarのマニュアルを読んでいて--to-command=
というオプションが有ることを知りまして、これはescapeshellcmdの影響を受けそうだと思って他にも調べたものです。
この記事を読む前に。escapeshellcmdの問題
blog.tokumaru.org blog.tokumaru.org
tar
GNU tarのマニュアルを読んでいくとオプションの種類が多すぎて把握しきれなかったのですが、オプションからcommandが実行できるものは複数ありました。
https://www.gnu.org/software/tar/manual/html_node/Device.html#SEC156
- `-F command'
- `--info-script=command'
- `--new-volume-script=command'
$ tar -xf archive.tar $ tar -xf archive.tar --to-command='ls -la' total 24 drwxrwxr-x 2 vagrant vagrant 4096 2017-01-15 08:48 . drwx------. 12 vagrant vagrant 4096 2017-01-15 08:47 .. -rw-rw-r-- 1 vagrant vagrant 10240 2017-01-15 08:47 archive.tar -rw-rw-r-- 1 vagrant vagrant 646 2016-10-30 09:07 opt.png
mac上のtar(bsdtar)では、--to-command=
はサポートされていないようです。
$ tar --help tar(bsdtar): manipulate archive files ... $ tar -xf archive.tar --to-command='ls' tar: Option --to-command=ls is not supported Usage: List: tar -tf <archive-filename> Extract: tar -xf <archive-filename> Create: tar -cf <archive-filename> [filenames...] Help: tar --help
wget
GNU Wget 1.18 Manual: Basic Startup Options に ‘--execute command’
とありますがこれはshellを叩くものではなく、.wgetrc
で設定するcommandの方です。
GNU Wget 1.18 Manual: Wgetrc Commands
パラメータの追加により、shellを叩く方法はなさそうでしたが、--execute output_document=../execute.png
を追加すると保存場所を書き換えることが出来ました。
例えば出力ファイル名とリクエスト先のドメインはアプリケーション側で制限されているが、クエリパラメータなどに当たる部分に自由に入力できる場合を想定します。*1
$ ls test.php $ cat test.php <?php $command = 'wget -O output.html http://example.com --execute output_document=overwrite.html'; $escaped_command = escapeshellcmd($command); var_dump($escaped_command); system($escaped_command); ?> $ php -f test.php string(79) "wget -O output.html http://example.com --execute output_document=overwrite.html" --2017-01-15 17:11:35-- http://example.com/ Resolving example.com... 2606:2800:220:1:248:1893:25c8:1946, 93.184.216.34 Connecting to example.com|2606:2800:220:1:248:1893:25c8:1946|:80... failed: Connection refused. Connecting to example.com|93.184.216.34|:80... connected. HTTP request sent, awaiting response... 200 OK Length: 1270 (1.2K) [text/html] Saving to: 'overwrite.html' overwrite.html 100%[==============================>] 1.24K --.-KB/s in 0s 2017-01-15 17:11:36 (80.7 MB/s) - 'overwrite.html' saved [1270/1270] $ ls overwrite.html test.php
このように、overwrite.html
へレスポンスの中身が書き出されます。出力内容を変更したい場合には--execute input=...
を追加して指定することができます。
$ wget http://example.com -O wget.png --execute output_document=overwrite.html --execute input=https://twitter.com/
こうすると出力結果はhttp://example.com
の内容へhttps://twitter.com/
の内容を追記したものになります。
追記したくない場合は--execute http_proxy=...
でproxyを設定して用意したサーバを経由するようにすればおそらく可能です。
~
や$
がescapeshellcmdでエスケープされるので、ログインユーザの~/.bashrc
などを書き換えたい場合はさらに一手間必要です。
curl
cURL - How To Use (マニュアルページ日本語訳)
curlにはwgetのような設定はなさそうですが、7.36.0以降であれば--next
が使えるため自由にリクエストを作れます。
$ curl http://example.com -o test.html --next https://twitter.com/ -o test2.html % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 1270 100 1270 0 0 3464 0 --:--:-- --:--:-- --:--:-- 3469 100 292k 100 292k 0 0 131k 0 0:00:02 0:00:02 --:--:-- 131k $ ls test.html test2.html
pythonやrubyなど
githubでescapeshellcmd
の検索をかけると他の言語を呼び出しているのが多いようでした。オプションが追加できたとしてもその言語経由で呼び出されるプログラムへのオプションになると思われるので、こちらは詳しく調べていません。
*1:この条件は実際にはあまりなさそうですが、出力ファイル名が自由に入力できてしまったらその時点で危険ですし、リクエスト先が自由に入力できる場合 http://legalhackers.com/advisories/Wget-Arbitrary-File-Upload-Vulnerability-Exploit.txt などの影響を受けます
twitterにあったSelf XSS
昨年(2015年)からtwitterに導入された投票機能ですが、入力した文字列がすぐにHTMLとして評価されているのか、Self XSSができてしまう脆弱性があり、投票機能の選択肢に<img oneror=alert(location.host) src=.>
などと入力するとimg
タグが生成されていました。
Google ChromeなどではXSSはCSPでブロックされましたが、IEでは動作しました。
Modern.IEで動いた画面です。img
タグのonerrorでalertが出ています。
hackeroneを通して報告したのですがduplicateであり、数カ月後にようやく修正されたことが公開されました 。*1
入力した文字列が即評価されてSelf XSSになるのも珍しいですが、Self XSS + CSPの組み合わせにより影響度はかなり低い状態でもBountyが払われたのはあまり見ない気がします。
脆弱性"&'<<>\ Advent Calendar 2016 - Adventarの22日目が空いていたので、22+n日目ということで書いてみました。
*1:2016年の1月1日に報告したのですが、年明け(年末?)だというのに数時間で返事が来て驚きました。
React.jsで起こるXSS (advent calendar 12日目の話)
[追記]この記事は情報が古めです。Reactで起こるXSSパターンは他にも存在するので、CTFやbug bountyなどの言葉と一緒に検索してみてください。
この記事は脆弱性"&'<<>\ Advent Calendar 2016の13日目の記事です。
前日の記事はnull
です。
この記事を見ている皆さんは仮想DOMに魂を震わせられているでしょうか*1、それともjavascriptに疲れてきた頃でしょうか。私はimagemagick関連を調べるのに疲れてきたところです。
React.jsで起こるXSS
ここ数年のwebサービスではReact.jsのような仮想DOMを扱うライブラリを使ってhtmlがレンダリングされていることが多いので、典型的なDOM Based XSSなどは少なくなってきたように感じます*2。React.jsを例にすると、JSXがjsに変換されて仮想DOM経由でDOMを組み立てるので、テンプレートなどからHTMLの文字列組立時にエスケープ漏れになるケースが少なくなったということですね。クライアント側をReact.jsのような仮想DOM、サーバ側ではjsonだけを作る構成にするとXSSのややこしいことを考えることが減って随分と楽になります。
それでもXSSが起こるパターンが有りまして、私が把握しているのは以下の2つのケースです。(細かいことを書くとeval()
やnew Function
とか色々ありますが)
後者はIE特有の動作としてcssのexpression()
経由でjavascriptが呼べるものです。IEについては詳しくないので、IE11であっても互換モードなどを指定せずに動くのかは把握できていません。CSS in JSとかでユーザからの入力をstyleで扱っているケースでは問題になるかもしれません。そんなコードを作る機会もあまりなさそうですが。
advent calendar 12日目の話
http://www.adventar.org/calendars/1404#list-2016-12-12
advent calendar 12日目で書いたのは前者のjavascript schemeのパターンでした。(参考: クロスサイトスクリプティング対策 ホンキのキホン - 葉っぱ日記)
記事のURLを入力する部分で、単にjavascript:...
と入力すると保存できないのですが、後ろに//https://...
とついていると保存できました。
現在は修正されていてこのパターンでは保存できません。昨日の日付が変わったあたりにadventarの作者の一人である @hokaccha さんにtwitterで報告した所、朝にはもう修正されていました*3。出力時のエスケープではなく、入力時のバリデーションによって防がれているため、すでに入力されている12日目のリンクはそのまま変わらず出力されています。
React.jsでもこういったパターンが有るのは知っていましたが、実際に刺さるのは初めてです。今回の場合はリンクのURL自体も表示されていますし、実際問題として(修正前も)あまり悪いこともできないので12日目のリンクはそのままにしています。
このXSSのパターンではユーザがクリックしないと発火しませんが、クリックジャッキングを使うかまたはステータスバーの偽装によって誘導するなどが考えられます。chromeでの例 xss *4
*1:http://qiita.com/mizchi/items/4d25bc26def1719d52e6
*2:単に使用されるjQueryのバージョンが上がったなどもあります