#674·gobuster

安全性: 为低严重程度的 DoS 报告请求私有通道

作者: Zasnit1创建于 2026年9月14日更新于 2026年9月14日

Security Report — Unbounded HTTP response-body read in gobuster (memory-exhaustion DoS)

  |   -- | -- Project | gobuster (GitHub.com/OJ/gobuster) Version reviewed | v3.8.2 Commit | c77583fbb8824058d74d38240896eff610683c8e (2026-01-13) Component | libgobuster/http.go → HTTPClient.Request() Class | CWE-400 Uncontrolled Resource Consumption / CWE-770 Allocation of Resources Without Limits Impact | Denial of service (memory exhaustion) of the gobuster process Attacker | The scan target (a malicious or compromised host the operator points gobuster at) Proposed severity | Low (client-side CLI, operator-chosen target — see "Impact & honest severity")

vhost is the realistic attack surface: the operator points gobuster at a URL they are testing, and the server operating at that URL fully controls the response. In vhost mode the impact is amplified in two ways:

  1. During PreRun, the default-vhost body and a random-vhost body are each read in full and retained for the entire scan as v.normalBody / v.abnormalBody (gobustervhost.go:108,124).
  2. Every word then issues another ReturnBody: true request (:173), and gobuster runs many worker threads concurrently, so peak allocation scales with the thread count.

s3/gcs additionally pass the unbounded body straight into xml.Unmarshal / json.Unmarshal (GCS into a map[string]interface{}), which roughly doubles the transient footprint and adds a parser-side amplification vector.

Root cause

The response body is untrusted input controlled by the scan target, but it is read without any upper bound. http.Client.Timeout limits how long the read may run, but within that window a fast server can deliver an amount of data limited only by throughput — which then all lands in a single []byte.

Proof of concept

The following faithfully reproduces the exact vulnerable pattern (io.ReadAll on a target-controlled body under a gobuster-style 10 s client timeout) and contrasts it with the proposed fix. It does not require building gobuster itself.

go
// malicious server streams an oversized body; victim mirrors libgobuster/http.go
func vulnerableRead(body io.Reader) (int, float64) {
    b, _ := io.ReadAll(body)                       // current gobuster behaviour
    return len(b), heapMiB()
}
func fixedRead(body io.Reader, max int64) (int, float64) {
    b, _ := io.ReadAll(io.LimitReader(body, max))  // proposed fix
    return len(b), heapMiB()
}

Measured results (Go 1.22, loopback, single request, 10 s client timeout):

Simulated malicious target streams: 500 MiB
baseline heap: 0.1 MiB
[VULNERABLE  io.ReadAll]  buffered 500 MiB
…