ssh: prevent malformed exit-status panic

A malicious server can crash any client by sending
a truncated exit-status payload, which triggers a
panic in binary.BigEndian.Uint32.

Change-Id: Iff4fe182b40210c99d46562f6c3b5ecf38bb98b4
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/780181
LUCI-TryBot-Result: golang-scoped@luci-project-accounts.iam.gserviceaccount.com <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Nicola Murino <nicola.murino@gmail.com>
Reviewed-by: Junyang Shao <shaojunyang@google.com>
Reviewed-by: David Chase <drchase@google.com>
This commit is contained in:
Neal Patel
2026-05-25 19:49:21 -07:00
parent e5306b2f3e
commit 9beb694f97
2 changed files with 33 additions and 0 deletions
+3
View File
@@ -423,6 +423,9 @@ func (s *Session) wait(reqs <-chan *Request) error {
for msg := range reqs {
switch msg.Type {
case "exit-status":
if len(msg.Payload) < 4 {
return errors.New("ssh: malformed exit-status request")
}
wm.status = int(binary.BigEndian.Uint32(msg.Payload))
case "exit-signal":
var sigval struct {
+30
View File
@@ -334,6 +334,25 @@ func TestExitWithoutStatusOrSignal(t *testing.T) {
}
}
func TestExitStatusShortPayload(t *testing.T) {
for _, payload := range [][]byte{nil, {0}, {0, 0}, {0, 0, 0}} {
conn := dial(exitStatusShortPayloadHandler(payload), t)
session, err := conn.NewSession()
if err != nil {
t.Fatalf("unable to request new session: %v", err)
}
if err := session.Shell(); err != nil {
t.Fatalf("unable to execute command: %v", err)
}
err = session.Wait()
if err == nil {
t.Fatalf("expected error for payload length %d", len(payload))
}
session.Close()
conn.Close()
}
}
// windowTestBytes is the number of bytes that we'll send to the SSH server.
const windowTestBytes = 16000 * 200
@@ -482,6 +501,17 @@ func exitWithoutSignalOrStatus(ch Channel, in <-chan *Request, t *testing.T) {
readLine(shell, t)
}
func exitStatusShortPayloadHandler(payload []byte) serverType {
return func(ch Channel, in <-chan *Request, t *testing.T) {
defer ch.Close()
shell := newServerShell(ch, in, "> ")
readLine(shell, t)
if _, err := ch.SendRequest("exit-status", false, payload); err != nil {
t.Errorf("unable to send status: %v", err)
}
}
}
func shellHandler(ch Channel, in <-chan *Request, t *testing.T) {
defer ch.Close()
// this string is returned to stdout