CVE-2025-59530
Description
quic-go is an implementation of the QUIC protocol in Go. In versions prior to 0.49.0, 0.54.1, and 0.55.0, a misbehaving or malicious server can cause a denial-of-service (DoS) attack on the quic-go client by triggering an assertion failure, leading to a process crash. This requires no authentication and can be exploited during the handshake phase. This was observed in the wild with certain server implementations. quic-go needs to be able to handle misbehaving server implementations, including those that prematurely send a HANDSHAKE_DONE frame. Versions 0.49.0, 0.54.1, and 0.55.0 discard Initial keys when receiving a HANDSHAKE_DONE frame, thereby correctly handling premature HANDSHAKE_DONE frames.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
github.com/quic-go/quic-goGo | < 0.49.1 | 0.49.1 |
github.com/quic-go/quic-goGo | >= 0.50.0, < 0.54.1 | 0.54.1 |
Affected products
1Patches
4275c172fec2bdrop initial packets when the handshake is confirmed
2 files changed · +5 −1
connection.go+3 −0 modified@@ -789,6 +789,9 @@ func (s *connection) handleHandshakeComplete(now time.Time) error { } func (s *connection) handleHandshakeConfirmed(now time.Time) error { + if err := s.dropEncryptionLevel(protocol.EncryptionInitial, now); err != nil { + return err + } if err := s.dropEncryptionLevel(protocol.EncryptionHandshake, now); err != nil { return err }
connection_test.go+2 −1 modified@@ -1102,7 +1102,7 @@ func TestConnectionHandshakeServer(t *testing.T) { data, err := (&wire.CryptoFrame{Data: []byte("foobar")}).Append(nil, protocol.Version1) require.NoError(t, err) - cs.EXPECT().DiscardInitialKeys() + cs.EXPECT().DiscardInitialKeys().Times(2) tc.connRunner.EXPECT().Retire(gomock.Any()) gomock.InOrder( cs.EXPECT().StartHandshake(gomock.Any()), @@ -1254,6 +1254,7 @@ func testConnectionHandshakeClient(t *testing.T, usePreferredAddress bool) { unpacker.EXPECT().UnpackLongHeader(gomock.Any(), gomock.Any()).Return( &unpackedPacket{hdr: hdr, encryptionLevel: protocol.Encryption1RTT, data: data}, nil, ), + cs.EXPECT().DiscardInitialKeys(), cs.EXPECT().SetHandshakeConfirmed(), tc.packer.EXPECT().AppendPacket(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn( func(buf *packetBuffer, _ protocol.ByteCount, _ time.Time, _ protocol.Version) (shortHeaderPacket, error) {
0264fbc02e94drop initial packets when the handshake is confirmed
2 files changed · +5 −1
connection.go+3 −0 modified@@ -845,6 +845,9 @@ func (c *Conn) handleHandshakeComplete(now time.Time) error { } func (c *Conn) handleHandshakeConfirmed(now time.Time) error { + if err := c.dropEncryptionLevel(protocol.EncryptionInitial, now); err != nil { + return err + } if err := c.dropEncryptionLevel(protocol.EncryptionHandshake, now); err != nil { return err }
connection_test.go+2 −1 modified@@ -1065,7 +1065,7 @@ func TestConnectionHandshakeServer(t *testing.T) { data, err := (&wire.CryptoFrame{Data: []byte("foobar")}).Append(nil, protocol.Version1) require.NoError(t, err) - cs.EXPECT().DiscardInitialKeys() + cs.EXPECT().DiscardInitialKeys().Times(2) gomock.InOrder( cs.EXPECT().StartHandshake(gomock.Any()), cs.EXPECT().NextEvent().Return(handshake.Event{Kind: handshake.EventNoEvent}), @@ -1216,6 +1216,7 @@ func testConnectionHandshakeClient(t *testing.T, usePreferredAddress bool) { unpacker.EXPECT().UnpackLongHeader(gomock.Any(), gomock.Any()).Return( &unpackedPacket{hdr: hdr, encryptionLevel: protocol.Encryption1RTT, data: data}, nil, ), + cs.EXPECT().DiscardInitialKeys(), cs.EXPECT().SetHandshakeConfirmed(), tc.packer.EXPECT().AppendPacket(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn( func(buf *packetBuffer, _ protocol.ByteCount, _ time.Time, _ protocol.Version) (shortHeaderPacket, error) {
ce7c9ea8834bdrop initial keys when the handshake is confirmed (#5354)
2 files changed · +9 −1
connection.go+7 −0 modified@@ -949,6 +949,13 @@ func (c *Conn) handleHandshakeComplete(now monotime.Time) error { } func (c *Conn) handleHandshakeConfirmed(now monotime.Time) error { + // Drop initial keys. + // On the client side, this should have happened when sending the first Handshake packet, + // but this is not guaranteed if the server misbehaves. + // See CVE-2025-59530 for more details. + if err := c.dropEncryptionLevel(protocol.EncryptionInitial, now); err != nil { + return err + } if err := c.dropEncryptionLevel(protocol.EncryptionHandshake, now); err != nil { return err }
connection_test.go+2 −1 modified@@ -1084,7 +1084,7 @@ func TestConnectionHandshakeServer(t *testing.T) { data, err := (&wire.CryptoFrame{Data: []byte("foobar")}).Append(nil, protocol.Version1) require.NoError(t, err) - cs.EXPECT().DiscardInitialKeys() + cs.EXPECT().DiscardInitialKeys().Times(2) gomock.InOrder( cs.EXPECT().StartHandshake(gomock.Any()), cs.EXPECT().NextEvent().Return(handshake.Event{Kind: handshake.EventNoEvent}), @@ -1235,6 +1235,7 @@ func testConnectionHandshakeClient(t *testing.T, usePreferredAddress bool) { unpacker.EXPECT().UnpackLongHeader(gomock.Any(), gomock.Any()).Return( &unpackedPacket{hdr: hdr, encryptionLevel: protocol.Encryption1RTT, data: data}, nil, ), + cs.EXPECT().DiscardInitialKeys(), cs.EXPECT().SetHandshakeConfirmed(), tc.packer.EXPECT().AppendPacket(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn( func(buf *packetBuffer, _ protocol.ByteCount, _ monotime.Time, _ protocol.Version) (shortHeaderPacket, error) {
bc5bccf10fd0drop initial packets when the handshake is confirmed
2 files changed · +9 −1
connection.go+7 −0 modified@@ -949,6 +949,13 @@ func (c *Conn) handleHandshakeComplete(now monotime.Time) error { } func (c *Conn) handleHandshakeConfirmed(now monotime.Time) error { + // Drop initial keys. + // On the client side, this should have happened when sending the first Handshake packet, + // but this is not guaranteed if the server misbehaves. + // See CVE-2025-59530 for more details. + if err := c.dropEncryptionLevel(protocol.EncryptionInitial, now); err != nil { + return err + } if err := c.dropEncryptionLevel(protocol.EncryptionHandshake, now); err != nil { return err }
connection_test.go+2 −1 modified@@ -1084,7 +1084,7 @@ func TestConnectionHandshakeServer(t *testing.T) { data, err := (&wire.CryptoFrame{Data: []byte("foobar")}).Append(nil, protocol.Version1) require.NoError(t, err) - cs.EXPECT().DiscardInitialKeys() + cs.EXPECT().DiscardInitialKeys().Times(2) gomock.InOrder( cs.EXPECT().StartHandshake(gomock.Any()), cs.EXPECT().NextEvent().Return(handshake.Event{Kind: handshake.EventNoEvent}), @@ -1235,6 +1235,7 @@ func testConnectionHandshakeClient(t *testing.T, usePreferredAddress bool) { unpacker.EXPECT().UnpackLongHeader(gomock.Any(), gomock.Any()).Return( &unpackedPacket{hdr: hdr, encryptionLevel: protocol.Encryption1RTT, data: data}, nil, ), + cs.EXPECT().DiscardInitialKeys(), cs.EXPECT().SetHandshakeConfirmed(), tc.packer.EXPECT().AppendPacket(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn( func(buf *packetBuffer, _ protocol.ByteCount, _ monotime.Time, _ protocol.Version) (shortHeaderPacket, error) {
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
8- github.com/advisories/GHSA-47m2-4cr7-mhcwghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2025-59530ghsaADVISORY
- github.com/quic-go/quic-go/blob/v0.55.0/connection.gonvdWEB
- github.com/quic-go/quic-go/commit/bc5bccf10fd02728eef150683eb4dfaa5c0e749cghsaWEB
- github.com/quic-go/quic-go/commit/ce7c9ea8834b9d2ed79efa9269467f02c0895d42ghsaWEB
- github.com/quic-go/quic-go/pull/5354nvdWEB
- github.com/quic-go/quic-go/security/advisories/GHSA-47m2-4cr7-mhcwnvdWEB
- pkg.go.dev/vuln/GO-2025-4017ghsaWEB
News mentions
0No linked articles in our index yet.