A memory corruption bug hiding in OpenBSD's dusty SysV semaphore attic — you need a shell first, then you have to race the kernel
The bug lives in sys/kern/sysv_sem.c in the sys_semget() path, where a context switch after tsleep() leaves a freed semaphore descriptor reachable and reusable. An attacker with a local user account crafts a concurrent workload that reallocates the freed slab into an attacker-controlled structure, then triggers the dangling reference to corrupt kernel memory and escalate to root. Affected: OpenBSD through 7.9, with the fix landing in the tree at commit 1957873d20. Any host still on 7.9 or earlier release/stable branches — including -current snapshots before the commit — is exposed.
The vendor's HIGH / 7.4 rating with AV:L/AC:H/PR:N/UI:N/C:H/I:H/A:H is honest: local-only, high complexity (race window), no privileges required beyond a shell, full compromise on success. That said, on the fleet-realistic OpenBSD population — which skews heavily toward firewalls, bastion hosts, and appliances that *don't hand out shells* — this bug is a MEDIUM by exposure. The vector is accurate; the urgency depends entirely on whether your OpenBSD boxes are multi-user Unix or single-purpose network appliances.
4 steps from start to impact.
Obtain local code execution on the OpenBSD host
pledge/unveil), or an insider. No network reach to the box is sufficient by itself.- Local user account or arbitrary code execution as any UID
- Ability to invoke
semget(2)— nopledgerestrictions blockingsysvsem
- Most production OpenBSD hosts are single-purpose (pf firewall, relayd, httpd, spamd) with no interactive users
- Bastion/jump hosts are typically the only multi-user OpenBSD role
Prime the SysV semaphore subsystem
semget(2) repeatedly to allocate semaphore identifiers, then arranges a workload that will cause sys_semget() to sleep via tsleep() — typically by contending on the semaphore table lock or semid_ds allocation. This sets up the racy window where a freed descriptor can be observed after context switch.kern.seminfolimits allow allocation- Ability to spawn multiple threads/processes to force contention
- Default OpenBSD
kern.seminfo.semmniandsemmnslimits are conservative — enumeration is trivial but not silent under audit - Some hardened installs disable SysV IPC entirely
kern.seminfo telemetry and syscall audit (if enabled) will show anomalous semget volumeRace the free-and-reallocate window
tsleep() boundary.- Reliable kernel heap primitive for reallocation
- Knowledge of OpenBSD kernel slab layout for the target release
- Multi-core host — single-CPU VMs make the race harder
- AC:H is real — the race window is narrow and non-deterministic
- OpenBSD kernel ASLR (KARL re-links per boot) rerandomizes symbol layout, defeating hardcoded gadget offsets
W^X, SMEP-equivalents, andmimmutable(2)reduce viable payload shapes
savecore outputCorrupt kernel state and pivot to root
struct ucred), or a syscall dispatch structure to hand themselves UID 0. Standard technique: locate the current process's credentials via the corrupted primitive, patch cr_uid = 0, return from the syscall as root.- Arbitrary kernel write primitive derived from the UAF
- No further mitigation blocking credential overwrite
- OpenBSD does not implement CFI on kernel indirect calls the way Linux does, but KARL still complicates target selection
- No published exploitation guide for this specific class on OpenBSD 7.9
accton enabled), unexpected root-owned processes in psThe supporting signals.
| In-the-wild exploitation | None observed. Not on CISA KEV. No campaigns tied to CVE-2026-57589 as of 2026-07-09. |
|---|---|
| Public PoC | No public exploit code published. Bug details are in commit 1957873d20 and NVD; weaponization would require independent kernel-race development. |
| EPSS | 0.00125 (≈0.1%) — bottom quartile probability of exploitation in the next 30 days. |
| KEV status | Not listed. |
| CVSS 3.1 vector | AV:L/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H — local, high complexity, no privs, full CIA impact. Vendor 7.4 HIGH. |
| Affected versions | OpenBSD ≤ 7.9 (including 7.9-stable and -current pre-fix). Older branches (7.8, 7.7, 7.6…) also carry the vulnerable code path. |
| Fixed version | OpenBSD tree commit 1957873d20; syspatch for 7.9 and 7.8 stable branches. -current snapshots after the commit date. |
| Internet exposure | Not remotely reachable — no Shodan/Censys/GreyNoise footprint applies. Attack surface = user population on OpenBSD hosts. |
| Disclosure | 2026-06-25, credited via NVD assignment; upstream commit landed shortly after. |
| CWE | CWE-416 Use-After-Free — context-switch race after tsleep() in sys_semget(). |
noisgate verdict.
Downgraded from vendor HIGH to MEDIUM because the decisive factor is local-only attack vector on a platform whose dominant deployment role is single-purpose network appliance with no interactive users — the exposed population that can even reach semget() is a small minority of OpenBSD hosts. High attack complexity (kernel race) and zero observed in-the-wild activity compound the downgrade; the vendor score is technically correct but blind to OpenBSD's deployment reality.
Why this verdict
- Local-only vector (AV:L): attacker must already be running code on the box. On OpenBSD's dominant footprint — pf firewalls, relayd/httpd appliances, spamd hosts — no unprivileged users exist. Adjustment: −1.0.
- High attack complexity (AC:H): narrow race window across
tsleep()context switch, requires reliable heap-reallocation primitive, KARL rerandomizes kernel layout per boot. Adjustment: −0.5. - No public exploit + EPSS 0.00125: no researcher has published a working weaponization, and market-signal probability of exploitation is bottom-quartile. Adjustment: −0.2.
- Role multiplier — bastion / jump host (typical high-value OpenBSD role): where OpenBSD IS multi-user, it's often the SSH jump box or admin bastion. Local root here yields credential harvesting for the entire operator population. Chain succeeds → outcome: credential-plane pivot. This is why the floor is MEDIUM, not LOW.
- Role multiplier — pf firewall / edge appliance (dominant OpenBSD role): no local users, no shell access, chain never fires. Represents the majority of installed base, which is why the floor is not HIGH.
- Role multiplier — developer workstation / lab: chain succeeds; blast radius is one host. Neutral.
- Floor check: OpenBSD-as-bastion is a plausible high-value role but does NOT dominate the installed base (>10% share is not credible relative to appliance role), so the HIGH floor does not clamp. MEDIUM stands.
Why not higher?
HIGH would require either an unauthenticated network vector, a public PoC lowering the complexity barrier, or evidence that most OpenBSD hosts run in a role with unprivileged user shells. None of those hold: it is local, no PoC exists, and the modal OpenBSD box is an appliance without local users.
Why not lower?
LOW would ignore the bastion-host role and the reality that a local root on an SSH jump host is a credential-plane compromise, not a single-host incident. It would also disregard that the fix is real, the code path is reachable by any local user by default, and a determined attacker with kernel expertise can weaponize this within days once someone publishes a race harness.
What to do — in priority order.
- Inventory OpenBSD hosts by role and shell-access population — Enumerate every OpenBSD ≤7.9 host and tag as (a) appliance-no-users, (b) bastion/jump, (c) developer/multi-user. Only (b) and (c) need urgent attention. Complete within the 30-day mitigation window applicable to bastion hosts specifically (see bottom line); appliance hosts fall to the MEDIUM 365-day remediation SLA.
- Restrict SysV semaphore access via
pledgeand login classes on bastions — For bastion hosts that MUST remain online before patching, wrap operator shells in login classes that omitsysvsemfrompledgewhere practical, and usesysctl kern.seminfo.semmni=0on hosts that don't need SysV IPC. Deploy within 30 days on bastions. - Apply syspatch to 7.9-stable and 7.8-stable branches — Run
syspatchon all affected hosts to pull the semget UAF fix. Bastions and multi-user hosts: within 30 days (noisgate mitigation SLA for HIGH-role hosts). Appliances: within the 365-day MEDIUM remediation window, aligned with next scheduled maintenance. - Enable process accounting and
savecoreon bastions — Turn onacctonso unexpected UID transitions are logged, and confirmsavecoreis configured so crash dumps from failed exploit attempts are captured for IR review. - Rotate credentials that pass through OpenBSD bastions — Treat bastion patching as a credential-hygiene event: rotate SSH host keys, revoke long-lived operator sessions, and reissue any secrets that traversed the host.
- Network segmentation / WAF / IPS: the bug is local (
AV:L) — no network signature will fire because no packets are involved. - Disabling network services (httpd, relayd, pf): doesn't affect
sys_semget()reachability; any local user can still invoke the syscall. - EDR on Linux/Windows: OpenBSD has no first-party EDR ecosystem; expecting your Falcon/Defender fleet to catch this is a category error.
- MFA on SSH: doesn't stop a legitimate operator or an attacker with valid creds from triggering the syscall — MFA gates login, not post-login syscall behavior.
Crowdsourced verification payload.
Run this on each OpenBSD host as any user (root not required for the version check; root recommended for the syspatch check). Invoke as ./check-cve-2026-57589.sh. Outputs VULNERABLE, PATCHED, or UNKNOWN and exits with 0/1/2 respectively.
#!/bin/sh
# check-cve-2026-57589.sh
# Determines whether the current OpenBSD host has the sysv_sem UAF fix.
# Exit codes: 0 = PATCHED, 1 = VULNERABLE, 2 = UNKNOWN
set -u
OS=$(uname -s)
if [ "$OS" != "OpenBSD" ]; then
echo "UNKNOWN: not an OpenBSD host (uname -s = $OS)"
exit 2
fi
REL=$(uname -r) # e.g. 7.9
KERN=$(uname -v) # e.g. GENERIC.MP#123
# Any release <= 7.9 is in scope unless syspatch applied.
case "$REL" in
7.9|7.8|7.7|7.6|7.5|7.4|7.3|7.2|7.1|7.0|6.*|5.*)
IN_SCOPE=1
;;
*)
IN_SCOPE=0
;;
esac
if [ "$IN_SCOPE" -eq 0 ]; then
echo "PATCHED: OpenBSD $REL is newer than affected range (>7.9)"
exit 0
fi
# Check for the syspatch entry addressing sysv_sem / semget UAF.
# syspatch -l lists applied patches; the fix will appear as e.g. 79-XXX_semget.
if command -v syspatch >/dev/null 2>&1; then
if syspatch -l 2>/dev/null | grep -Eiq 'sem(get)?|sysv_sem'; then
echo "PATCHED: OpenBSD $REL with sysv_sem syspatch applied ($KERN)"
exit 0
fi
fi
# Check for -current snapshot post-fix (heuristic: build date after 2026-06-25).
BUILD_EPOCH=$(sysctl -n kern.osrevision 2>/dev/null || echo 0)
CUTOFF=202606250000 # YYYYMMDDHHMM style cutoff — adjust if your fleet uses snapshots
if [ "$BUILD_EPOCH" -gt "$CUTOFF" ] 2>/dev/null; then
echo "PATCHED: kernel osrevision $BUILD_EPOCH is after fix cutoff"
exit 0
fi
echo "VULNERABLE: OpenBSD $REL ($KERN) — no sysv_sem syspatch found"
exit 1
If you remember one thing.
syspatch (or rebuild from post-fix snapshot) within 30 days, matching the noisgate mitigation SLA for HIGH; rotate SSH host keys and operator credentials as part of the same maintenance window. Appliance-role OpenBSD hosts (pf firewalls, relayd/httpd, spamd) fall under the noisgate remediation SLA for MEDIUM — no mitigation SLA required, and the vendor patch can land in the 365-day window at the next scheduled maintenance. If CISA adds this to KEV or a public race harness ships, escalate everything to immediate-hours patching regardless of role.Sources
What defenders are saying.
Crowdsourced verification outputs.
Results submitted by users who ran the verification payload against their environment.