function previewContent() {
const input = document.getElementById('input').value;
document.getElementById('preview').innerHTML =
sanitizeHtml(input); // just in case
}
問題点2
ISO-2022-JP
• 古き良き日本の文字コード
• 特定のバイト列が出現すると2バイトで1文字を構成するモードに
• 例: [0x1B] $ B
• [0x1B] ( B が出現すると通常モード(ASCII)に戻る
A A A [0x1B] $ B % F % 9 % H ! z [0x1B] ( B B B B
※制御文字や空白は [0xXX]で表現
AAAテスト★BBB
decode
Slide 88
Slide 88 text
ISO-2022-JP
• 他では出現しない特徴的なバイト列のため自動検出が容易
• 文字コードがないページに [0x1B] $ B を発見 ISO-2022-JPで表示
• 誤認させるのも簡単
• 2バイトで1文字のモードに切り替わると、ASCIIモードに入るま
でバイトが食いつぶされるためXSSの危険あり:
[0x1B] $ B
">
腫�蜆就 ">
decode
Slide 89
Slide 89 text
• 既にASCIIモード中に [0x1B] ( B が出現すると無視されるバイ
ト列として扱われる
ISO-2022-JP
<[0x1B](Bscript>alert(/XSS/)<[0x1B](B/script>
alert(/XSS/)
decode
OK
/XSS/
この辺りを使ってもう一度DOMPurifyのバイパスを考える
Slide 90
Slide 90 text
DOMPurifyバイパス再考
• 今回は属性が使えない
• 食いつぶして属性の中身を露出させるシナリオは無理
[0x1B] $ B
">
[0x1B] $ B
sanitize
どこかに<>を置ける場所があれば…
Slide 91
Slide 91 text
• DOMPurifyはデフォルトで許可
• 内側にHTMLタグっぽい文字列があるとstyleごと消される
• mXSS対策
• < の後にアルファベットなどが続くケースを拒否
• ただしそれ以外のケースでは <> は残ったままになる
a <style><a>
<@>
a
<@>
sanitize
なぜ無効にされた?
• ありえないバイト列で特殊文字が生成されていたから(だと思う)
• 自動選択でXSSが刺さるリスクが高かった
[0x1B] $ B [0x01] [0x03] [0x1B] ( B
[0x1B] $ B [0x01] [0x07] [0x1B] ( B
[0x1B] $ B [0x01] [0x08] [0x1B] ( B
[0x1B] $ B [0x01] [0x1D] [0x1B] ( B
[0x1B] $ B [0x01] [0x1F] [0x1B] ( B
[0x1B] $ B [0x01] [0x3D] [0x1B] ( B
q"
q&
q'
q<
q>
q\
結局、明示的にISO-2022-JPをcharsetに指定したときのデコードはこの動作のままでEOLを迎えた