β¬’ DragonFlyBSD Kernel Audit
← triage Β· dashboard
DF-2566

smb_t2_placedata corrupts mbuf m_len on oversized TRANS2 count causing kernel heap OOB read and panic

Summary

smb_t2_placedata does m->m_len-=len-count with no check count<=len. Both count and offset are u_int16 values from attacker-controlled SMB TRANS2 response. When malicious server reports DataCount/ParameterCount larger than actual payload subtraction wraps last mbuf m_len to huge value corrupted chain read by md_get_mem which trusts m_len leaking kernel heap to userspace and panicking on unmapped page. smb_t2_reply only verifies displacement ordering never validates doff+dcount<=packet_size. smb_iod_recvall validates 4-byte signature and pulls up 32-byte header but no content bounds. Attacker is malicious/MitM SMB server reached moment client mounts smbfs share and performs any TRANS2 (QPathInfo FIND_FIRST2). Impact kernel heap info leak KASLR defeat near-certain panic DoS.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-2566 Β· 9 files
FileTypeDescriptionSize
smb_evil.c trigger-source malicious SMB1 server: NBSSN+NegProt+SessSetup+TreeConnect then TRANS2 with oversized DataCount 10.0 KB view raw
build.sh build-script cc -o smb_evil smb_evil.c 169 B view raw
run.sh run-script start server + mount_smbfs + ls to trigger TRANS2 1008 B view raw
run.log run-log baseline run: panic signature 257 B view raw
panic.txt panic-signature panic: overflowed mbuf in m_free/smb_t2_done 624 B view raw
fix_run.log run-log fixed-module run: TRANS2 rejected, no panic 1.3 KB view raw
env.txt environment uname / kern.version / smbfs.ko loaded 254 B view raw
fix.diff suggested-fix bound count<=len in smb_t2_placedata 863 B view raw
VERDICT.md verdict full analysis 5.2 KB ↓ raw
VERDICT.md verdict full analysis
↓ download raw

DF-2566 β€” smb_t2_placedata mbuf m_len corruption on oversized TRANS2 count β†’ kernel heap OOB / panic

Verdict

REPRODUCED (kernel panic from a malicious SMB server). Fix VALIDATED (fixed smbfs.ko rejects the oversized count, no panic).

Mechanism (trigger β†’ primitive β†’ effect)

The DragonFly SMB client (netproto/smb, built into the loadable smbfs.ko) parses SMB1 TRANS2 responses in smb_t2_reply(). DataCount/DataOffset (dcount/doff) and ParameterCount/ParameterOffset (pcount/poff) are u_int16 values taken verbatim from the (attacker- or MITM-controlled) server response. The only ordering check (smb_rq.c:504) validates ddisp, never that dcount fits the actual response bytes.

smb_t2_placedata() then does, with no bounds check on count (sys/netproto/smb/smb_rq.c:436):

m0 = m_split(mtop, offset, M_WAITOK);          /* split the response at 'offset' */
for(len = 0, m = m0; m->m_next; m = m->m_next)
    len += m->m_len;
len += m->m_len;                                /* len = bytes available from offset */
m->m_len -= len - count;                        /* BUG: if count > len, m_len WRAPS UP */

When a malicious server reports dcount (or pcount) larger than the bytes actually present from offset, len - count is negative, so m->m_len grows by count - len β€” the last mbuf's length field is corrupted far beyond its real data. The corrupted chain is then concatenated into the reply mdchain and consumed by md_get_mem(), which trusts m_len and reads past the mbuf into adjacent kernel heap (info leak). On an INVARIANTS kernel the overflow is caught on free β†’ panic: overflowed mbuf.

Reproduction (live, on the running kernel)

smb_evil.c is a minimal C malicious SMB1 server (no python needed on the guest). It completes the NetBIOS session + NegProt (NT LM 0.12, share-level, no-encrypt) + SessionSetupAndX + TreeConnectAndX handshake so mount_smbfs -N -I 127.0.0.1 //guest@127.0.0.1/share /mnt/s connects, then on the first SMB_COM_TRANSACTION2 (0x32, triggered by ls /mnt/s β†’ FIND_FIRST2) it returns a TRANS2 response with:

  • TotalDataCount = DataCount (dcount) = 4096
  • DataOffset (doff) = 55 (8 bytes of real payload follow)
  • actual response payload = 8 bytes

So len = 8, count = 4096, and m->m_len -= 8 - 4096 inflates m_len by 4088.

Observed crash (unpatched #0 kernel + original smbfs.ko)

netsmb_dev: loaded
md_get_mem(474): incomplete copy
panic: overflowed mbuf 0xfffff801184ac200
Trace:
  m_free+0x351 -> m_free+0x351 -> m_freem+0x15 -> md_done+0x1c ->
  smb_t2_done+0x36 -> smbfs_findclose+0x86 -> Debugger("panic")

The md_get_mem(474): incomplete copy proves the corrupted chain was read past the mbuf (the OOB read happened); the overflowed mbuf panic is INVARIANTS catching the corruption on free. On a non-INVARIANTS kernel the read past the mbuf is a silent kernel-heap info leak returned to the readdir caller.

Impact ceiling

Malicious/compromised SMB server (or network MITM) β†’ corrupts a connecting client kernel's mbuf chain on any TRANS2 (mount/list/stat). Effects: - Kernel heap OOB read / info leak (silent on non-INVARIANTS) β€” the directory-listing data returned to userspace contains adjacent kernel heap bytes (KASLR-defeat / disclosure). - Reliable DoS / panic on the default GENERIC (INVARIANTS) kernel.

Trigger precondition is realistic: any user (or auto-mount facility) that mounts/lists an attacker-controlled SMB share. This is a CLIENT bug reached via network; it is not a local unprivileged→root escalation, so the Phase-6 uid0 chain does not apply — the realistic ceiling is heap OOB-read/leak + DoS.

Exploit chain

Client-side memory corruption reached by a malicious server. Not a local privesc; Phase 6 (uid0) N/A. The primitive (arbitrary m_len inflation via controlled count) is characterized above; on non-INVARIANTS it yields a silent heap leak via the md_get_mem over-read.

PoC changes

Authored smb_evil.c (malicious SMB1 server), build.sh, run.sh. No prior scaffolding existed. Server uses share-level/no-encrypt negotiate so no password/NTLM is needed (-N).

Fix (fix.diff)

In smb_t2_placedata(), after computing len (bytes available from offset), reject count > len with EBADRPC (and free the split chain) before the corrupting subtraction. This bounds the attacker-controlled count to the actual response bytes. Mirrors the defensive style already used for the m_split failure. Supersedes any pre-verification proposal (verified line-accurate).

Fix validation

  • Baseline (#0, original smbfs.ko): PoC β†’ md_get_mem(474): incomplete copy β†’ panic: overflowed mbuf in m_free/smb_t2_done. Guest down.
  • Fixed smbfs.ko (only this fix.diff, rebuilt module): same PoC β†’ the malicious TRANS2 is rejected (count > len β†’ EBADRPC), client retries, smb_maperror: Unmapped error 2:0, the ls fails with an error, no panic, guest stays up. fix_status = fixed.
  • The fix is in a loadable module, so validation was a single-module rebuild (make in sys/vfs/smbfs) + install + re-run β€” no full kernel rebuild needed.

Fix verification

fixed
baseline reproduced→ patch + rebuild →patched clean

VALIDATED. Malicious TRANS2 panics on original smbfs.ko ('panic: overflowed mbuf' in m_free/smb_t2_done). On rebuilt smbfs.ko (only this fix.diff) same PoC hits added count>len guard: malicious TRANS2 rejected (EBADRPC), client retries, 'smb_maperror: Unmapped error 2:0', ls fails with error, no panic, guest stays up. Single-module rebuild (make in sys/vfs/smbfs) + install + re-run β€” no kernel rebuild needed.

baseline: md_get_mem(474): incomplete copy / panic: overflowed mbuf 0xfffff801184ac200 (smbfs.ko d85477...) -> guest down. fixed: smb_maperror: Unmapped error 2:0 (smbfs.ko 6ab3d8...), TRANS2 rejected count>len, ls fails, guest UP, no panic.
↓ fix.diffDragonFly 6.5-DEVELOPMENT #0 (kernel unchanged) + rebuilt loadable smbfs.ko containing only DF-2566 fix.diff (sha256 6ab3d857a0ffabb24ae4e414c333b582472f62e4c6315ca87e5c6bf0a2a61e63 vs original d854773e5b3642544c89d4adebe20efb5e54258aaffcfea61694006d1cd54895)

Confirmed kernel references

Detail

Exploit chain

none (not local unprivileged->root class). CLIENT-side memory corruption reached by malicious/compromised SMB server (or network MITM) when any user/auto-mount mounts and lists attacker-controlled share. Realistic ceiling: kernel-heap OOB read / info leak (silent on non-INVARIANTS: corrupted m_len makes md_get_mem read adjacent kernel heap into readdir data returned to userspace -> KASLR-defeat/disclosure) plus reliable DoS/panic on default GENERIC (INVARIANTS) kernel. No uid0 chain applies. Primitive (arbitrary m_len inflation via controlled count) characterized.

Evidence (decisive lines)

netsmb_dev: loaded | md_get_mem(474): incomplete copy | panic: overflowed mbuf 0xfffff801184ac200 | Trace: m_free+0x351 -> m_free+0x351 -> m_freem+0x15 -> md_done+0x1c -> smb_t2_done+0x36 -> smbfs_findclose+0x86 -> Debugger(panic). Server log: '*** MALICIOUS TRANS2: dcount=4096 doff=55 actual=8'.

PoC changes

Authored smb_evil.c (malicious SMB1 server: NBSSN+NegProt+SessSetup+TreeConnect then TRANS2 response with oversized DataCount), build.sh, run.sh. Fixed DataOffset field patch offset (byte 47 in SMB message) and used share-level/no-encrypt negotiate so -N needs no password.

Verified recommended fix

In smb_t2_placedata() (sys/netproto/smb/smb_rq.c) after computing len (bytes available from offset), reject count > len with EBADRPC (and m_freem the split chain) before corrupting m->m_len -= len - count. Bounds attacker-controlled count to actual response bytes. Full git-apply-able diff in findings/poc/DF-2566/fix.diff.

Verdict

REPRODUCED. smb_t2_placedata (sys/netproto/smb/smb_rq.c:436) computes m->m_len -= len - count with no check that count<=len, where count is the attacker-controlled TRANS2 DataCount/ParameterCount taken verbatim from server response (only check at smb_rq.c:504 validates displacement, never count<=len). Wrote minimal malicious SMB1 server (smb_evil.c, C) that completes NBSSN session + NegProt (NT LM 0.12, share-level, no-encrypt) + SessionSetupAndX + TreeConnectAndX so mount_smbfs connects, then on first TRANS2 (cmd 0x32, triggered by ls /mnt/s -> FIND_FIRST2) returns DataCount=4096 with only 8 real payload bytes: len=8, count=4096, so m->m_len -= (8-4096) inflates m_len by 4088, corrupting chain. md_get_mem then read past mbuf ('md_get_mem(474): incomplete copy') and INVARIANTS caught overflow on free -> 'panic: overflowed mbuf' in m_free/smb_t2_done.