Skip to content

Commit 2d7227c

Browse files
committed
Fix: Validation gap; high severity; confidence: certain.
=== Affected Locations `dnscrypt-proxy/oblivious_doh.go:167` === Summary `decryptResponse()` reads a 2-byte length field from decrypted ODoH plaintext without first verifying that the plaintext is at least 2 bytes long. A validly decrypting attacker-controlled response with 0-1 bytes of plaintext causes a slice panic and process denial of service. === Provenance Verified from the reported finding and reproduced against reachable production call sites. Reproduction used a temporary test that built a real decryptable ODoH response and confirmed the panic path. Source report provenance includes Swival Security Scanner: https://swival.dev === Preconditions Attacker controls a decryptable ODoH response plaintext shorter than 2 bytes, which in practice requires operating the ODoH target or otherwise possessing the response secrets. === Proof `dnscrypt-proxy/query_processing.go:175` and `dnscrypt-proxy/serversInfo.go:1060` pass attacker-controlled ODoH replies into `decryptResponse()`. In `dnscrypt-proxy/oblivious_doh.go:167`, the function executes `binary.BigEndian.Uint16(responsePlaintext[0:2])` immediately after decryption. No prior check enforces `len(responsePlaintext) >= 2`. When decryption yields an empty or 1-byte plaintext, `responsePlaintext[0:2]` panics with an out-of-bounds slice, terminating the process. This was reproduced with a temporary Go test that generated a real ODoH query, reconstructed the matching HPKE server context, returned an AEAD-authenticated response with empty plaintext, and observed the panic during `decryptResponse()`. === Why This Is A Real Bug The input is reachable from production network paths, the panic occurs before any recovery in the affected flow, and a panic in this process is a denial of service. The attacker capability is constrained but realistic for a malicious or compromised ODoH target, matching the product threat surface. === Fix Requirement Reject decrypted ODoH plaintexts shorter than 2 bytes before reading the length field. === Patch Rationale The patch adds a minimum-length check on `responsePlaintext` before slicing `responsePlaintext[0:2]`. This converts the crash condition into normal malformed-response handling while preserving existing behavior for well-formed inputs. === Residual Risk None === Patch `004-short-decrypted-odoh-plaintext-triggers-out-of-bounds-read.patch` adds a guard in `dnscrypt-proxy/oblivious_doh.go` to verify `len(responsePlaintext) >= 2` before calling `binary.BigEndian.Uint16(...)`, returning an error for malformed short plaintexts instead of panicking.
1 parent bac7789 commit 2d7227c

File tree

1 file changed

+3
-0
lines changed

1 file changed

+3
-0
lines changed

dnscrypt-proxy/oblivious_doh.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,9 @@ func (q ODoHQuery) decryptResponse(response []byte) ([]byte, error) {
180180
if err != nil {
181181
return nil, err
182182
}
183+
if len(responsePlaintext) < 2 {
184+
return nil, fmt.Errorf("Malformed response")
185+
}
183186

184187
responseLength := binary.BigEndian.Uint16(responsePlaintext[0:2])
185188
if int(responseLength)+2 > len(responsePlaintext) {

0 commit comments

Comments
 (0)