
Prometheus v3.15.0 - 높음 - 원격 - SSRF - http_sd __unix_socket__ UDS 스크레이프의 정리되지 않은 Dial 잔여물
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · prometheus-unix-socket-dial
분석 문서: https://abraxaslabs.tech/research/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 | 높음: 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 소켓에 연결 | __unix_socket__에서 clientForTarget -> DialContext(ctx, "unix", socketPath) | 실험실 입증. http_sd에서 경로 /sockets/jail/../oracle.sock; 스크레이프 상태 up |
해당 경로에서 ../ 따라가기 | filepath.Clean 없음 / .. 거부 없음 | 실험실 입증. 커널이 순회를 /sockets/oracle.sock으로 해석함 |
| Linux 추상 소켓에 다이얼 | @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는 보통 노출 파싱에 실패하므로 High는 연결/GET이며 Docker JSON의 TSDB 유출이 아님 |
| Docker 변경 (컨테이너 생성, 쓰기) | 스크레이프 동사 | 차단됨. 스크레이프는 GET 전용 |
| 인증 없는 Prometheus HTTP로 스크레이프 작업 추가 | /-/reload, admin API | 차단됨. 두 플래그 모두 기본 비활성; reload에는 본문 없음 |
| 기본 예제 구성에서 사용 | __unix_socket__ 미설정; 예제는 TCP로 localhost:9090 스크레이프 | 차단됨. 트리 내 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임); unix:///var/run/docker.sock을 __address__로 취급한 것을 취약점으로 본 것(이는 거부됨); 기본 예제 구성이 이 잔재라고 주장한 것. 연출: 리버스 셸. 오라클은 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를 제공하고, 두 개의 unix HTTP 오라클을 제공합니다: 파일시스템 /sockets/oracle.sock 및 추상 @prom-lab-oracle. Prometheus는 127.0.0.1:19390에 게시됩니다. Admin API와 라이프사이클은 비활성화된 상태로 유지됩니다.
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 이후의 모든 ..를 거부할 것. scrape YAML의 접두사 허용 목록(allowlist)이 더 강력하다. 운영자 설정에서 온 것이 아니라면 PopulateDiscoveredLabels에서 해당 레이블을 제거하는 것도 SD 주입을 차단한다.
5241a27fe3c6983549fccc32f6e65917408c63cd)scrape/scrape.go · scrape/target.go · config/config.goGNU Affero GPL v3.0. LICENSE 참조. 루프백 실습 전용. 보증 없음.