_reject dereferences pq->mp without NULL check panicking on data-less REJECT from malicious target
Summary
_reject() unconditionally dereferences received PDUs mbuf chain as pdu=mtod(pq->mp pdu_t*). so_recv() never attaches mbuf when PDU carries no AHS no data segment no digests - pq->mp stays NULL. Target sending REJECT with DSLength=0 and AHSLength=0 (with digests off default) drives mtod(NULL) read from address 0 kernel panic. Remote DoS. Attacker: iSCSI target full-feature phase send 48-byte REJECT BHS opcode=0x3f AHSLength=0 DSLength=0. Default config vulnerable.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-2460 Β· 10 files| File | Type | Description | Size | |
|---|---|---|---|---|
| mtarget.c | trigger-source | malicious iSCSI target: injects data-less REJECT | 3.6 KB | view raw |
| idrv.c | trigger-source | minimal initiator driver: ISCSISETSOC starts receiver pre-login | 2.8 KB | view raw |
| build.sh | build-script | cc -o mtarget mtarget.c; cc -o idrv idrv.c | 123 B | view raw |
| run.sh | run-script | launch mtarget reject + idrv | 769 B | view raw |
| panic.txt | panic-signature | Fatal trap 12 va=0x10 ism_recv+0xee | 654 B | view raw |
| dmesg.txt | dmesg | patched: _reject NULL-mbuf drop message | 88 B | view raw |
| env.txt | environment | uname, kern.version, cc, module sha256 | 482 B | view raw |
| fix.diff | suggested-fix | NULL-check pq->mp in _reject, drop data-less REJECT | 910 B | view raw |
| VERDICT.md | verdict | full analysis | 5.0 KB | β raw |
| manifest.json | manifest | this catalog | 2.1 KB | view raw |
DF-2460 β _reject dereferences pq->mp without a NULL check
Verdict
REPRODUCED β remote kernel panic (DoS) from a malicious iSCSI target. The bug is a NULL-pointer read (page fault), not a write primitive, so no privilege escalation chain applies; the realistic impact ceiling is a remote denial of service against any host running the iscsi_initiator module that connects to a malicious/compromised iSCSI target. Fix validated on a rebuilt module.
Mechanism (trigger β primitive β effect)
- A root user starts an iSCSI session and hands a connected TCP socket to the
kernel via the
ISCSISETSOCioctl.i_setsoc()(sys/dev/disk/iscsi/initiator/iscsi.c:413) immediately callsisc_start_receiver()(iscsi.c:430), which spawns theisc_socreceiver thread (isc_soc.c:568). The receiver runs before iSCSI login completes, so it processes whatever the target sends next. - The malicious target sends a 48-byte REJECT PDU (
opcode=0x3f) withAHSLength=0,DSLength=0and no digests (the default, no CRC32C). so_recv()(isc_soc.c:365) computeslen = 0for such a PDU and therefore skips theif(len){...}block that assignspq->mp = sbp.sb_mb(isc_soc.c:428-481);pq->mpstaysNULL(set bymemsetinpdu_alloc,iscsivar.h:321).ism_recv()dispatches the REJECT to_reject()(isc_sm.c:448βisc_sm.c:79). The very first statement dereferences the NULL mbuf:c pdu = mtod(pq->mp, pdu_t *); /* isc_sm.c:87 */mtod(m,t)expands to(t)((m)->m_data);m_datalives at offset0x10instruct mbuf, so the kernel reads from address0x10β page fault.
Evidence (panic signature, unpatched #0 kernel)
Fatal trap 12: page fault while in kernel mode fault virtual address = 0x10 fault code = supervisor read data, page not present Stopped at ism_recv+0xee: movq 0x10(%rdx),%rdx db>
The faulting instruction (movq 0x10(%rdx),%rdx with %rdx==0) is exactly the
mtod(NULL,...) read of m_data at offset 0x10. Reproduced by
mtarget reject (sends the REJECT) + idrv (passes a connected socket to the
kernel via ISCSISETSOC).
Threat model / privilege boundary
- The attacker is the iSCSI target (a malicious or compromised storage
server). The victim is the kernel of any host whose
iscsi_initiatorconnects to it. No authentication is required by the iSCSI default (RFC 3720), and the bug fires before login completes, so the malicious PDU needs no valid session state. - Triggering requires
ISCSISETSOC, which needsopen("/dev/iscsi")β the device isUID_ROOT, GID_WHEEL, 0600(iscsi.c:641,762), so a privileged user must initiate the connection. That is the normal operational model (iscontrol(8)runs as root). Once a session exists, the attacker (target) controls the damage; the boundary crossed is network β kernel.
Why no escalation (Phase 6 hard blocker)
The primitive is a read from a fixed low address (NULL + 0x10) β it is a
NULL-pointer dereference, not an attacker-controlled write. There is no
attacker-influenced destination, no content control, and no freed/reused object.
This is the "genuinely read-only / no write primitive" valid hard blocker from
Phase 6: the only achievable effect is crashing the kernel. (The controlled
fault address 0x10 is not mappable on this kernel and conveys no primitive.)
PoC changes
mtarget.c(new): minimal TCP server that injects a crafted PDU per mode. For DF-2460, sends a 48-byte REJECT BHS (opcode=0x3f, F=1, AHS=0, DS=0).idrv.c(new): minimal initiator driver βopen /dev/iscsi,ISCSISETSES,open /dev/iscsiN,socket()+connect(),ISCSISETSOCβ to start the kernel receiver without performing iSCSI login, exactly the preconditioni_setsoc()creates.fix.diff(new): NULL-checkpq->mpin_rejectand drop the PDU.
Fix (fix.diff)
Add a NULL guard at sys/dev/disk/iscsi/initiator/isc_sm.c:87 (_reject):
if pq->mp == NULL (data-less REJECT), log via xdebug and pdu_free() the
PDU instead of mtod()-dereferencing it. Without the data segment the rejected
task tag cannot be recovered, so dropping the PDU is the correct safe action.
Supersedes the finding markdown's proposal (the markdown suggested a NULL
check; this implements it at the exact faulting line with the drop semantics).
Fix validation (rebuilt single module, not a full kernel)
- Baseline (unpatched
#0module): PoC panics the guest βFatal trap 12,fault virtual address = 0x10,ism_recv+0xee. (panic.txt) - Patched module (rebuilt
iscsi_initiator.ko, reinstalled,kldloaded, sha256b1e16868...): PoC runs clean βidrvreports "still alive after 6s (no panic)";dmesgshows ">>> _reject: 0] REJECT with NULL mbuf (no data segment) - dropping". - The module rebuild was used because this is a loadable KLD (
bsd.kmod.mk), so a full nativekernel rebuild is unnecessary; the patched.cis compiled with the same flags/KERNCONF and linked into the module.
Fix verification
fixedVALIDATED: mtarget(reject)+idrv PANICS on unpatched #0 module ('Fatal trap 12 va=0x10 Stopped at ism_recv+0xee') and does NOT panic on single-fix module (idrv 'still alive after 6s', dmesg '>>> _reject: 0] REJECT with NULL mbuf (no data segment) - dropping').
baseline: Fatal trap 12 va=0x10 supervisor read data, Stopped at ism_recv+0xee: movq 0x10(%rdx),%rdx. patched: '>>> _reject: 0] REJECT with NULL mbuf (no data segment) - dropping' -- no panic, guest up.
Confirmed kernel references
Detail
Exploit chain
none (NULL-pointer READ -- mtod(NULL) deref at fixed offset 0x10). Read-only NULL-deref, not write primitive; no escalation derivable (Phase 6 read-only hard blocker). Realistic impact: remote kernel panic / DoS from malicious iSCSI target.
Evidence (decisive lines)
Fatal trap 12: page fault while in kernel mode | fault virtual address = 0x10 | fault code = supervisor read data, page not present | Stopped at ism_recv+0xee: movq 0x10(%rdx),%rdx [mtod(pq->mp==NULL)]
PoC changes
Wrote mtarget.c (reject mode: 48-byte REJECT BHS opcode=0x3f F=1 AHS=0 DS=0) and idrv.c (ISCSISETSOC starts receiver pre-login). Plus fix.diff (NULL-check pq->mp in _reject).
Verified recommended fix
In _reject (isc_sm.c:87) NULL-check pq->mp before mtod; if NULL (data-less REJECT) xdebug+pdu_free+return (without data segment the rejected task tag cannot be recovered). Supersedes finding proposal (implements NULL check at faulting line with drop semantics). Full git-apply-able diff in findings/poc/DF-2460/fix.diff.
Verdict
REPRODUCED. _reject (isc_sm.c:87) does pdu = mtod(pq->mp, pdu_t*) unconditionally. so_recv (isc_soc.c:428-481) only assigns pq->mp inside if(len){...}; a REJECT with AHSLength=0, DSLength=0, no digests has len=0 so pq->mp stays NULL (pdu_alloc memset). mtod(NULL) reads m_data at offset 0x10 -> page fault at 0x10. Confirmed by 'Stopped at ism_recv+0xee: movq 0x10(%rdx),%rdx' with fault virtual address 0x10.
No comments yet.