Re-queued NOP-IN PDU in _nop_in causes bcopy(NULL) panic in isc_sendPDU
Summary
When target sends NOP-In with itt=0xFFFFFFFF and ttt!=0xFFFFFFFF _nop_in() mutates received PDU into NOP-OUT and re-queues via isc_qout(). Received PDUs have pp->ahs==NULL (so_recv sets pp->ahs_len from BHS AHSLength AHS bytes in pq->mp never in pp->ahs). If target sets AHSLength!=0 in NOP-In pp->ahs_len nonzero while pp->ahs NULL and when proc_out() runs re-queued PDU through isc_sendPDU() bcopy(pp->ahs ...) bcopy from NULL panics kernel. Remote DoS on default config. Attacker: iSCSI target post-Login full-feature phase. NOP-In/T logic normal iSCSI keep-alive so target can send without suspicion.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-2459 Β· 9 files| File | Type | Description | Size | |
|---|---|---|---|---|
| mtarget.c | trigger-source | malicious target: NOP-IN itt=ffffffff ttt=1 AHSLength=1 | 3.6 KB | view raw |
| idrv.c | trigger-source | minimal initiator driver (ISCSISETSOC pre-login) | 2.8 KB | view raw |
| build.sh | build-script | cc -o mtarget mtarget.c; cc -o idrv idrv.c | 52 B | view raw |
| run.sh | run-script | launch mtarget nopin + idrv | 582 B | view raw |
| panic.txt | panic-signature | Fatal trap 12 va=0x0 memmove+0xb5 | 642 B | view raw |
| env.txt | environment | uname, kern.version, cc, module sha256 | 482 B | view raw |
| fix.diff | suggested-fix | in _nop_in re-queue: clear stale ahs/ds lens, force i_prepPDU | 1.1 KB | view raw |
| VERDICT.md | verdict | full analysis | 3.7 KB | β raw |
| manifest.json | manifest | this catalog | 1.9 KB | view raw |
DF-2459 β Re-queued NOP-IN PDU in _nop_in causes bcopy(NULL) in isc_sendPDU
Verdict
REPRODUCED β remote kernel panic (DoS) from a malicious iSCSI target. The bug
is a NULL-pointer read (page fault @0x0) inside bcopy/memmove, not a
write primitive; no escalation chain applies (Phase 6 read-only hard blocker).
Fix validated on a rebuilt module.
Mechanism (trigger β primitive β effect)
- Same precondition as DF-2460: a privileged user passes a connected iSCSI
socket to the kernel via
ISCSISETSOC, starting the receiver thread before login (iscsi.c:430). - The malicious target sends a NOP-IN PDU (
opcode=0x20) withitt = 0xffffffffandttt != 0xffffffff, and a non-zeroAHSLength(e.g.AHSLength=1β 4 bytes of AHS). so_recv()(isc_soc.c:387-389) setspp->ahs_len = bhs->AHSLength*4and reads the AHS bytes into the mbuf chainpq->mpβ it never setspp->ahs, which staysNULL(frompdu_alloc'smemset)._nop_in()(isc_sm.c:195) takes theitt==-1 && ttt!=-1branch, mutates the BHS into a NOP-OUT (isc_sm.c:217-227) and re-queues the same PDU viaisc_qout()(isc_sm.c:228). Becausepq->lenis non-zero (set byso_recv),isc_qoutdoes not calli_prepPDU(isc_sm.c:309), so the stalepp->ahs_lensurvives.proc_out()dequeues the NOP-OUT and callsisc_sendPDU()(isc_sm.c:530), whose active (mbuf) implementation does:c if(pp->ahs_len) { /* isc_soc.c:120 -- true */ bcopy(pp->ahs, ..., pp->ahs_len); /* isc_soc.c:125 -- pp->ahs == NULL */bcopy(NULL, ...)faults reading address0x0.
Evidence (panic signature, unpatched #0 kernel)
Fatal trap 12: page fault while in kernel mode fault virtual address = 0x0 fault code = supervisor read data, page not present instruction pointer = 0x8:0xffffffff80bca9b5 ... Stopped at memmove+0xb5: movl (%rsi),%edx db>
memmove (called by bcopy) with %rsi==0 is the bcopy(pp->ahs==NULL,...).
Reproduced by mtarget nopin + idrv.
Threat model / privilege boundary
Identical to DF-2460: the attacker is the iSCSI target; the bug fires pre-login,
no auth needed; the privileged ISCSISETSOC (root, /dev/iscsi is 0600
root:wheel) is the only thing the victim must do (normal iscontrol usage).
Why no escalation (Phase 6 hard blocker)
NULL-pointer read (bcopy from NULL). No attacker-controlled write
destination, no content control, no object reuse β the read-only / no-write
hard blocker applies; the only effect is a kernel panic (DoS).
PoC changes
mtarget.c,idrv.c: same harness as DF-2460;nopinmode sends the 52-byte NOP-IN (opcode=0x20, F=1, AHSLength=1, itt=0xffffffff, ttt=1+ 4 AHS bytes).fix.diff(new): in_nop_in's re-queue branch, clear the stale AHS/data length fields (pp->ahs_len=0,pp->ds_len=0, pointers NULL) and forcei_prepPDUto re-prepare the outgoing NOP-OUT (pq->len=0).
Fix (fix.diff)
Root cause: a received PDU is re-queued for sending with pp->ahs_len set from
the wire but pp->ahs == NULL (AHS lives in pq->mp). The outgoing NOP-OUT
keep-alive carries no AHS/data, so the fix drops the stale length fields and
lets isc_qoutβi_prepPDU recompute pq->len/BHS lengths for the NOP-OUT.
Supersedes the finding markdown's proposal (which identified the
bcopy(pp->ahs) line; this fixes the root cause at the re-queue site).
Fix validation (rebuilt single module)
- Baseline (unpatched
#0): PoC panics βFatal trap 12 va=0x0 Stopped at memmove+0xb5(panic.txt). - Patched module (sha256
f71cd799...): PoC runs clean βidrvreports "still alive after 6s (no panic)", guest up, no panic in serial log.
Fix verification
fixedVALIDATED: mtarget(nopin)+idrv PANICS on unpatched #0 module ('Fatal trap 12 va=0x0 Stopped at memmove+0xb5') and does NOT panic on single-fix module (idrv reports 'still alive after 6s (no panic)', guest up).
baseline: Fatal trap 12 va=0x0 supervisor read data, Stopped at memmove+0xb5 (bcopy NULL). patched: 'idrv: still alive after 6s (no panic)' -- no panic, guest up.
Confirmed kernel references
Detail
Exploit chain
none (NULL-pointer READ -- bcopy from NULL). Read-only NULL-deref, not write primitive, no escalation chain derivable (Phase 6 read-only hard blocker). Realistic impact ceiling: remote kernel panic / DoS from malicious iSCSI target.
Evidence (decisive lines)
Fatal trap 12: page fault while in kernel mode | fault virtual address = 0x0 | fault code = supervisor read data, page not present | Stopped at memmove+0xb5: movl (%rsi),%edx [bcopy(pp->ahs==NULL,...) in isc_sendPDU]
PoC changes
Wrote mtarget.c (nopin mode: 52-byte NOP-IN itt=0xffffffff ttt=1 AHSLength=1 + 4 AHS bytes) and idrv.c (minimal initiator: open /dev/iscsi, ISCSISETSES, socket+connect, ISCSISETSOC -- starts kernel receiver PRE-login so injected NOP-IN processed). Plus fix.diff (clear stale ahs_len/ds_len in _nop_in re-queue).
Verified recommended fix
In _nop_in's re-queue branch (isc_sm.c:225-228), zero pp->ahs_len/pp->ahs/pp->ds_len/pp->ds and set pq->len=0 so isc_qout re-runs i_prepPDU for outgoing NOP-OUT keep-alive, preventing isc_sendPDU's bcopy(pp->ahs==NULL,...). Supersedes finding proposal (fixes root cause at re-queue site). Full git-apply-able diff in findings/poc/DF-2459/fix.diff.
Verdict
REPRODUCED. _nop_in (isc_sm.c:212-228) re-queues a received NOP-IN (itt=0xffffffff, ttt!=0xffffffff) as a NOP_OUT via isc_qout WITHOUT clearing pp->ahs_len, which so_recv set from wire AHSLength while pp->ahs stays NULL (received PDUs carry AHS in pq->mp, never pp->ahs). isc_sendPDU (isc_soc.c:120-125, mbuf build) then does bcopy(pp->ahs==NULL, ..., pp->ahs_len) -> page fault at 0x0. Confirmed by 'Stopped at memmove+0xb5: movl (%rsi),%edx' with fault virtual address 0x0 (NULL read in bcopy/memmove).
No comments yet.