krb5 commit: Fix null deref in KDC when decoding invalid NDR

ghudson at mit.edu ghudson at mit.edu
Tue Nov 15 11:30:06 EST 2022


https://github.com/krb5/krb5/commit/fa62bd33a0c0889c083999c0289ffa81a5d51e7b
commit fa62bd33a0c0889c083999c0289ffa81a5d51e7b
Author: Greg Hudson <ghudson at mit.edu>
Date:   Wed Oct 12 00:46:52 2022 -0400

    Fix null deref in KDC when decoding invalid NDR
    
    In ndr_dec_delegation_info(), keep di->transited_services_length valid
    by incrementing it as we add entries.  Otherwise
    ndr_free_delegation_info() could dereference a null
    di->transited_services field.  Also bound nservices using data->length
    to prevent inordinately large memory allocations.  Credit to OSS-Fuzz
    for discovering the issues.
    
    ticket: 9073 (new)
    tags: pullup
    target_version: 1.20-next

 src/kdc/ndr.c   | 14 +++++++-------
 src/kdc/t_ndr.c | 32 +++++++++++++++++++++++++++++---
 2 files changed, 36 insertions(+), 10 deletions(-)

diff --git a/src/kdc/ndr.c b/src/kdc/ndr.c
index 2d16e6a5b..48395abe5 100644
--- a/src/kdc/ndr.c
+++ b/src/kdc/ndr.c
@@ -142,7 +142,7 @@ ndr_dec_delegation_info(krb5_data *data, struct pac_s4u_delegation_info **out)
     krb5_error_code ret;
     struct pac_s4u_delegation_info *di = NULL;
     struct k5input in;
-    uint32_t i, object_buffer_length;
+    uint32_t i, object_buffer_length, nservices;
     uint8_t version, endianness, common_header_length;
 
     *out = NULL;
@@ -193,26 +193,26 @@ ndr_dec_delegation_info(krb5_data *data, struct pac_s4u_delegation_info **out)
     ret = dec_wchar_pointer(&in, &di->proxy_target);
     if (ret)
         goto error;
-    di->transited_services_length = k5_input_get_uint32_le(&in);
+    nservices = k5_input_get_uint32_le(&in);
 
     /* Here, we have encoded 2 bytes of length, 2 bytes of (length + 2), and 4
      * bytes of pointer, for each element (deferred pointers). */
-    if (di->transited_services_length > UINT32_MAX / 8) {
+    if (nservices > data->length / 8) {
         ret = ERANGE;
         goto error;
     }
-    (void)k5_input_get_bytes(&in, 8 * di->transited_services_length);
+    (void)k5_input_get_bytes(&in, 8 * nservices);
 
     /* Since we're likely to add another entry, leave a blank at the end. */
-    di->transited_services = k5calloc(di->transited_services_length + 1,
-                                      sizeof(char *), &ret);
+    di->transited_services = k5calloc(nservices + 1, sizeof(char *), &ret);
     if (di->transited_services == NULL)
         goto error;
 
-    for (i = 0; i < di->transited_services_length; i++) {
+    for (i = 0; i < nservices; i++) {
         ret = dec_wchar_pointer(&in, &di->transited_services[i]);
         if (ret)
             goto error;
+        di->transited_services_length++;
     }
 
     ret = in.status;
diff --git a/src/kdc/t_ndr.c b/src/kdc/t_ndr.c
index 6f1173f47..378066044 100644
--- a/src/kdc/t_ndr.c
+++ b/src/kdc/t_ndr.c
@@ -107,8 +107,24 @@ static uint8_t s4u_di_double[] = {
     0x45, 0x00, 0x53, 0x00, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 };
 
+static uint8_t fuzz1[] = {
+    0x01, 0x10, 0x08, 0x20, 0x20, 0x20, 0x20, 0x20, 0x24, 0x00, 0x00, 0x00,
+    0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+    0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xff, 0xff, 0xff,
+    0xff, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00,
+    0x20, 0x20, 0x20, 0x20
+};
+
+static uint8_t fuzz2[] = {
+    0x01, 0x10, 0x08, 0x00, 0x00, 0xff, 0xff, 0xff, 0x24, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x1e, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x16, 0x00, 0x00,
+    0x1e, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x1e
+};
+
 static void
-test_dec_enc(uint8_t *blob, size_t len, char *name)
+test_dec_enc(uint8_t *blob, size_t len, char *name, int fail)
 {
     krb5_data data_in, data_out;
     struct pac_s4u_delegation_info *di = NULL;
@@ -120,7 +136,14 @@ test_dec_enc(uint8_t *blob, size_t len, char *name)
 
     data_in = make_data(blob, len);
     ret = ndr_dec_delegation_info(&data_in, &di);
-    if (ret) {
+    if (fail) {
+        if (!ret) {
+            printf("%s: unexpected decode success\n", name);
+            exit(1);
+        }
+        printf("%s: failed as expected\n", name);
+        return;
+    } else if (ret) {
         printf("%s: bad decode (%d): %s\n", name, ret, strerror(ret));
         exit(1);
     }
@@ -146,7 +169,8 @@ test_dec_enc(uint8_t *blob, size_t len, char *name)
     printf("%s matched\n\n", name);
 }
 
-#define RUN_TEST(blob) test_dec_enc(blob, sizeof(blob), #blob)
+#define RUN_TEST(blob) test_dec_enc(blob, sizeof(blob), #blob, 0)
+#define RUN_TEST_FAIL(blob) test_dec_enc(blob, sizeof(blob), #blob, 1)
 
 int
 main()
@@ -156,6 +180,8 @@ main()
     RUN_TEST(s4u_di_short);
     RUN_TEST(s4u_di_long);
     RUN_TEST(s4u_di_double);
+    RUN_TEST_FAIL(fuzz1);
+    RUN_TEST_FAIL(fuzz2);
 
     printf("Passed NDR tests\n");
 }


More information about the cvs-krb5 mailing list