ssh: reject unexpected message types on established channels
ch.msg is only read while the channel open or a channel request with a reply is pending, so anything the default arm of channel.handlePacket delivered to it was never consumed. The blocking send there let a misbehaving peer fill the buffer with well-formed but unexpected message types carrying a valid channel id and stall the mux read loop, deadlocking the whole connection. No conforming peer sends such messages during the connection protocol. Treat them as a protocol error and tear the connection down, as handleUnknownChannelPacket already does for the same messages when the channel id is not in use. Fixes CVE-2026-56855 Fixes golang/go#81317 Change-Id: I87420dfe68fcb62a17df4b47dc5ffb6ccd72ba26 Reviewed-on: https://go-review.googlesource.com/c/crypto/+/826524 Reviewed-by: Roland Shoemaker <roland@golang.org> LUCI-TryBot-Result: golang-scoped@luci-project-accounts.iam.gserviceaccount.com <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Auto-Submit: Neal Patel <nealpatel@google.com> Reviewed-by: Nicholas Husin <husin@google.com>
This commit is contained in:
committed by
Gopher Robot
parent
a6cdac6084
commit
86efde54dc
+2
-1
@@ -519,7 +519,8 @@ func (ch *channel) handlePacket(packet []byte) error {
|
||||
default:
|
||||
}
|
||||
default:
|
||||
ch.msg <- msg
|
||||
// No other message type is expected on an established channel.
|
||||
return fmt.Errorf("ssh: unexpected message type %d on channel %d", packet[0], ch.localId)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -5,9 +5,11 @@
|
||||
package ssh
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -1360,6 +1362,54 @@ func TestChannelRejectedInboundDropsTraffic(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestChannelBogusMessageTearsDownMux(t *testing.T) {
|
||||
// A message that is well-formed on the wire but is not a type expected on
|
||||
// an established channel is a protocol error that tears the mux down.
|
||||
clientMux, serverMux := muxPair()
|
||||
defer serverMux.Close()
|
||||
defer clientMux.Close()
|
||||
|
||||
serverRes := make(chan *channel, 1)
|
||||
go func() {
|
||||
newCh, ok := <-serverMux.incomingChannels
|
||||
if !ok {
|
||||
close(serverRes)
|
||||
return
|
||||
}
|
||||
c, _, err := newCh.Accept()
|
||||
if err != nil {
|
||||
close(serverRes)
|
||||
return
|
||||
}
|
||||
serverRes <- c.(*channel)
|
||||
}()
|
||||
|
||||
if _, err := clientMux.openChannel("chan", nil); err != nil {
|
||||
t.Fatalf("openChannel: %v", err)
|
||||
}
|
||||
serverCh := <-serverRes
|
||||
if serverCh == nil {
|
||||
t.Fatal("server did not accept channel")
|
||||
}
|
||||
|
||||
// Craft a packet that the client mux routes to clientCh (bytes 1:5 hold the
|
||||
// peer's channel id) and that decode accepts, but whose type is unexpected
|
||||
// on a channel. The service-string length equals the channel id, so the
|
||||
// same four bytes serve both as the routing id and as the string length.
|
||||
id := serverCh.remoteId
|
||||
packet := []byte{msgServiceRequest, 0, 0, 0, 0}
|
||||
binary.BigEndian.PutUint32(packet[1:], id)
|
||||
packet = append(packet, make([]byte, id)...)
|
||||
if err := serverMux.conn.writePacket(packet); err != nil {
|
||||
t.Fatalf("writePacket: %v", err)
|
||||
}
|
||||
|
||||
// The mux loop exits with the protocol error.
|
||||
if err := clientMux.Wait(); err == nil || !strings.Contains(err.Error(), "unexpected message type") {
|
||||
t.Fatalf("mux error = %v, want unexpected message type", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestChannelCloseIdempotent(t *testing.T) {
|
||||
a, b := memPipe()
|
||||
defer a.Close()
|
||||
|
||||
Reference in New Issue
Block a user