Skip to content
GOAHEAD-SA-2026-0005

GOAHEAD-SA-2026-0005

github.com • October 27, 2026

Important — OpenSSL builds are NOT vulnerable. This defect is in GoAhead's mbedtls TLS backend ( src/mbedtls.c ). Builds linked against OpenSSL — the project's preferred and default TLS backend — handle Diffie-Hellman parameters via a separate code path and are not exposed.

Important — OpenSSL builds are NOT vulnerable. This defect is in GoAhead's mbedtls TLS backend ( src/mbedtls.c ). Builds linked against OpenSSL — the project's preferred and default TLS backend — handle Diffie-Hellman parameters via a separate code path and are not exposed.

GoAhead's mbedtls TLS backend transmits a 1-byte Diffie-Hellman modulus on every TLS 1.2 DHE-RSA ServerKeyExchange because the third argument of mbedtls_ssl_conf_dh_param_bin — the byte length of the prime P — is set to sizeof(dhm_g) (1 byte) instead of sizeof(dhm_p) (256 bytes). The 2048-bit RFC 3526 group the code declares is silently truncated to its first byte ( P=0xFF=255 ). MbedTLS does not validate the bit-length of the configured P and accepts the truncated buffer; the server then advertises P_len=1, P=0xFF, G_len=1, G=0x02 on the wire.

A passive observer who captures a completed DHE handshake against a permissive client can recover the server's DH exponent in single-digit iterations (the reporter took five), derive the premaster and master secrets, derive the AES-256-GCM session keys, and decrypt the entire HTTPS exchange. Forward secrecy is destroyed for the affected session. The defect does not affect TLS 1.3 or any ECDHE cipher suite — those handshakes do not consult the configured finite-field group — and does not affect builds linked against OpenSSL.

The practical attack surface is bounded by the client: most modern stacks (OpenSSL >= 1.0.2, current Go, current Java, current browsers) refuse a sub-768-bit DH group and silently fall back to ECDHE, so the truncated group never produces a usable session. Permissive clients — embedded clients without minimum-size enforcement, legacy stacks, custom telemetry collectors — accept it.

Scope — exploitation requires all of the following:

The server is built with the mbedtls TLS backend ( ME_COM_MBEDTLS=1 ME_COM_OPENSSL=0 ). Builds linked against OpenSSL are not affected.

The TLS handshake is TLS 1.2 (or earlier) and uses a DHE_RSA cipher suite ( TLS_DHE_RSA_* ). TLS 1.3 and ECDHE handshakes are not affected.

The client accepts a 1-byte (8-bit) DH group . OpenSSL >= 1.0.2, current Go, current Java, and current mainstream browsers refuse it; permissive embedded or legacy clients accept it.

The attacker can observe the handshake on the network . Passive on-path or on-link recording is sufficient; no man-in-the-middle position, no credentials, no prior authentication.

The default GoAhead build links against OpenSSL, where this defect is not reachable. The mbedtls backend is opt-in at build time. Within an mbedtls-built deployment, the truncated parameters reach the wire on every TLS 1.2 DHE-RSA handshake — but the handshake only completes if the client accepts the truncated group, which most modern clients do not.

The defect is a one-token error in the call site. The buffers are correct; only the third argument (the byte length of P ) is wrong:

sizeof(dhm_g) evaluates to 1, so mbedtls is told that dhm_p is a 1-byte buffer. mbedtls duly copies the first byte (0xFF) into its internal representation of P and discards the remaining 255 bytes. mbedtls does not validate the bit-length of the configured P against any minimum — that is the caller's responsibility — and accepts the 1-byte value.

On every subsequent TLS 1.2 DHE-RSA handshake, the server constructs the ServerKeyExchange server params from the configured group:

Because P=255 is small enough to brute-force (the server exponent can be recovered in at most 254 iterations) and is not even prime (255 = 3 x 5 x 17), the discrete-log problem the protocol relies on is computationally trivial. An attacker who captured the handshake recovers the server exponent, computes the premaster secret, derives the master secret and AES-256-GCM session keys via the TLS 1.2 PRF, and decrypts the captured request and response.

OpenSSL's s_client rejects the truncated group with DH_check_params_ex: modulus too small and refuses to complete the handshake; current Java, Go, and mainstream browsers behave similarly. Permissive clients — embedded clients without minimum-size enforcement, custom-built TLS 1.2 stacks, deliberately weak test clients — accept the group and complete the handshake, producing a session that a passive observer can decrypt.

TLS 1.3 and ECDHE cipher suites do not consult the configured finite-field group, so handshakes that complete via TLS 1.3 or any TLS_ECDHE_* suite are unaffected by this defect.

The verify call is corrected to pass sizeof(dhm_p) for the prime length. The 2048-bit RFC 3526 group is then installed verbatim and transmitted on the wire as P_len=256, P= . The invariant restored is "the configured DH group is transmitted verbatim on the wire". No public API change, no new configuration option, no new build flag.

Applied to src/mbedtls.c in sslOpen() (commit 4421c113 ):

Reference documentation:

Reported by Sébastien Féry.

Extracted Entities

Platforms (1)

Tools (1)