Overview

The Orca Security Research Pod discovered a use-after-free race condition in the Linux kernel’s ksmbd SMB3 server. When two connections share a session over SMB3 multichannel, the kernel can read a freed channel struct – exposing the per-channel AES-128-CMAC signing key and causing a kernel panic. An attacker needs valid SMB credentials and network access to port 445. No exotic prerequisites. The fix is merged (commit e4a8a96a93d) – update your kernel.

AttributeDetails
CVECVE-2026-23226
Researcher CVSS7.5 High – AV:N/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:H
NVD CVSS7.8 High – AV:L/AC:H (under dispute – see scoring note)
CWECWE-416 (Use After Free), CWE-362 (Race Condition)
Affected ComponentLinux kernel ksmbd SMB3 server – ksmbd_chann_list XArray
Bug Introducedcommit 1d9c4172110e (Dec 2023) – channel list converted to XArray without synchronization
Confirmed AffectedLinux kernels containing commit `1d9c4172110e` without `e4a8a96a93d` (approx. 5.15.145+, 6.1.x select builds, 6.7 through 6.19.0)
Attack VectorNetwork – SMB3 port 445
AuthenticationLow – valid SMB user credentials required
Exploit ComplexityHigh – race condition, ~1 hit per 750 attempts
Active ExploitationNone confirmed
PoC AvailableYes – developed and confirmed by Orca Security Research Pod
FixMerged – commit e4a8a96a93d (stable), upstream 4f3a06cc579
Discovered ByOrca Security Research Pod
Patch AuthorNamjae Jeon, ksmbd maintainer
CoordinationGreg KH, Steve French, kernel security team

What Is ksmbd – and Why Does It Matter?

Most Linux administrators know Samba – the long-standing userspace daemon that lets Linux servers share files with Windows clients over SMB. ksmbd is its younger, kernel-native counterpart. Merged into mainline Linux in kernel 5.15 (November 2021), it implements the SMB2/3 protocol entirely inside the kernel for better performance – no context switches to userspace for every request.

That architectural choice has a direct security consequence: a vulnerability in ksmbd is a kernel vulnerability. It doesn’t compromise a process running as root. It compromises the kernel itself – full machine takeover territory.

ksmbd has accumulated a notable list of serious bugs since its merge. The pattern is consistent: complex concurrent state management introduced without adequate locking. Before CVE-2026-23226, notable examples include CVE-2022-47939 (unauthenticated out-of-bounds write leading to RCE), CVE-2023-32254 (TOCTOU race in session setup), CVE-2023-32257 (use-after-free), and, directly related to this finding, CVE-2025-40039. That last one is worth understanding, because CVE-2026-23226 is essentially the same mistake made twice.

CVE-2025-40039 involved the rpc_handle_list XArray inside struct ksmbd_session – another sparse array used to track shared mutable state across threads. It had no lock. The fix added rpc_lock (a read-write semaphore). That fix was applied in 2025. But ksmbd_session has three XArrays. Only one was fixed. The ksmbd_chann_list XArray – used to track multichannel connections – was left unprotected. CVE-2026-23226 is that oversight.

Understanding SMB3 Multichannel

To understand the vulnerability, you need to understand what multichannel is and why it creates concurrent access to the channel list.

  • SMB3 Multichannel – introduced in Windows Server 2012 / SMB dialect 3.0. Allows a single authenticated SMB session to use multiple TCP connections simultaneously, spreading I/O across multiple network interfaces for throughput. From the server’s perspective, a single ksmbd_session object must track all active TCP connections as “channels,” stored in the ksmbd_chann_list XArray.

The setup requires two distinct phases. First, the client authenticates over a primary TCP connection using full NTLM authentication – this is standard SMB3. Then, for each additional connection (channel), the client sends a special SESSION_SETUP request with a flag set: SESSION_FLAG_BINDING = 0x01. This binding request references the already-authenticated session ID and must be signed with the session’s AES-128-CMAC key.

When ksmbd sees that binding flag, it takes a completely different code path – the binding_session label in smb2_sess_setup(). This is the path where the race lives.

The binding path has a strict set of requirements ksmbd validates before proceeding:

  • Dialect must match the primary connection
  • Request must be signed (SMB2_FLAGS_SIGNED)
  • ClientGUID must match the primary session
  • Session must be in SMB2_SESSION_VALID state

All of these are checked correctly. The bug isn’t in the validation. It’s in what happens after, when ksmbd looks up the channel list to register the new connection.

The Vulnerability – A Missing Lock

The Broken Data Structure

struct ksmbd_session tracks three types of shared mutable state, each in its own XArray. Here’s the problem in plain sight:

Two out of three XArrays got locks. The channel list was left bare.

  • XArray – a kernel sparse array structure (Linux 4.20+) that maps integer keys to pointers. ksmbd uses ksmbd_chann_list to map TCP connection pointers to struct channel objects. The key operations are xa_load() (read an entry) and xa_erase() (atomically remove and return an entry). These two operations are not safe to call simultaneously from different threads without external locking, the XArray’s internal lock only protects modifications against other modifications, not a reader against a concurrent erase.

The Vulnerable Code

The read path – called from the channel binding handler, performs a bare xa_load() with no lock held:

The delete path – triggered during session logoff, calls xa_erase() then immediately kfree(), also with no lock:

Note the use of kfree() rather than kfree_rcu(). RCU (Read-Copy-Update) is the standard kernel mechanism for safely freeing objects that concurrent readers might still hold. Not using it means the memory is freed immediately, with no grace period for in-flight readers to finish. Combined with no lock on the read side, this is a textbook use-after-free setup.

What Gets Freed and Read

struct channel is a 24-byte object (kmalloc-32 slab):

Three call sites in smb2pdu.c dereference the returned channel pointer to read the signing key, without any synchronization:

The signing key is the AES-128-CMAC key used to verify every SMB3 packet on this channel. Reading it from freed memory means either reading garbage (signature verification will fail with corrupted data) or reading the original key if the allocator hasn’t reused the slot yet (signing key exposure).

Why the race window is wide

The critical enabling factor is ksmbd_conn_wait_idle(). During connection teardown, ksmbd calls this function to drain in-flight work before freeing the connection’s resources. But it only drains the work queue of the specific connection being torn down – not all connections that share the same session.

This means: when conn2 tears down and calls ksmbd_chann_del() to free conn1’s channel struct, conn1’s worker threads may still be processing signed requests that call lookup_chann_list(). There is no session-level barrier. The teardown of one connection races freely against the ongoing work of another connection in the same session.

On a 4-core machine with ksmbd’s concurrent ksmbd-io worker thread pool, this window is wide enough to hit reliably – our PoC fires 8–11 times per 500-iteration run with the first hit typically within 60–120 seconds.

Impact

What an attacker achieves

The race produces three possible outcomes, depending on timing and allocator state:

Signing key leak. If the kmalloc-32 slab slot hasn’t been overwritten by the time lookup_chann_list() reads it, the UAF returns the original 16-byte smb3signingkey. This is the per-channel AES-128-CMAC key used to sign all SMB3 traffic on that channel. An attacker who recovers this key can forge signatures, impersonate the server, or bypass signature verification entirely for the duration of the session.

Signature bypass via garbage key. Even if the slot has been partially overwritten, ksmbd will use whatever garbage bytes it reads as the signing key. Server-side signature verification will silently accept or reject packets based on corrupted state – undermining the integrity guarantees that SMB3 signing is supposed to provide.

Kernel panic (DoS). The struct channel contains chann->conn at offset 16 – a pointer to the connection object. If the freed slot gets reallocated and partially overwritten, dereferencing this pointer causes a kernel panic. This is the most reliably observable outcome and what our PoC consistently triggers on KASAN builds.

What this is not

The current PoC does not achieve kernel code execution. It proves the UAF reliably and triggers a kernel crash. Converting a race-based UAF to full RCE requires additional work: confirming a write UAF path, heap grooming in kmalloc-32, building a KASLR bypass from the information leak, and constructing a full exploit chain. That is a realistic multi-month research effort – and a separate project. We’re disclosing at UAF confirmation because that’s the honest and correct point to disclose. The vulnerability is real, the impact is significant, and the fix is available.

Who is Affected

EnvironmentRisk
Linux kernel since Dec 2023 + ksmbd + multichannel enabledAffected
Linux kernel before commit 1d9c4172110e (Dec 2023)Not affected – bug not yet introduced
ksmbd running but multichannel not configuredSignificantly reduced – binding code path unreachable
Samba (smbd, not ksmbd)Not affected – separate implementation
No SMB server runningNot affected

CVSS scoring note

Our researcher assessed score is 7.5 High with Attack Vector: Network (AV:N). The attack requires sending SMB3 packets to port 445 from a remote machine – no local access. NVD currently scores it 7.8 High with AV:L (Local), which we believe is incorrect. Notably, correcting the vector to AV:N actually lowers the score from 7.8 to 7.5 – this is a technical accuracy dispute, not an attempt to inflate severity. A formal CVSS dispute has been filed referencing this post.

The Fix

Namjae Jeon (ksmbd maintainer) sent the patch the morning after our disclosure. We reviewed it, confirmed it covered all five access points, and it was committed by Greg KH on February 16, 2026.

  • Stable tree commit: e4a8a96a93d08570e0405cfd989a8a07e5b6ff33
  • Upstream commit: 4f3a06cc57976cafa8c6f716646be6c79a99e485
  • Reported-by: Igor Stepansky, Orca Security (credited in kernel git)
  • Submitted for stable backport (Cc: stable@vger.kernel.org)

The fix adds one field to struct ksmbd_session:

And protects all five access points. The key subtlety in ksmbd_chann_del() – the write lock wraps only the xa_erase(), then releases before kfree(). This is intentional: the lock guards the XArray from concurrent access. Once the pointer is removed from the array and no other holder exists, the free needs no lock.

free_channel_list() and both xa_store() call sites in the session binding path are also wrapped with down_write/up_write in the patch.

Proof of Concept

Background: why this PoC was hard to build

When we initially disclosed to the kernel security team, we noted that no KASAN-triggering PoC existed. The problem: triggering the race requires a custom SMB3 client that implements channel binding with SESSION_FLAG_BINDING = 0x01, correct AES-128-CMAC signing, and matching dialect negotiation. No existing tool – smbclient, Impacket, or otherwise – does this out of the box.

Building the PoC took five iterations:

  • v1 – Used Impacket’s high-level login() twice. Never reached the binding path because Impacket always sends Flags = 0. No UAF.
  • v2 – Sent raw SMB2 packets with SESSION_FLAG_BINDING = 0x01. Got STATUS_INVALID_PARAMETER. Root cause: secondary connection negotiated SMB 3.0 while primary used SMB 3.1.1 – ksmbd checks dialect match and rejects.
  • v3 – Matched the dialect. Still STATUS_INVALID_PARAMETER. Root cause: Capabilities field packed as 2 bytes (<H) instead of 4 (<I), corrupting the entire packet body.
  • v4 – Fixed packet structure. Now getting STATUS_USER_SESSION_DELETED. Root cause: signed with HMAC-SHA256 instead of AES-128-CMAC. SMB 3.0 uses AES-128-CMAC for signing – wrong algorithm means signature verification fails.
  • v5 – Instead of reimplementing SMB3 signing from scratch, patched Impacket directly. One line change. Impacket handles everything – NTLM auth, correct dialect, correct signing – we just set the right flag.

The final insight: don’t fight the protocol stack, patch the one missing feature.

Required: Impacket Patch

Setting smb3._binding = True on a connection object causes its next SESSION_SETUP to send SESSION_FLAG_BINDING = 0x01. All signing, NTLM, and dialect handling stay in Impacket’s existing implementation – unchanged.

PoC Code

Lab environment

The PoC was confirmed on Linux 6.12.0 in a KASAN-enabled QEMU/KVM VM on bare metal Ubuntu 24.04:

  • CONFIG_KASAN_GENERIC=y – makes the UAF visible as a clean crash report
  • CONFIG_RANDOMIZE_BASE=n – KASLR disabled for lab convenience only, doesn’t affect UAF triggering
  • kasan_multi_shot=1 boot parameter – KASAN keeps reporting after the first hit rather than going silent, essential for measuring hit rate
  • 4 vCPUs – genuine SMP parallelism, the race requires concurrent kernel threads on separate cores
  • KASAN (Kernel Address Sanitizer) – a kernel debug tool that tracks memory validity using shadow memory. For every 8 bytes of real kernel memory, 1 byte of shadow memory records whether those bytes are safe to access. When freed memory is touched, KASAN catches it immediately and prints full allocation and free stack traces. KASAN is only used in debug kernels – production kernels don’t have it, which means the same race causes a silent crash or kernel panic instead of a clean report.

KASAN output – confirmed

The PoC runs on the attacker machine (right) and sends SMB3 packets over the network to the VM running ksmbd (left). Within ~2 minutes, the race fires and KASAN reports the UAF in real time

Split-screen: VM terminal (left) showing BUG: KASAN: slab-use-after-free in smb2_sess_setup+0x4300/0x5f80 appearing. Host terminal (right) showing [*] Round 100/500. Both visible simultaneously – network attack on the right, kernel crash on the left.

The KASAN splat from our run:

Reading the splat: Thread 38687 allocated the object during session login. Thread 28565 freed it during logoff. The same thread 28565 then attempts to read it in the binding path – KASAN catches the access to the freed kmalloc slab.

The race fires across all four CPUs in our VM:

Hits on CPUs 1, 2, and 3 independently – this is a genuine SMP race across ksmbd’s worker thread pool, not a single-threaded timing artifact.

A note on the KASAN object size: Our KASAN output shows a kmalloc-64 object allocated by ksmbd_alloc_user. The original vulnerability report describes the UAF on the 24-byte struct channel (kmalloc-32) via smb3_check_sign_req(). Both paths involve the same missing lock on ksmbd_chann_list – our PoC happened to trigger a related object free in the same logoff path. The struct channel / smb3signingkey path described in the original report is the most security-sensitive impact (signing key exposure), the kmalloc-64 hit confirms the broader locking gap.

Remediation

The fix is available now.

Update to a kernel containing commit e4a8a96a93d08570e0405cfd989a8a07e5b6ff33. The patch has been officially released – check your distribution’s kernel security advisories for when it lands in your specific branch.

DeploymentAction
ksmbd + multichannel enabledDisable multichannel if not required: remove ksmbd: smb3 multichannel = yes from smb.conf, restart ksmbd
ksmbd + multichannel not configuredLower risk – the binding code path is unreachable. Verify with grep -r multichannel /etc/ksmbd/
Samba (smbd, not ksmbd)Not affected
No SMB server runningNot affected

Check if you’re running ksmbd:

Interim mitigations if multichannel cannot be disabled:

  • Firewall port 445 to known trusted client IP ranges – reduces the attacker pool to your internal network
  • Minimize valid SMB accounts – every valid credential is a potential attacker credential for this vulnerability
  • Monitor for abnormal SESSION_SETUP request volume (see Detection)

Detection

On a production kernel (no KASAN), the UAF doesn’t produce clean output. Production kernels will show a generic kernel oops rather than a UAF report. Watch dmesg and journalctl -k for:

These are suspicious when appearing in the context of kworker threads and ksmbd stack frames.

Network-level: The PoC sends dozens of SESSION_FLAG_BINDING requests per minute against the same session ID. Legitimate multichannel usage binds a handful of channels per session and holds them – it does not rapidly cycle bind/logoff. Detect with:

The entire vulnerability in two functions. lookup_chann_list() is two lines with no lock. ksmbd_chann_del() frees the same pointer concurrently. Nothing coordinates them.

Frequently Asked Questions

Does this affect Samba?

No. Samba (smbd) is a completely separate userspace SMB implementation and is not affected. This vulnerability is specific to ksmbd – the kernel-native server. You can tell which you’re running with ps aux | grep ksmbd.mountd (ksmbd) vs ps aux | grep smbd (Samba).

Is multichannel enabled by default

No. It requires explicitly adding ksmbd: smb3 multichannel = yes to /etc/ksmbd/smb.conf. Most ksmbd deployments don’t have this configured, which means the binding code path – and therefore this vulnerability – is unreachable for them.

The fix is merged – am I safe?

Once your kernel includes commit e4a8a96a93d, yes. The patch is in mainline and submitted for stable backport. Check your distribution’s kernel changelog. If you can’t update yet, disabling multichannel in smb.conf removes the attack vector entirely.

Can an unauthenticated attacker trigger this?

No. Valid SMB credentials are required. SESSION_FLAG_BINDING is only accepted after a primary session has been fully authenticated – the kernel checks sess->state == SMB2_SESSION_VALID before entering the binding path. This limits the attacker pool to users who already have legitimate SMB access.

Why does your CVSS score (7.5) differ from NVD’s (7.8)?

NVD scores this with AV:L (Local attack vector). We assess AV:N (Network) – the attack is triggered entirely by sending SMB3 packets to port 445. No local access is required. With AV:N, the correct score is 7.5. Notably, correcting NVD’s vector lowers the score rather than raising it – this is a technical accuracy dispute. A formal dispute has been filed.

Timeline

DateEvent
February 8, 2026Disclosed to kernel security team by Orca Security Research Pod
February 9, 2026Patch authored by Namjae Jeon, reviewed and confirmed by Orca Security
February 9, 2026Greg KH waived embargo – no working PoC at time of disclosure, fix pushed immediately
February 16, 2026Patch committed to mainline Linux by Greg KH – commit e4a8a96a93d
February 18, 2026CVE-2026-23226 published
March 2026PoC developed – required custom multichannel binding client; 5 iterations
March 16, 2026KASAN confirmation – 8 reliable hits in single run
March 17, 202611 additional hits confirmed, demo video recorded
March 2026This blog post and PoC published

Credits

  • Discovered and reported by: Orca Security Research Pod (Reported-by in kernel commit e4a8a96a93d)
  • Patch authored by: Namjae Jeon (linkinjeon@kernel.org), ksmbd maintainer
  • Signed off by: Steve French (stfrench@microsoft.com), ksmbd co-maintainer
  • Committed by: Greg Kroah-Hartman (gregkh@linuxfoundation.org), February 16, 2026
  • Prior related fix: CVE-2025-40039 (same bug class, rpc_handle_list XArray) – the chann_lock pattern in this fix mirrors that one directly

How Can Orca Help?

CVE-2026-23226 presents a triage challenge that traditional security tools struggle with: exploitability depends on three conditions aligning simultaneously, and verifying them across a large cloud environment without logging into every asset is not feasible. Orca’s agentless SideScanning architecture was built exactly for this scenario.

In a single query, Orca scans your entire cloud environment and surfaces every Linux asset where ksmbd is present and port 445 is internet-exposed – no agents, no credentials, no access to the machines themselves. What would take a security team days of manual work across hundreds of assets takes Orca seconds:

Orca Security query to identify vulnerable non-Windows VMs with public-facing SMB port 445 and ksmbd service activity.

The output is a precise, prioritized list of assets that combine internet exposure on port 445 with ksmbd presence – the two strongest remote indicators that all three exploitability conditions could be met. 

The only step that goes beyond what Orca can express in a single query is the exact kernel version range (5.15.145 – 6.19.0), since kernel versions are stored as free-form strings with distro-specific suffixes. A simple post-filter script on the exported results closes that gap in minutes.

From there, Orca’s internet accessibility scoring and lateral movement graph tell you exactly how much each flagged asset matters – which ones are directly reachable from the internet, what sensitive data or downstream systems they connect to, and where to focus remediation effort first. That combination of agentless discovery, precise signal, and business context is what separates a list of potentially vulnerable machines from an actionable remediation plan.