😈

【JavaScript】intigritiの脆弱性クイズ 【XSS】

2023/06/06に公開

久々にintigriti社の脆弱性クイズに挑戦しました。
今回は比較的に簡単なXSSです。

intigritiのクイズをやってみたシリーズ
https://zenn.dev/k41531/articles/e7498612cabe05
https://zenn.dev/k41531/articles/f468b9be977ea9
https://zenn.dev/k41531/articles/48bd23ac24b645

問題

https://twitter.com/intigriti/status/1636350636634656771

<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8">
</head>
<body>
	<script>
	let search = new URLSearchParams(location.search)
	const redirectURL = search.get('redirectURL');
	
	let regex = /^[a-zA-Z0-9\t]+\:\/\/example\.com\/.*/
	let regexUrl = redirectURL.match(regex)
	let antiXSS = redirectURL.startsWith('javascript')

	if (regexUrl && !antiXSS) {
		location.href = redirectURL;
	};
	</script>
</body>
</html>

解説

公式の解説はこちら
https://www.youtube.com/watch?v=v1KGyiEA81o

どんなコード?

  • redirectURLパラメータを使用して、リダイレクトする。
  • redirectURLの値の先頭がjavascriptでないかを検証する。
  • redirectURLの値がexample.comのURLであるかを検証する。

どうやって攻撃する?

  • redirectURLに悪意のあるペイロードを入れる。

例えば、javascript:alert(1)などのペイロードです。
もし、このコードが検証されていなければ、このペイロードを設定することで、ウェブページにアクセスしたユーザ全員に対してアラートを表示させることができます。
しかし、前述の通り、redirectURLの値の先頭がjavascriptでないかを検証しているため、このペイロードは検証に引っかかります。

バイパスするには

  1. redirectURLがjavascript:で始まらないことを確認するantiXSSチェック

  2. redirectURLが特定のパターンに一致することを確認する正規表現チェック

1のantiXSSのバイパスは簡単です。
javascript:alert(1)のようなペイロードをjavascriptで始まらないようにするだけです。
javascriptを全て大文字(JAVASCRIPT:alert(1))にしたり、jだけ大文字(Javascript:alert(1))にしたり、などの方法があります。

2の正規表現は、/^[a-zA-Z0-9\t]+\:\/\/example\.com\/.*/です。
任意の英数字の後に://example.com/という文字列が続くURLかどうかを検証しています。
そのため、redirectURL=https://example.com/の場合は、当然検証に引っかからず、リダイレクトが行われます。
では、redirectURL=Javascript:alert(1)のようなペイロードを設定するにはどうすればいいでしょうか。

改行を利用したバイパス

example.comというテキストが含まれないといけないので、次のようなペイロードを作ります。
redirectURL=Javascript://example.com/
これは、正規表現チェックもantiXSSチェックも通過します。
一方で、javascriptでは//を使うことで、その行の残りの部分がコメントアウトされてしまうので、それ以降に何を書いても無視されます。
そこで、改行を利用します。

//example.com/
alert(1)

これをredirectURLに設定できれば、alert(1)が実行されるれるので、XSSが発生します。

改行はURLに含めることができないので、URLエンコードを利用します。
Webで改行をURLエンコーディングについて検索すると次のような文字列が得られます。

  • %0D%0A : CRLF
  • %0A : CR

WindowsならCRLF、MacやLinuxならCRなどといった違いがあるようですが、今回使うのはどちらでもありません。
%250aを使います。
%25%のURLエンコードなので、%250a%0aと同じ、つまり、改行を2回エンコードしたものです。

解答

よって、次のペイロードが答えです。
redirectURL=Javascript://example.com/%250aalert(1)

なぜ2回エンコードするのか

ペイロードがredirectURL=Javascript://example.com/%0aalert(1)だった場合、

let search = new URLSearchParams(location.search)
const redirectURL = search.get('redirectURL');

この部分での、redirectURLの値がJavascript://example.com/\nalert(1)になります。
これだと改行コードも含めてコメントアウトされてしまします。

ペイロードがredirectURL=Javascript://example.com/%0250aalert(1)であれば、
redirectURLの値がJavascript://example.com/%0aalert(1)となるので、リダイレクトした時に改行コードに変換されて、alert(1)が実行されます。

Discussion