目次
概要
BIG-IP Forward Proxy構成でAPMモジュールの機能を使わずにURLフィルタリングを行う際に、
iRulesで HTTP::host
コマンドを用いてHTTPヘッダーの処理をしてると、
処理の仕方によってはアクセス制御をバイパス (迂回)される可能性があるため参考例を記載します。
具体的にアクセス制御がバイパスされる通信パターンは、 下記のようにHTTP Request LineとHTTP Host Headerで異なる通信先を指定している場合です。
GET http://mal-site.test/ HTTP/1.1 Host: www.google.com
ただし、APMのAccess Profile (Per-Session Policy)をHTTP Forward Proxy向けVirtual Serverに適用していると、
Explicit-proxy request hostname and Host header disagree.
のメッセージが表示されて偽装が検知されるため、
アクセス制御はバイパスされないのを確認しております。
よって、本記事の内容は、セキュリティが重要視されるForward Proxy構成であるにも関わらず、APMモジュールの機能を使用していない特殊事例になります。
ただ、実装の仕方を間違えるとアクセス制御がバイパスされる可能性があるので、参考例として情報を整理しております。
また、BIG-IP自体の脆弱性ではなく、あくまでiRulesでのコード拡張による実装のミスが所以するものです。
もし悪用されると、悪意あるWeb Serverに機密情報をアップロードされるようなリスクが考えられます。
対象バージョン情報
本記事に記載の情報は、下記のバージョンで調査を行っております。
F5 BIG-IP Virtual Edition Version 16.1.2
BIG-IPのモジュールは、LTM, APM, SSLOを有効化しております。SSL Orchestrator Package Version 16.1.0
SSLOはiApps LXのパッケージとして提供されているため、個別のバージョン情報があります。
本記事に該当するのは、APMを用いずにLTMの機能のみでiRulesを用いて脆弱なロジックで実装した場合です。
脆弱な実装の例
下記のコードは、HTTP Forward Proxy向けVirtual Serverに適用して、URLフィルタリングを自前実装する想定のコンセプトです。
- 脆弱な実装のコード
when HTTP_PROXY_REQUEST { log local0. "Debug: HTTP::host value is [HTTP::host]" set allow_flag 0 ;# 0: deny 1: allow if { [string tolower [HTTP::host]] starts_with "www.example.com" } { set allow_flag 1 log local0. "Match: start_with www.example.com rule" } elseif { [string tolower [HTTP::host]] starts_with "www.google.com" } { log local0. "Match starts_with www.google.com rule" set allow_flag 1 } elseif { [string tolower [HTTP::host]] starts_with "one.one.one.one" } { log local0. "Match: starts_with one.one.one.one" set allow_flag 1 } elseif { [string tolower [HTTP::host]] starts_with "1.1.1.1" } { log local0. "Match: starts_with 1.1.1.1" set allow_flag 1 } if { $allow_flag == 0 } { reject log local0. "Debug: Reject Connection." } }
- 特筆すべき点は、
if { [string tolower [HTTP::host]] starts_with "www.example.com" } {
のように、HTTP::host
の情報を直接文字列の比較に使用している点です。
アクセス制御がバイパスされるシナリオ
まず前提となる情報として HTTP::host
はHTTPのHost Headerの情報を評価します。
表現を変えると、HTTP Request Lineの情報は評価されません。
そのため、HTTP Request Lineには実際の通信先を指定して、 Host Headerにはアクセス許可されていそうな接続先の情報を渡すことで、アクセス制御がバイパスされる可能性があります。
- 具体的には下記の例のようなHTTP Request LineとHTTP Host HeaderのHTTP(80/tcp)通信となります。
GET http://mal-site.test/ HTTP/1.1 <= HTTP Request Lineは実際の接続先 Host: www.google.com <= HTTP::host で取得される値
curl
コマンドを用いた再現は下記の例のようになります。
curl --verbose --proxy http://PROXY-ADDRESS:PORT/ http://mal-site.test/ --Header 'Host: www.example.com'
補足となりますが、HTTPS(443/tcp)通信だと CONNECT Method で第一段階で実通信先にトンネルを張りに行く動作をしていたので、 アクセス制御はバイパスされていないように見受けられました。
curl --verbose--proxy PROXY-ADDRESS:PORT https://mal-site.test/ --Header 'Host: www.google.com' --insecure
HTTPS(443/tcp)通信時のHTTP Request LineとHTTP Host Headerは、下記のように実通信先 (悪意あるWeb Server)を示しました。
CONNECT mal-site.test:443 HTTP/1.1 Host: mal-site.test:443
実装の改善案
改善案では接続先を HTTP::uri
の情報を元に割り出すように修正しました。
- 改善案のコード
when HTTP_PROXY_REQUEST { log local0. "Debug: HTTP::uri value is [HTTP::uri]" set domain [string tolower [URI::host [HTTP::uri]]] log local0. "Debug: domain value is $domain" set allow_flag 0 ;# 0: deny 1: allow if { $domain starts_with "www.example.com" } { set allow_flag 1 log local0. "Match: start_with www.example.com rule" } elseif { $domain starts_with "www.google.com" } { log local0. "Match starts_with www.google.com rule" set allow_flag 1 } elseif { $domain starts_with "one.one.one.one" } { log local0. "Match: starts_with one.one.one.one" set allow_flag 1 } elseif { $domain starts_with "1.1.1.1" } { log local0. "Match: starts_with 1.1.1.1" set allow_flag 1 } if { $allow_flag == 0 } { reject log local0. "Debug: Reject Connection." } }
- 特筆すべき点は、
set domain [string tolower [URI::host [HTTP::uri]]]
の処理にて、
URI::host
の引数に HTTP::uri
の情報を渡してドメイン部分だけを取り出しています。
根本的な対策
iRulesによるコードの拡張を行っていると、脆弱なロジックで実装してしまう可能性があるため、 BIG-IPの本来のAPMモジュールの機能をできるだけ活用するのが好ましいです。