面白い手法 ~XSS編~
脆弱性を顕現化させる手法は様々ありますが、今回は「いやー、こんなことよく考えたな。天才かよ!」と興奮したXSSの手法を一つご紹介します。
概要
元ネタはPortSwigger社の公式ホームページで公開している記事です。ざっくりとまとめると、accesskey属性を利用すればこれまではJavaScriptの実行は難しいと考えられていたパターンでも、JavaScriptの実行が可能になるよというお話です。記事の公開は2015年ですが、手法としてはあまり広く認知されていない感覚があります。恐らく、ブラウザによって挙動が異なることや、重箱をつつく系の攻撃手法なので網羅性を求める脆弱性診断サービスではあまり指摘されないことなどが理由として考えられます。
この手法の画期的なポイントは、type属性がhiddenに設定されているため要素は画面に表示されず、「onmouseover」や「onfocus」といったJavaScriptイベントを発生させることが難しいと考えられていたパターンにおいて、accesskey属性を利用すれば要素が画面に非表示でもJavaScriptイベントを発生させられることです。
accesskey属性とは
では、このaccesskey属性とは何なのでしょうか?
簡単に説明すると、キーボードショートカットキーを割り当てることができる属性です。それによって、アプリケーションの操作性の向上やマウス操作が難しい場面でアプリケーションを操作できるなどの利点があります。ブラウザでとあるサイトを見ている際に、途中でページの最初に戻るために「Ctrl + Home」とキーボード操作する、それに近いことaccesskey属性を利用することで開発者側で設定することができます。例えば、特定の入力欄に移動する、リンク先の記事に飛ぶなどです。
以下のコードでは、accesskey属性として「x」で入力欄に移動する。「y」でリンク先に遷移するアクセスキーを設定しています。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>アクセスキー</title>
</head>
<body>
<form action="#">
<div>
<label for="l1">入力欄に移動(accesskey="x")</label>
<input type="text" accesskey="x">
</div>
<div>
<a href="https://kisri.net/" accesskey="y">リンク先に遷移(accesskey="y")</a>
</div>
</form>
</body>
</html>

利用するブラウザによってアクセスキーを有効にする方法は異なります。詳細はMDN Web Docsの該当ページをご確認ください。今回はGoogle Chromeを利用した場合の想定で「Alt + アクセスキー文字列」で有効となります。実際に「Alt + x」とキーボードで入力すると以下のようになりました。

少しわかりづらいですが、先ほどの画像と比べると画面の入力欄の枠線が太くなっており、入力欄に移動していることが分かります。同様に「Alt + y」を入力するとリンク先に遷移します。
アクセスキーを使ったXSSの手法
では、このアクセスキーを使ったXSSの手法の詳細を確認していきます。ここでは例として、単純な登録フォームの入力画面を想定してみます。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>アクセスキーを利用したXSS</title>
</head>
<body>
登録
<form action="/testform" method="post">
<div>
<label for="l1">お名前</label>
<input type="text" name="name">
</div>
<div>
<label for="l2">年齢</label>
<input type="text" name="age">
</div>
<input type="hidden" name="testToken" value="12345" >
<input type="submit" value="登録!" />
</form>
</body>
</html>

そして、注目するのはtype属性がhiddenに設定されているtestTokenです。このtestTokenは別のページからこのページに遷移する際のリクエストに設定されたパラメーター値が使われる仕様とします。まず、onclick属性を含むコードを挿入することが出来た場合を考えてみます。HTMLの該当の箇所はこのようになります。
<body>
登録
<form action="/testform" method="post">
<div>
<label for="l1">お名前</label>
<input type="text" name="name">
</div>
<div>
<label for="l2">年齢</label>
<input type="text" name="age">
</div>
<input type="hidden" name="testToken" value="12345" onclick="alert(document.domain)" >
<input type="submit" value="登録!" />
</form>
</body>

onclick属性を挿入することができましたが、type属性がhiddenに設定されているinputタグを画面上でクリックすることはできないので、JavaScriptを実行することができません。
次にaccesskey属性を含むコードを挿入できた場合を考えます。例えばtestTokenの値として「12345" accesskey="x" onclick="alert(document.domain)」を挿入しました。するとHTMLの該当の箇所はこのようになります。
<body>
登録
<form action="/testform" method="post">
<div>
<label for="l1">お名前</label>
<input type="text" name="name">
</div>
<div>
<label for="l2">年齢</label>
<input type="text" name="age">
</div>
<input type="hidden" name="testToken" value="12345" accesskey="x" onclick="alert(document.domain)" >
<input type="submit" value="登録!" />
</form>
</body>

そして、Google Chromeで「Alt + x」とキーボードで入力します。残念ながら、JavaScriptは作動しませんでした。そこでFirefoxで試してみます。Firefoxで「Alt + Shift + x」とキーボードで入力します。すると、JavaScriptは作動しました。

どうやらPortSwiggerが公開しているCross-site scripting (XSS) cheat sheetの情報によると、inputタグ内のaccesskey属性を利用したJavaScriptの作動は、Firefoxのみ利用可能な手法となっているそうです。

実際に攻撃は成立するか?
ではこの手法が実際に成立するかどうか考えてみたいと思います。攻撃が成立する条件として考えられるのは以下の3つです。
- ユーザー由来の入力値に対する適切な確認・無効化対策が取られていない
- 被害者はFirefoxのブラウザを利用している
- 被害者がアクセスキーを有効にするために行動する
JavaScriptが作動した前述のHTMLを見て気が付いた方も多いかと思います。そもそもユーザー由来の入力値に対して、ダブルクォーテーションなどの適切な無効化処理が抜けてしまっていることが大きな要因です。また、利用自体があまり推奨されていないアクセスキーを被害者自身に利用させるというシナリオも実現可能性は高いとは思えません。
結論としては、XSSの基本的な防御策が施されていれば、攻撃が実際に成立するため実現性は低いと考えられます。
まとめ
今回紹介したXSSの手法はいかがでしたでしょうか。攻撃手法自体は重箱をつつく系統のものなので、初めて知ったという方もいるかもしれません。また改めて、結局はXSSの基本的な対策が重要であるという気づきも与えてくれたのではないでしょうか。