CVE-2019-3564 (published 2019-05-06T16:29Z)

Go Facebook Thrift servers would not error upon receiving messages with containers of fields of unknown type. As a result, malicious clients could send short messages which would take a long time for the server to parse, potentially leading to denial of service. This issue affects Facebook Thrift prior to v2019.03.04.00.

Severity: MEDIUM
CVSS v2 base score: 5.0 (AV:N/AC:L/Au:N/C:N/I:N/A:P)
CVSS v3 base score: 7.5, HIGH (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H)

Weakness information:
cwe_id: CWE-755
cwe_name: Improper Handling of Exceptional Conditions
description: The product does not handle or incorrectly handles an exceptional condition.
extended_description: 
            
         
         

Fix information:
repo_url: https://github.com/facebook/fbthrift
commit_details: repo_url: https://github.com/facebook/fbthrift
author: Nate Mueller
author_date: 2019-02-25 08:36:25-08:00
msg: Properly error when asked to skip an unknown field type

Summary:
We weren't returning an error when asked to skip over a field with and
unknown type.  In this particular test case the code attempts to skip
over a map with a large number of fields of unknown type and the ~3B
noop calls take almost 30s.  A misbehaving client could DoS a server
by sending short messages that take a long time to parse.  There may
have been other failure modes as well.

The test covers the binary protocol because that is where the issue
was found.  However, the issue is common to all protocols.

This fixes CVE-2019-3552.

Reviewed By: spalamarchuk

Differential Revision: D14088980

fbshipit-source-id: 8a9d63260db717347217a8d2ac883c4ce331d964
repository: repo_url: https://github.com/facebook/fbthrift
repo_name: facebook/fbthrift
description: Facebook's branch of Apache Thrift, including a new C++ server.
repo_language: C++
owner: facebook
file_changes: filename: binary_protocol_test.go
change_type: ModificationType.MODIFY
diff: @@ -21,9 +21,31 @@
 package thrift
 
 import (
+	"strings"
 	"testing"
+	"time"
 )
 
 func TestReadWriteBinaryProtocol(t *testing.T) {
 	ReadWriteProtocolTest(t, NewBinaryProtocolFactoryDefault())
 }
+
+func TestSkipUnknownTypeBinaryProtocol(t *testing.T) {
+	var m MyTestStruct
+	d := NewDeserializer()
+	f := NewBinaryProtocolFactoryDefault()
+	d.Protocol = f.GetProtocol(d.Transport)
+	// skip over a map with invalid key/value type and 1.7B entries
+	data := []byte("\n\x10\rO\t6\x03\n\n\n\x10\r\n\tslice\x00")
+	start := time.Now()
+	err := d.Read(&m, data)
+	if err == nil {
+		t.Fatalf("Parsed invalid message correctly")
+	} else if !strings.Contains(err.Error(), "unknown type") {
+		t.Fatalf("Failed for reason besides unknown type")
+	}
+
+	if time.Now().Sub(start).Seconds() > 5 {
+		t.Fatalf("It should not take seconds to parse a small message")
+	}
+}

programming_language: Go
filename: protocol.go
change_type: ModificationType.MODIFY
diff: @@ -22,6 +22,7 @@ package thrift
 
 import (
 	"errors"
+	"fmt"
 )
 
 type ProtocolID int16
@@ -127,8 +128,6 @@ func Skip(self Protocol, fieldType Type, maxDepth int) (err error) {
 	}
 
 	switch fieldType {
-	case STOP:
-		return
 	case BOOL:
 		_, err = self.ReadBool()
 		return
@@ -206,6 +205,7 @@ func Skip(self Protocol, fieldType Type, maxDepth int) (err error) {
 			}
 		}
 		return self.ReadListEnd()
+	default:
+		return fmt.Errorf("unable to skip over unknown type id %d", fieldType)
 	}
-	return nil
 }

programming_language: Go