intigriti XSS Challenge Writeup
はじめに
久しぶりの投稿になりますが、今回は先週intigritiが開催したXSS Challengeを解いてみたのでそのWriteup記事です。 解けた中から抽選で一人にはBurp Suite Proのライセンスとプライベートプログラムへの招待がもらえるのですが、筆者は今回当選しませんでした😢 また開催されたら挑戦したいと思います。
Our XSS challenge is OVER!
— intigriti (@intigriti) November 25, 2019
🏁 We were looking for a client-side race condition
🏆 Check the video below to discover the winner!
🤔 Wondering what the solution was? Post your write-ups below! 👇 pic.twitter.com/247vI0NtyB
しかし、問題自体は面白いものだったので筆者がどのようにアプローチをして解くことができたのかをまとめました。
今回の問題
<script> const whitelist = ['intigriti.com','intigriti.io']; var url = new URL(location.hash.substr(1)); if(whitelist.indexOf(url.hostname) > -1){ document.write("Redirecting you to " + encodeURIComponent(url.href) + "..."); window.setTimeout(function(){ location = location.hash.substr(1); }); } else{ document.write(url.hostname + " is not a valid domain.") } </script>
今回の問題はDom Based XSSでした。 2019/11/17時点ではまだ問題があるサーバーにアクセスすることが可能なので、興味がある方は自身で解いてみてください。
https://challenge.intigriti.io/
最初のアプローチ
筆者はまず1行ずつコードを読んでいき、結局どこで発火させればいいかをみました。 この時点でXSSを発火させるべき箇所は予想がついていて、7行目にある
location = location.hash.substr(1);
この部分でlocation
の値にjavascript:alert()
を入れることができればこの問題は解くことができるようでした。
しかし、この値挿入するにはif(whitelist.indexOf(url.hostname) > -1){
この条件分岐でTrueの処理に入らなければなりません。
つまり、window.location.hash.substr(1)
の値がintigriti.com
もしくはintigriti.io
でなければなりません。
よって最初のURLの値は以下のようになる必要があります。
https://challenge.intigriti.io/#https://intigriti.com
では、どのようにしたらTrueの処理に入りながらlocation.hash.substr(1)の値を書き換えることができるかを考えた結果、Trueの処理に入った後にハッシュの値を変更することができればこの問題を解くことができると思いました。 実際、ブラウザの開発者ツールを使用してブレイクポイントをうって検証を行ったところ思った通りハッシュの値を変更することによってjavascriptを実行することができました。
Trueの処理に入った後に値を変更することって可能なの?
次に考えたことが、そもそもそんなことができるかという話です。
しかし、今回のコードには怪しそうな箇所がありました。
それはwindow.setTimeout()
です。
なのでsetTimeoutについて調べてみることにしました。
このページには次のような記載がありました。
delay [Optional] 指定した関数やコードを実行する前に待つタイマーの時間をミリ秒 (1/1000 秒) 単位で指定します。この引数を省略すると値 0 を使用しますので "直ちに" 実行する、より正確に言えばできるだけ早く実行することを意味します。
つまりTrueの処理に入った後かつsetTimeoutに指定した関数が実行可能状態になる前にハッシュの値を書き換えればいいのです。
回答
ということで、実際にそのような動作をするコードを書いてみました。
<script> var win; setTimeout(function(){ win = window.open("https://challenge.intigriti.io/#https://intigriti.com","","width=300,height=400") },10); setTimeout(function(){ win.location ="https://challenge.intigriti.io/#javascript:alert(document.domain)" },70); </script>
その結果問題なくXSSは発火しました。
このコードでは最初に問題ページを開いてから50ミリ秒後にそのタブのURLを変更しています。
ブラウザはlocation
の値が書き換えられてもハッシュのみの値が変わっている場合新しいリクエストを送信しません。
よって、問題ページから遷移することなくjavascript実行時に値を書き換えることができます。
しかし、このペイロードには問題があり複数回このHTMLをリロードしないとXSSが発火しない可能性があります。
なぜなら、先ほど紹介した通りsetTimeout
は実行可能状態になったら指定した関数を実行するからです。
よって、今回指定した70ミリ秒は環境によって値が異なるのです。
筆者はブラウザにBurpを通すことによって時間を調整しました。
なので筆者の環境では何度かリロードを行ったら発火しましたが、他の人の環境で発火する保証はありません。
しかし、この問題を解いた人の中には洗礼されたペイロードを書いてる方がいました。
Last week @intigriti released a pretty neat XSS challenge. I knew that race condition like this should be possible but I never actually tested it. Although, in my opinion, hints spoiled the challenge :(
— terjanq (@terjanq) November 25, 2019
My solution: https://t.co/zTjmcUwgwg https://t.co/exhCli0Q7w
なので再現確認を行いたい方はこの方のペイロードを確認するといいかもしれません。
さいごに
今回久しぶりにブログを更新してみました。 最後にブログを投稿してから複数の脆弱性を投稿してきましたが、ハンター側のリスクと会社が思うリスクが一致しないために脆弱性として取り扱われないことや、返事が返ってこないことなどいろいろあるのでバグハンターも大変だと最近実感しました。 時間があれば、筆者が初めてスターバックスでXSSを見つけた時のWriteupも書いてみたいと思います。