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

pythonrubyなど

githubescapeshellcmdの検索をかけると他の言語を呼び出しているのが多いようでした。オプションが追加できたとしてもその言語経由で呼び出されるプログラムへのオプションになると思われるので、こちらは詳しく調べていません。

*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では動作しました。

f:id:ooooooo_q:20161231211412p:plain

Modern.IEで動いた画面です。imgタグのonerrorでalertが出ています。

hackeroneを通して報告したのですがduplicateであり、数カ月後にようやく修正されたことが公開されました 。*1

hackerone.com

入力した文字列が即評価されてSelf XSSになるのも珍しいですが、Self XSS + CSPの組み合わせにより影響度はかなり低い状態でもBountyが払われたのはあまり見ない気がします。

脆弱性&quot;&amp;&#39;&lt;&lt;&gt;\ Advent Calendar 2016 - Adventarの22日目が空いていたので、22+n日目ということで書いてみました。

*1:2016年の1月1日に報告したのですが、年明け(年末?)だというのに数時間で返事が来て驚きました。

React.jsで起こるXSS (advent calendar 12日目の話)

この記事は脆弱性"&'<<>\ 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とか色々ありますが)

  • リンクにjavascript:...などが指定できるケース
  • cssが自由に記述できてexpression()が出来るケース(IEのみ)

後者はIE特有の動作としてcssexpression()経由で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のパターンでした。(参考: クロスサイトスクリプティング対策 ホンキのキホン - 葉っぱ日記

f:id:ooooooo_q:20161213071333p:plain

記事のURLを入力する部分で、単にjavascript:...と入力すると保存できないのですが、後ろに//https://...とついていると保存できました。

現在は修正されていてこのパターンでは保存できません。昨日の日付が変わったあたりにadventarの作者の一人である @hokaccha さんにtwitterで報告した所、朝にはもう修正されていました*3。出力時のエスケープではなく、入力時のバリデーションによって防がれているため、すでに入力されている12日目のリンクはそのまま変わらず出力されています。

React.jsでもこういったパターンが有るのは知っていましたが、実際に刺さるのは初めてです。今回の場合はリンクのURL自体も表示されていますし、実際問題として(修正前も)あまり悪いこともできないので12日目のリンクはそのままにしています。

このXSSのパターンではユーザがクリックしないと発火しませんが、クリックジャッキングを使うかまたはステータスバーの偽装によって誘導するなどが考えられます。chromeでの例 xss

*1:http://qiita.com/mizchi/items/4d25bc26def1719d52e6

*2:単に使用されるjQueryのバージョンが上がったなどもあります

*3:https://twitter.com/hokaccha/status/808065021976592385

cookie経由のXSSの話 脆弱性"&'<<>\ Advent Calendar 2016 (4日目)

この記事は脆弱性"&'<<>\ Advent Calendar 2016の4日目の記事です。

前日の記事はHack Patch!: CyVDB-1118関連です。

今回の記事は、eval(document.cookie)になるようなことは避けましょうという内容です。

cookie 経由でのXSSについて。

cookieに値を入れるにはサーバサイドからのレスポンスヘッダーかjavascriptからの操作になります。XSSなどがない限り攻撃者が値を入れることはできなそうですが、以下のような条件により可能です。

  • HTTP通信のMITM
  • クエリパラメータをcookieに載せる実装
  • サブドメインからの攻撃
  • ヘッダインジェクション

特にHTTP通信から攻撃する方法は徳丸先生の記事によるものが詳しく(HTTPSを使ってもCookieの改変は防げないことを実験で試してみた | 徳丸浩の日記)、これを防ぐためにはHSTSをサイト全体に設定する他ない状況となっています。

eval(document.cookie)と等価になるような実装がされていることはあまり多くないのですが、たまーに見かけるのが現状です。

サブドメインからのXSS事例

レアケースかと思われますが、数年前に別サブドメインからcookieを経由してXSSが可能だったケースを見つけたことがありました。

某植物系レンタルサーバさんのあるサーバ管理画面にてこんなコードが有りました。(変数名などは念のため変えています。)

  var param = $.cookie('cookie_name');
    ...
    ...
    $('.class_'+param).attr('checked', true);

一見すると問題があるようには見えませんが、XSSが可能です。というのもjQueryの古いバージョンでは$(.class_<img onerror=alert(1) src=.>)で、DOM Based XSSが発火するためです。(jQuery Migrate Plugin なんてなかった件 - ほむらちゃほむほむ

ここ数年はjQuery 1.10以上が使われることが多いためjQuery由来の問題は減ってきていますが、画面の作成時期によっては古いjQueryがそのまま使われています。

さて、このままでもMITMによって攻撃可能ですが他の攻撃方法がないかを探してみました。その結果、サーバ管理画面のドメインxxx.jpの別サブドメインblog.xxx.jpにてDOM Based XSSを見つけました。こちらもjQuery由来の問題でprettyPhotoというjQuery pluginが脆弱性がある状態で使用されており、細工したURLにアクセスするとXSSが可能な状態でした。

組み合わせると、以下の手順でサーバの管理画面に攻撃することができました。

  1. blog.xxx.jpXSSを含むURLをターゲットにアクセスさせる
  2. javascriptからcookie_name=<img onerror=alert(location.href) src=.>のようなクッキーを発行する
  3. xxx.jpにリダイレクトさせる
  4. xxx.jp$(.class_<img onerror=alert(1) src=.>)が評価されてXSSが発火

eval(document.cookie)と等価な部分があると別ドメインXSSにひきずられることがあり、普通のXSSよりも気づにくい状態となります。cookieのセキュリティーポリシーがscheme+domain+portではないことに由来する問題ですね。

幸いサーバ管理画面の方は報告後すぐに修正されていました。

攻撃を受けにくくするために

この攻撃を発見して以降、念のため重要度が高いドメインではそのときだけシークレットウィンドウを開いて操作することを推奨しています。cookieやlocalStorageによる攻撃、CSRF、ブラウザ拡張機能による影響を受けにくくなります。