Calif exploits a 37-year-old OpenBSD kernel bug to get local root

Calif exploits a 37-year-old OpenBSD kernel bug to get local root

Calif, a security firm, has published a technical write-up describing a local privilege escalation to root on OpenBSD. The chain starts with a shared memory attachment counter defined as a signed 16-bit short, a definition Calif traces back roughly 37 years to the original BSD sources. Repeatedly forking a process that holds shared memory attachment records can push that counter past its maximum value of 32,767, wrapping it to a negative value. This integer overflow can lead to a use-after-free via a race condition, and combined with a separate out-of-bounds write elsewhere in the kernel, it allows local privilege escalation to root. Calif says it reported the vulnerabilities to OpenBSD earlier this year, often with suggested patches; the two issues were fixed in OpenBSD errata 026 and 018.

The exploit works even with KARL, SMAP and SMEP all enabled, and without relying on any information disclosure bug. The published steps: map 127 SysV shared memory segments, unmap them, then fork 259 processes to wrap the shared memory attach count to -32,643. Heap grooming follows, using sysctl(KERN_MALLOCSTATS) fields such as kb_total and kb_totalfree to track allocations and time new page allocations. The race is then won by calling IPC_RMID in one process while a helper process attaches a new, size-confused shared memory segment. Next, IP socket allocations are aligned and IP_MULTICAST_TTL is set to a non-default value of 2, which forces allocation of a persistent 16-byte struct ip_moptions whose final word packs two 16-bit counters, the number of memberships and the maximum memberships. A conditional out-of-bounds write of -1 overwrites both fields. Closing the socket then triggers atomic decrements while traversing the group members, which modify a self-referential page table entry (PTE) mapping twice, flipping it from user to supervisor accessible and achieving kernel code execution by overwriting a predictable kernel pointer. The exploit finishes by cleaning up the shared memory accounting and restoring the overwritten pointer, yielding a root shell.

Calif frames the result as a write-what-where primitive: the write comes from a TAILQ_REMOVE operation, the "what" is the attacker's own userland memory mapping made supervisor-accessible to bypass SMAP/SMEP, and the "where" is a predictable kernel pointer location. The predictability exists because, while OpenBSD's KARL randomizes kernel function and data addresses, the kernel's own memory allocators, both the boot-time allocator and the best-fit allocator, lack base address randomization, so early boot allocations land at the same locations across boots on the same hardware. The exploit specifically targets the lo0 and enc0 network interface ioctl handlers, overwriting one to gain control flow and using the other to restore the overwritten pointer afterward.

Beyond the errata fixes already applied, Calif proposes three further mitigations. First, safe-unlinking, which checks doubly linked list invariants before an unlink write and calls abort() or panic() if they are violated; FreeBSD and NetBSD already implement this in their sys/queue.h. Second, randomizing the self-referential PTE map's allocation address at boot, for example by picking a PML4 slot via 320 + (random_byte & 127), which would shift PTE_BASE across a range from 0xffffa00000000000 through 0xffffdf8000000000 in 512-GiB steps; if that proves unsuitable, alternatives like marking the PTE read-only and toggling CR0.WP during modification might work instead. Third, making the kernel allocator itself non-deterministic, by randomizing its base address at boot (which Calif says is effectively free) and randomizing allocation order at run time (which does add some overhead); either approach alone would be enough to break the predictable pointer location. Calif also points to compiler-level mitigations: it has urged FreeBSD to enable the array bounds sanitizer in release builds, having privately reported 21 issues to FreeBSD that the sanitizer would have caught, some with local privilege escalation impact, and it flags an upcoming feature called Alloc Tokens that would isolate pointer-containing heap allocations from non-pointer ones, making overflow and use-after-free exploitation harder. The post closes by noting that in the era of AI-assisted vulnerability discovery, eliminating whole classes of bugs and raising attack complexity matters more than ever.

Key facts

  • A shared memory attachment counter, a signed 16-bit short dating back roughly 37 years to original BSD source code, could be overflowed past 32,767 to wrap negative, by forking 259 processes after mapping and unmapping 127 SysV shared memory segments.
  • The overflow enabled a use-after-free via a race condition which, combined with a separate kernel out-of-bounds write, gave local root on OpenBSD even with KARL, SMAP and SMEP enabled and without any information disclosure bug.
  • The exploit abuses a crafted 16-byte struct ip_moptions (triggered via a non-default IP_MULTICAST_TTL of 2) to corrupt membership counters, then closes the socket to trigger atomic decrements that flip a self-referential PTE mapping from user to supervisor accessible, overwriting a predictable kernel pointer via the lo0 and enc0 ioctl handlers.
  • Both issues were fixed in OpenBSD errata 026 and 018 after Calif reported them, with suggested patches, earlier this year.
  • Calif proposes safe-unlinking, randomizing the self-referential PTE map's boot-time address, and making the kernel allocator's base address and allocation order non-deterministic, plus compiler mitigations like the array bounds sanitizer (already the subject of 21 issues Calif reported to FreeBSD) and the upcoming Alloc Tokens feature.

Why it matters

The bug sat in BSD code for roughly 37 years before being found and chained into a working root exploit, and the exploit succeeds against OpenBSD's existing hardening, KARL, SMAP and SMEP, without needing an information leak. That shows how a single-field integer overflow in ancient code can still defeat modern mitigations when kernel memory allocators themselves remain predictable.

Who it affects

OpenBSD users and administrators are the direct audience, since the flaw allowed any local user to escalate to root. The write-up is also aimed at other BSD projects: Calif names FreeBSD and NetBSD specifically, noting they already carry a safe-unlinking defense in sys/queue.h, and separately says it reported 21 related issues to FreeBSD caught by the array bounds sanitizer.

How to use it

There is no product or paid offering here; this is a published security research report. OpenBSD administrators should ensure they have applied errata 026 and 018, which already fix the two chained issues described in the post.

How solid is it

The account comes directly from Calif, the firm that found, reported and exploited the bug, and it names the precise OpenBSD errata that fixed both underlying issues, giving a concrete, checkable outcome rather than an unverified claim.

Risks and caveats

The post gives no CVE numbers and only says the vulnerabilities were reported 'earlier this year,' without exact report or fix dates. It does not say whether OpenBSD has adopted any of the three proposed mitigations beyond the errata fixes, nor does it quote any response from OpenBSD developers. No exploit code is published, and the post's closing remark about AI-assisted vulnerability discovery is a general observation, not a claim that AI was used to find this specific bug.

“We managed to get local root in OpenBSD with KARL / SMAP / SMEP enabled without the use of an information disclosure vulnerability”

— Calif