
Prometheus v3.15.0 - 高 - リモート - SSRF - http_sd __unix_socket__ 未サニタイズの Dial が UDS スクレイプに残存
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · prometheus-unix-socket-dial
クラス: SSRF 到達範囲: リモート
Prometheus v3.15.0 (5241a27) - CNCF / Prometheus
v3.15.0 で Unix Domain Socket スクレイプが追加された (#12024 / #18091)。そのパスはターゲットラベル __unix_socket__ であり、オペレータの許可リストではない。http_sd と file_sd はそのラベルをそのままコピーする。PopulateLabels はそれを一切検証しない。そして newUnixSocketScrapeClient は filepath.Clean も許可リストも ../ や Linux 抽象 @ 名の拒否もなしに DialContext(ctx, "unix", socketPath) を呼び出す。
TCP スクレイプではこれはできない。 CheckTargetAddress は依然として / を含む __address__ 値を拒否するため、アドレスとしての unix:///var/run/docker.sock は拒否される。新しいラベルが穴である。
標準設定ではこのラベルは設定されない。Prometheus への未認証 HTTP ではジョブを追加できない。これは SD ゲート型である。
| ID | CVE 未採番 |
| クラス | SSRF (SD ラベルからの未サニタイズ unix Dial) |
| 到達範囲 | リモート (SD ゲート型: http_sd / file_sd の書き込み者) |
| CWE | CWE-918, CWE-22 |
| CVSS | High: 8.6 CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:N/A:N |
| 製品 | Prometheus |
| 影響を受けるバージョン | v3.15.0 (5241a27fe3c6983549fccc32f6e65917408c63cd)、イメージ prom/prometheus:v3.15.0 |
| 認証 | SD ゲート型: http_sd / file_sd ラベルの書き込み者 (または信頼できないメタデータを __unix_socket__ にコピーする relabel) |
| ライセンス | GNU Affero GPL v3.0 |
| ラボ | 127.0.0.1 のみ |
http_sd JSON (または書き込み可能な file_sd) の制御で十分である。SD URL はオペレータ設定である。残る問題は、グループの labels が __unix_socket__ を設定でき、Prometheus がそれを Dial してしまうことである。
| 権限 | メソッド / シンク | ラボ実証または推測 |
|---|---|---|
| Prometheus UID が開けるホスト unix ソケットに接続 | clientForTarget -> __unix_socket__ からの DialContext(ctx, "unix", socketPath) | ラボ実証済み。http_sd からのパス /sockets/jail/../oracle.sock、スクレイプヘルス up |
そのパス内の ../ を辿る | filepath.Clean なし / .. 拒否なし | ラボ実証済み。カーネルがトラバーサルを /sockets/oracle.sock に解決 |
| Linux 抽象ソケットを Dial | @name の同じ Dial | ラボ実証済み。__unix_socket__=@prom-lab-oracle、スクレイプヘルス up |
| そのソケット上で Prometheus UID として HTTP GET | スクレイプは GET、__metrics_path__ がリクエストパス | ラボ実証済み。GET /fs-traversal と /abstract、User-Agent Prometheus/、lab_witness が取り込まれた |
| TCP スクレイプでは指定できないソケットに到達 | CheckTargetAddress は __address__ 内の / を拒否 | ラボ実証済み。__address__ としての unix:///var/run/docker.sock は is not a valid hostname をログ出力、アクティブターゲットにならず、prometheus_target_sync_failed_total=1 |
| それらのソケットが対象範囲内なら Docker Engine / kubelet HTTP API に GET | __unix_socket__=/var/run/docker.sock と Docker API パス | 推測。同じ未サニタイズ Dial。このラボは実際のエンジンソケットをマウントしない。JSON API は通常 exposition パースに失敗するため、High は接続/GET であり、Docker JSON の TSDB 持ち出しではない |
| Docker を変更 (コンテナ作成、書き込み) | スクレイプの動詞 | 不可。スクレイプは GET のみ |
| 未認証 Prometheus HTTP 経由でスクレイプジョブを追加 | /-/reload、admin API | 不可。両フラグともデフォルト無効、reload にはボディがない |
| デフォルトの example 設定でこれを使う | __unix_socket__ 未設定、example は localhost:9090 を TCP でスクレイプ | 不可。ツリー内の k8s/consul/docker SD は __meta_* のみを出力し、それらのプレフィックスは relabel 後に削除される |
ベンダーのセキュリティモデルは既に SD が TCP スクレイプターゲットを選択することを許容している。新しいプリミティブは __address__ では指定できなかったホスト IPC である。
UDS スクレイプの 3.15 FEATURE ラインの後、prometheus/prometheus v3.15.0 をピン留めした。このピンでのデフォルト OSS は admin API、lifecycle HTTP、remote-write receiver、OTLP receiver が無効である。PopulateDiscoveredLabels はすべての http_sd グループラベルをコピーする。__unix_socket__ は __meta_* ではないため、メタデータワイプを生き残る。PopulateLabels は __address__ のみをチェックする。
func (sp *scrapePool) clientForTarget(t *Target) *http.Client {
socketPath := t.labels.Get(UnixSocketLabel)
if socketPath == "" {
return sp.client
}
client, err := newUnixSocketScrapeClient(socketPath, ...)
func newUnixSocketScrapeClient(socketPath string, ...) (*http.Client, error) {
optFuncs = append([]config_util.HTTPClientOption{config_util.WithDialContextFunc(
func(ctx context.Context, _, _ string) (net.Conn, error) {
return (&net.Dialer{}).DialContext(ctx, "unix", socketPath)
},
)}, optFuncs...)
CheckTargetAddress は依然として address 内の / を拒否する。URL() は __scheme__ / __address__ / __metrics_path__ から scheme/host/path を保持し、ソケットは dial 専用である。
既に記録済みの誤った道: SD が既に TCP ターゲットを選択するためこれをモデル内 NONE と採点すること (新しいプリミティブはホスト IPC である)、__address__ としての unix:///var/run/docker.sock を穴とみなすこと (それは拒否される)、デフォルトの example 設定がこの残存問題だと主張すること。茶番: リバースシェル。オラクルは SD JSON が指定したソケット上でのスクレイプヘルス up である。
python3 prometheus-unix-socket-dial-Abraxas-Labs.py
または
cd lab
./run.sh
prom/prometheus:v3.15.0 は Python サイドカーと netns を共有する。サイドカーは 127.0.0.1:8080/targets.json で http_sd を、2 つの unix HTTP オラクル (ファイルシステム /sockets/oracle.sock と抽象 @prom-lab-oracle) を提供する。Prometheus は 127.0.0.1:19390 で公開される。Admin API と lifecycle は無効のままである。
SUCCESS prometheus-unix-socket-dial sd=http_sd fs-traversal=yes abstract=yes tcp-slash-rejected=yes how=sd-labels PROMETHEUS-UNIX-SOCKET-DIAL-WITNESS
IOC: scrapeUrl=http://localhost:1/fs-traversal と /abstract、両方ともヘルス up。TCP スラッシュはアクティブターゲットにならなかった。
SD からの __unix_socket__ を Dial する前に、サニタイズされた絶対ファイルシステムパスであることを確認すること。空値、NUL、Linux 抽象 @ 名、相対パス、および filepath.Clean 後の .. を拒否すること。スクレイプ YAML からのプレフィックスの許可リストがより強固である。オペレータ設定由来でない限り PopulateDiscoveredLabels からラベルを削除することも SD インジェクションを塞ぐ。
5241a27fe3c6983549fccc32f6e65917408c63cd)scrape/scrape.go · scrape/target.go · config/config.goGNU Affero GPL v3.0。LICENSE を参照。ループバックラボのみ。無保証。