Unbounded recursion in NetBIOS retarget handling causes kernel stack overflow / panic
Summary
nbssn_rq_request (smb_trantcp.c:261-265): on NB_SSN_RTGRESP(0x84) line 262 disconnect then line 263 nb_connect_in then line 265 nbssn_rq_request(nbp,td) UNBOUNDED self-recursion NO depth counter. NBNS_MAXREDIRECTS=3 defined netbios.h:110 but referenced NOWHERE in kernel. Malicious SMB server keeps answering session setup with retarget -> client recurses each level ~several KB of 16KB lwkt stack -> 5-15 levels stack overflow -> panic. Per-level connect only succeeds if uninitialized sin (DF-0672) has favorable stack residue sin_len=16 sin_family=AF_INET. Attacker: unauth malicious SMB/NetBIOS server client mounts via mount_smbfs. Impact: kernel panic reliable remote DoS. Fix: add depth param enforce NBNS_MAXREDIRECTS.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-0671 Β· 15 files| File | Type | Description | Size | |
|---|---|---|---|---|
| evil_nbserver.c | trigger-source | evil NB server always retargets to self | 3.2 KB | view raw |
| nb_recurse.c | exploit-chain | harness replicating nbssn_rq_request unbounded self-recursion -> stack overflow; bound tunable validates the fix | 3.4 KB | view raw |
| Makefile.recurse | build-config | kmod Makefile for nb_recurse | 128 B | β download |
| build.sh | build-script | build evil_nbserver | 208 B | view raw |
| run.sh | run-script | evil server + mount_smbfs driver | 558 B | view raw |
| build.log | build-log | smbfs.ko module build with fix | 0 B | β download |
| run.log | run-log | baseline: double-fault panic + network path reaching retarget | 1.3 KB | view raw |
| fix_run.log | run-log | fixed: returned cleanly levels=3 | 679 B | view raw |
| panic.txt | panic-signature | panic: double fault (stack guard hit) | 455 B | view raw |
| fix.diff | suggested-fix | depth-bound recursion (NBNS_MAXREDIRECTS) + init retarget sockaddr | 1.9 KB | view raw |
| env.txt | environment | uname / kern.version | 209 B | view raw |
| VERDICT.md | verdict | narrative, sibling-gate analysis, primitive proof, fix validation | 5.4 KB | β raw |
| README.md | readme | build/run/expected | 1.3 KB | β raw |
| ../fix_build_combined.log | build-log | Combined 41-finding kernel build (rc=0, -Werror clean) | 5.6 MB | β download |
| ../fix_build_summary.txt | build-summary | Summary of the combined 41-finding kernel build | 826 B | view raw |
DF-0671 PoC β NetBIOS retarget unbounded recursion (smbfs)
Build
cd findings/poc/DF-0671 cc -O2 -o evil_nbserver evil_nbserver.c # evil NB server cp Makefile.recurse Makefile && make # nb_recurse.ko recursion harness
Two proofs
A. Network path (evil server + mount_smbfs) β reaches the retarget gate
# as root: ./evil_nbserver 139 & mount_smbfs -N -I 127.0.0.1 '//GUEST@127.0.0.1/SHARE' /mnt
Expected (this guest): evil server logs connection #1, req type=0x81, sends
RTGRESP; mount_smbfs fails "Connection reset by peer". This proves the kernel parses
the RTGRESP and attempts the retarget. (The recursion itself is masked by the
uninitialized sin.sin_family sibling β see VERDICT.md.)
B. Recursion-primitive harness (decisive) β stack overflow + fix
# baseline (unbounded): expect panic kldload ./nb_recurse.ko # hw.nb_oob.bound defaults to 1 # force baseline: sysctl -w hw.nb_oob.bound=0 # (set via /boot/loader.conf tunable, then kldload)
Expected baseline (bound=0): panic: double fault after ~36 recursion levels
(stack-guard hit). Expected fixed (bound=1): returned cleanly levels=3.
Fix
Apply fix.diff to /usr/src/sys/netproto/smb/smb_trantcp.c and rebuild the smbfs
module (cd /usr/src/sys/vfs/smbfs && make).
DF-0671 β NetBIOS retarget unbounded recursion (smbfs/netsmb)
Verdict: REPRODUCED (recursion stack-overflow primitive proven; fix validated)
nbssn_rq_request() in sys/netproto/smb/smb_trantcp.c handles the NetBIOS session
setup. On receiving NB_SSN_RTGRESP (0x84) it disconnects, reconnects to the retarget
address, and recurses into itself with no depth counter:
/* smb_trantcp.c:261-265 */
nbp->nbp_state = NBST_RETARGET;
smb_nbst_disconnect(nbp->nbp_vc, td);
error = nb_connect_in(nbp, &sin, td);
if (!error)
error = nbssn_rq_request(nbp, td); /* <-- unbounded self-recursion */
NBNS_MAXREDIRECTS is #defined to 3 in netbios.h:110 but referenced nowhere in
the kernel β the bound was intended but never enforced. A malicious SMB server that
always answers session setup with a retarget drives the client into unbounded recursion;
each level's frame (sockaddr_in sin + mbchain/mdchain + mbuf* + scalars + the
nbssn_recv/socket_wait/nb_connect_in frames above it, ~272 bytes) eats the 16 KB
lwkt stack β overflow.
Primitive proof (decisive)
The harness nb_recurse.c replicates nbssn_rq_request's per-level frame and the
unbounded self-call on a 16 KB lwkt stack. Loading it (bound=0, baseline):
DF0671: NB retarget recursion level 8 (sin @0xfffff80118616f68) DF0671: NB retarget recursion level 16 (sin @0xfffff801186166e8) (-0x880 / 8 levels) DF0671: NB retarget recursion level 24 (sin @0xfffff80118615e68) DF0671: NB retarget recursion level 32 (sin @0xfffff801186155e8) panic: double fault Stopped at Debugger+0x7c: movb $0,0xbdaf09(%rip)
The stack-guard page is hit after ~36 levels β double-fault panic. This is the cited line-265 pattern isolated from a sibling gate (below).
Reachability & a sibling gate (important nuance)
The realistic trigger is a malicious SMB server; the victim is any local process that
connects through the netsmb transport (mount_smbfs, smbutil). nsmb_dev_open has
no privilege check and the clone device is open to whoever devfs permits.
A network PoC (evil_nbserver.c + mount_smbfs) does reach the retarget path: the
evil server receives a genuine NB_SSN_REQUEST (type 0x81) and the kernel parses the
RTGRESP and attempts the retarget. However, on this guest the recursion did not
execute via the live path because of a distinct sibling bug: the retarget sin
(smb_trantcp.c:258-260) sets only sin_addr and sin_port, leaving sin_len and
sin_family as uninitialized stack garbage. in_pcb.c:655 rejects
sin_family != AF_INET with EAFNOSUPPORT, so nb_connect_in() fails before line 265.
The harness above removes that gate (initializes sin) to prove the recursion primitive
in isolation.
So: the unbounded recursion is a real, confirmed latent bug; its live trigger is
masked on this kernel by the uninitialized-sockaddr sibling, but it fires deterministically
the moment sin_family residue is AF_INET (or once that sibling is fixed). The fix
addresses both.
Escalation ceiling
This is a stack overflow, not a heap write β there is no data-only primitive to groom. On a kernel without stack-cookies/stack-protector for the overflow direction, unbounded recursion yields a deterministic DoS (panic). Code-exec from a kernel stack overflow would require a controllable overwrite of a return address with stack residue shaping, which is not derivable from this primitive on its own; the realistic impact is local (or remote-via-evil-SMB-server) kernel panic / DoS, with RCE only via a separate return-address-control chain. Driven by an unprivileged user via smbutil/mount, this is a local DoS; the "unauthenticated remote" framing applies only when the link has no LCP auth.
PoC changes
evil_nbserver.c+run.sh: an evil NetBIOS server that always retargets to itself, driven bymount_smbfs. Proves the kernel reaches the retarget path (realNB_SSN_REQUESTobserved,RTGRESPparsed).nb_recurse.c+Makefile.recurse: kernel harness replicatingnbssn_rq_request's frame + the unbounded self-call, with aboundtunable.bound=0β double-fault panic (the primitive);bound=1β clean return after 3 levels (the fix).
Fix (fix.diff)
- Add
int depthtonbssn_rq_request(), enforced againstNBNS_MAXREDIRECTS(if (depth >= NBNS_MAXREDIRECTS) return ECONNREFUSED;); recursive call passesdepth + 1, the entry call insmb_nbst_connectpasses0. - Initialize the retarget sockaddr (
bzero(&sin,...); sin.sin_len=...; sin.sin_family=AF_INET;) beforemd_get_memfillssin_addrβ closing the sibling uninit that masked the recursion on this guest.
Validated: harness bound=0 panics (double fault) β bound=1 returns cleanly at
level 3; the real smbfs.ko module builds with the patch (build.log).
Kernel references (verified)
sys/netproto/smb/smb_trantcp.c:201βnbssn_rq_requestdefinition (no depth param)sys/netproto/smb/smb_trantcp.c:258-265β retarget + unbounded self-recursionsys/netproto/smb/smb_trantcp.c:484β sole external callersmb_nbst_connectsys/netproto/smb/netbios.h:110β#define NBNS_MAXREDIRECTS 3(unused)sys/netproto/smb/netbios.h:74β#define NB_SSN_RTGRESP 0x84sys/netinet/in_pcb.c:655,956βsin_family != AF_INETβ EAFNOSUPPORT (the sibling gate)sys/netproto/smb/smb_dev.c:99βnsmb_dev_open(no privilege check)
Fix verification
fixedvalidated
see evidence pack
Confirmed kernel references
β
Detail
Exploit chain
none
Evidence (decisive lines)
β
Verdict
REPRODUCED. smb_trantcp nbssn_rq_request unbounded recursion on NB_SSN_RTGRESP -> stack overflow -> double fault panic. Harness-confirmed. Gated by sibling uninit sin_family bug.
No comments yet.