読者です 読者をやめる 読者になる 読者になる

cocuh's note

type(あうとぷっと) -> 駄文

SVGでXSSをしてみる。その1

svgjavascriptが埋め込んだ時の挙動がどう違うのか気になっていろいろ試したのでとりあえずまとめてみます。
特に言及していなければfirefoxで試しています。

scriptを混入させたsvgを書く

思いつくjavascriptをいれる方法をひと通り入れてみました。
明らかに動かなさそうなものやxssじゃないものもはいってますが挙動が気になったので入れてみました。

  1. onclick="console.log()"
  2. 内部スクリプトでのelem.onclick = function(){console.log()}
  3. 外部スクリプトでのelem.onclick = function(){console.log()}
  4. style属性でのexpression: style="stroke-width:expression(console.log())"
  5. cssでのexpression: .xss{stroke-width:expression(console.log())}
  6. htmlでいう<img src='javascript:console.log()'/>
  7. htmlでいう<img src='xss.js'/>
  8. aタグのhref='javascript:console.log'
  9. scriptタグでの外部スクリプト(script.js)を実行
  10. scriptタグでのインラインスクリプトを実行
<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet href="xss.css" type="text/css"?>

<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
    <text x="100" y="100" onclick="console.log('text onclick: '+document.domain)">onclick</text>
    <text x="100" y="120" id="elem_ex">event handler(external)</text>
    <text x="100" y="140" id="elem_in">event handler(internal)</text>
    
    <circle cx="40" cy="40" r="20" style="stroke-width:expression(console.log('expression:'+document.domain));;fill:red;stroke:black;"></circle>
    <circle cx="40" cy="80" r="20" class="xss"></circle>
    
    <image x="0" y="100" height="100px" width="100px" xlink:href="cat.png"/>
    <image x="0" y="200" height="100px" width="100px" xlink:href="javascript:console.log('image src:'+document.domain)"/>
    <image x="0" y="300" height="100px" width="100px" xlink:href="consolelog.js"/>
    
    <a xlink:href="javascript:console.log('a tag href:'+document.domain)"><text x="100" y="160">a tag href</text></a>
    <script xlink:href="script.js"></script>
    <script>
    	console.log('inline script: '+document.domain)
        document.getElementById("elem_in").onclick=function(){
            console.log('internal javascript onclick:'+document.domain)
        };
    </script>
</svg>
.xss{
    fill:blue;
    stroke:red;
    stroke-width: expression(console.log("css expression:"+document.domain));
}
  • consolelog.js
console.log("console log: "+document.domain)
  • script.js
window.onload = function(){
    console.log('external javascript:'+document.domain);
    document.getElementById("elem_ex").onclick=function(){
        console.log('external javascript onclick:'+document.domain)
    };
};

一応gistに上げてあります。

https://gist.github.com/cocu/8085221


これを直接ブラウザで表示させるとこんな感じになります。
f:id:cocu_628496:20131223014213j:plain

onclickを設定した要素をすべてクリックした後のコンソールがこれです。
f:id:cocu_628496:20131223014948j:plain
見難いので先ほどで通らなかったものに線を引きます。

  1. onclick="console.log()"
  2. 内部スクリプトでのelem.onclick = function(){console.log()}
  3. 外部スクリプトでのelem.onclick = function(){console.log()}
  4. style属性でのexpression: style="stroke-width:expression(console.log())"*1
  5. cssでのexpression: .xss{stroke-width:expression(console.log())}*2
  6. htmlでいう<img src='javascript:console.log()'/>*3
  7. htmlでいう<img src='xss.js'/>
  8. aタグのhref='javascript:console.log'
  9. scriptタグでの外部スクリプト(script.js)を実行
  10. scriptタグでのインラインスクリプトを実行

css関係のxssとimg src系のxssは通っていないのがわかります。


svgをhtmlに入れる

先程はsvgそのままをブラウザで表示させました。
これをhtmlに入れてみます。

入れる方法としては次の7つがあります。

  • objectタグ
  • embedタグ
  • iframeタグ
  • svgタグ
  • imgタグ
  • inputタグtype=image
  • background属性

https://gist.github.com/cocu/8086611

それぞれの挙動の違いについてはこちらのページがよくまとまっています。
表示方法についての補足を参照してください。
(ほぼ同じことを試しています。)

svg要素の基本的な使い方まとめ
http://www.h2.dion.ne.jp/~defghi/svgMemo/svgMemo_02.htm

これらを実際にそれぞれ試してみました。

objectタグ

f:id:cocu_628496:20131223030101p:plain

embedタグ

f:id:cocu_628496:20131223030116p:plain

iframeタグ

f:id:cocu_628496:20131223031204p:plain

svgタグ(inline svg)

f:id:cocu_628496:20131224025124p:plain
※inline svgではxmlcss読み込みができないためhead内のlinkタグで行っています
間違った画像を貼ってしまっていたので貼り直しました。
修正前:http://f.hatena.ne.jp/cocu_628496/20131223031902 (2013 12/24 02:53)

imgタグ

f:id:cocu_628496:20131223031359p:plain

inputタグtype=image

f:id:cocu_628496:20131223031406p:plain

background属性

f:id:cocu_628496:20131223031412p:plain

結論

objectタグ,embedタグ,iframeタグ,svgタグに関して
  • svg単体の挙動と同じ
  • スクリプトが実行された
  • svg内aタグでカーソルが変化する
  • 外部ファイルも反映された(ねこ,2つ目の円のcss宣言)
imgタグ,inputタグtype=img,background属性に関して
  • svg単体の挙動と同じ
  • スクリプトが実行されない
  • 外部ファイルも反映されていない(仕様と違う?)

これらの違いに関してw3cに記述がありました。(Editor's Draftですが)
先ほどの実験での前者をDynamic Interactive Mode
後者がAnimated Modeと呼ばれているようです。

https://svgwg.org/specs/integration/#referencing-modes

追記(2013 12/23 0416)

ちなみに、Animated Modeの挙動はブラウザによっても違うようです。

  • IE(11)

f:id:cocu_628496:20131223041327p:plain

f:id:cocu_628496:20131223041309j:plain

f:id:cocu_628496:20131223040847j:plain

IEとoperaは外部ファイルが反映されており、スクリプトは実行されていません。

chromeはまた違った挙動を示しています(要検証)


次の記事でContents-Security-Policy下での挙動について触れます。
http://cocu.hatenablog.com/entry/2013/12/24/060950

*1:[INFO]"Error in parsing value for 'stroke-width'. Declaration dropped."はこれ

*2:[INFO]"Error in parsing value for 'stroke-width'. Declaration dropped."のもうひとつはこれ

*3:[ERROR]"uncaught exception: ReferenceError: console is not defined"を吐いたのはこれ。