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)
PoC verification
Evidence pack
findings/poc/DF-2566 Β· 9 files| File | Type | Description | Size | |
|---|---|---|---|---|
| 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 |
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) = 4096DataOffset (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, originalsmbfs.ko): PoC βmd_get_mem(474): incomplete copyβpanic: overflowed mbufinm_free/smb_t2_done. Guest down. - Fixed
smbfs.ko(only thisfix.diff, rebuilt module): same PoC β the malicious TRANS2 is rejected (count > lenβEBADRPC), client retries,smb_maperror: Unmapped error 2:0, thelsfails 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
(
makeinsys/vfs/smbfs) + install + re-run β no full kernel rebuild needed.
Fix verification
fixedVALIDATED. 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.
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.
No comments yet.