CVE-2022-42898: Integer Overflow or Wraparound (CWE-190)
value: PAC parsing in MIT Kerberos 5 (aka krb5) before 1.19.4 and 1.20.x before 1.20.1 has integer overflows that may lead to remote code execution (in KDC, kadmind, or a GSS or Kerberos application server) on 32-bit platforms (which have a resultant heap-based buffer overflow), and cause a denial of service on other platforms. This occurs in krb5_pac_parse in lib/krb5/krb/pac.c. Heimdal before 7.7.1 has "a similar bug."
CWE description: The product performs a calculation that can
         produce an integer overflow or wraparound when the logic
         assumes that the resulting value will always be larger than
         the original value. This occurs when an integer value is
         incremented to a value that is too large to store in the
         associated representation. When this occurs, the value may
         become a very small or negative number.
CVSS v3 base score: 8.8
Published: 2022-12-25T06:15Z
Repository: https://github.com/krb5/krb5
Language: C
Files changed: src/lib/krb5/krb/pac.c, src/lib/krb5/krb/t_pac.c
Commit date: 2022-11-03 01:00:45 -0400
Commit message: Fix integer overflows in PAC parsing

In krb5_parse_pac(), check for buffer counts large enough to threaten
integer overflow in the header length and memory length calculations.
Avoid potential integer overflows when checking the length of each
buffer.  Credit to OSS-Fuzz for discovering one of the issues.

CVE-2022-42898:

In MIT krb5 releases 1.8 and later, an authenticated attacker may be
able to cause a KDC or kadmind process to crash by reading beyond the
bounds of allocated memory, creating a denial of service.  A
privileged attacker may similarly be able to cause a Kerberos or GSS
application service to crash.  On 32-bit platforms, an attacker can
also cause insufficient memory to be allocated for the result,
potentially leading to remote code execution in a KDC, kadmind, or GSS
or Kerberos application server process.  An attacker with the
privileges of a cross-realm KDC may be able to extract secrets from a
KDC process's memory by having them copied into the PAC of a new
ticket.

ticket: 9074 (new)
tags: pullup
target_version: 1.20-next
target_version: 1.19-next

Diff: diff --git a/src/lib/krb5/krb/pac.c b/src/lib/krb5/krb/pac.c
index 2f1df8d42b27..f6c4373de0f8 100644
--- a/src/lib/krb5/krb/pac.c
+++ b/src/lib/krb5/krb/pac.c
@@ -26,10 +26,12 @@
 
 #include "k5-int.h"
 #include "int-proto.h"
 #include "authdata.h"
 
+#define MAX_BUFFERS 4096
+
 /* draft-brezak-win2k-krb-authz-00 */
 
 /*
  * Add a buffer to the provided PAC and update header.
  */
@@ -315,10 +317,13 @@ krb5_pac_parse(krb5_context context,
     p += 4;
 
     if (version != 0)
         return EINVAL;
 
+    if (cbuffers < 1 || cbuffers > MAX_BUFFERS)
+        return ERANGE;
+
     header_len = PACTYPE_LENGTH + (cbuffers * PAC_INFO_BUFFER_LENGTH);
     if (len < header_len)
         return ERANGE;
 
     ret = krb5_pac_init(context, &pac);
@@ -347,12 +352,12 @@ krb5_pac_parse(krb5_context context,
 
         if (buffer->Offset % PAC_ALIGNMENT) {
             krb5_pac_free(context, pac);
             return EINVAL;
         }
-        if (buffer->Offset < header_len ||
-            buffer->Offset + buffer->cbBufferSize > len) {
+        if (buffer->Offset < header_len || buffer->Offset > len ||
+            buffer->cbBufferSize > len - buffer->Offset) {
             krb5_pac_free(context, pac);
             return ERANGE;
         }
     }
 
diff --git a/src/lib/krb5/krb/t_pac.c b/src/lib/krb5/krb/t_pac.c
index 0b1b1f05648b..173bde7bab84 100644
--- a/src/lib/krb5/krb/t_pac.c
+++ b/src/lib/krb5/krb/t_pac.c
@@ -429,10 +429,20 @@ static const unsigned char s4u_pac_ent_xrealm[] = {
     0x76, 0xff, 0xff, 0xff, 0x95, 0x40, 0x76, 0xe4,
     0x0a, 0x0a, 0xb9, 0xe7, 0x93, 0x0f, 0x05, 0xf8,
     0x8a, 0x81, 0x9c, 0x9c, 0x00, 0x00, 0x00, 0x00
 };
 
+static const unsigned char fuzz1[] = {
+    0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00,
+    0x06, 0xff, 0xff, 0xff, 0x00, 0x00, 0xf5
+};
+
+static const unsigned char fuzz2[] = {
+    0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00,
+    0x20, 0x20
+};
+
 static const char *s4u_principal = "w2k8u@ACME.COM";
 static const char *s4u_enterprise = "w2k8u@abc@ACME.COM";
 
 static const krb5_keyblock s4u_srv_key = {
     0, ENCTYPE_AES256_CTS_HMAC_SHA1_96,
@@ -826,10 +836,18 @@ main(int argc, char **argv)
 
         krb5_free_principal(context, sp);
         krb5_free_principal(context, sep);
     }
 
+    /* Check problematic PACs found by fuzzing. */
+    ret = krb5_pac_parse(context, fuzz1, sizeof(fuzz1), &pac);
+    if (!ret)
+        err(context, ret, "krb5_pac_parse should have failed");
+    ret = krb5_pac_parse(context, fuzz2, sizeof(fuzz2), &pac);
+    if (!ret)
+        err(context, ret, "krb5_pac_parse should have failed");
+
     /*
      * Test empty free
      */
 
     ret = krb5_pac_init(context, &pac);

Vulnerable code:         if (buffer->Offset < header_len ||
            buffer->Offset + buffer->cbBufferSize > len) {
Fixed code: #define MAX_BUFFERS 4096

    if (cbuffers < 1 || cbuffers > MAX_BUFFERS)
        return ERANGE;

        if (buffer->Offset < header_len || buffer->Offset > len ||
            buffer->cbBufferSize > len - buffer->Offset) {
static const unsigned char fuzz1[] = {
    0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00,
    0x06, 0xff, 0xff, 0xff, 0x00, 0x00, 0xf5
};

static const unsigned char fuzz2[] = {
    0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00,
    0x20, 0x20
};

    /* Check problematic PACs found by fuzzing. */
    ret = krb5_pac_parse(context, fuzz1, sizeof(fuzz1), &pac);
    if (!ret)
        err(context, ret, "krb5_pac_parse should have failed");
    ret = krb5_pac_parse(context, fuzz2, sizeof(fuzz2), &pac);
    if (!ret)
        err(context, ret, "krb5_pac_parse should have failed");