The ffs_mountfs() function, part of the UFS filesystem handling code (shared between FreeBSD and Mac OS X XNU) is affected by an integer overflow vulnerability, leading to an exploitable denial of service condition and potential arbitrary code execution.

This issue is related to those published in the UFS code as part of the Month of Kernel Bugs, and the set of DMG flaws that couldn't make it to the MoKB schedule. As DMG encapsulates filesystem streams, most of the bugs existent in the FreeBSD kernel sources tree can be abused in Mac OS X's XNU via rogue DMG images.

Affected versions

This issue has been verified on Mac OS X 10.4.8 (8L2127) x86. Previous versions might be affected. FreeBSD 6.1 is affected as well.

Proof of concept, exploit or instructions to reproduce

The provided proof of concept will cause a so-called kernel panic due to allocation of a negative size buffer.

$ gunzip MOAB-10-01-2007.dmg.gz
$ hdiutil attach MOAB-10-01-2007.dmg

Note: Safari will do it automatically unless the 'Open safe files' option is disabled in your preferences (which is strongly recommended), allowing remote exploitation of this issue.

Debugging information

The following information corresponds to an up-to-date Mac OS X 10.4.8 (8L2127) system, and the provided proof of concept DMG image.

(gdb) paniclog 
panic(cpu 1 caller 0x001C0D97): getbufzone: incorect size = -2560
Backtrace, Format - Frame : Return Address (4 potential args on stack) 
0x13ebb8b8 : 0x128d1f (0x3c9540 0x13ebb8dc 0x131df4 0x0) 
0x13ebb8f8 : 0x1c0d97 (0x3d100c 0xfffff600 0x25aa5180 0x3) 
0x13ebb938 : 0x1c1d85 (0x25aa5180 0xfffff400 0x13ebb998 0x1a157e) 
0x13ebb9c8 : 0x1c1fdc (0x2ee3ad4 0x1b1000e0 0x0 0xfffff400) 
0x13ebba08 : 0x1c227b (0xfffff400 0x2b9b404 0x0 0x1) 
0x13ebba28 : 0x2e19b6 (0x2ee3ad4 0x1b1000e0 0x0 0xfffff400) 
0x13ebbaa8 : 0x2e2172 (0x2ee3ad4 0x2ec3d00 0x13ebbf40 0x0) 
0x13ebbae8 : 0x1e6147 (0x2ec3d00 0x2ee3ad4 0xbffff210 0x0) 
0x13ebbb38 : 0x1d394c (0x2ec3d00 0x2ee3ad4 0xbffff210 0x0) 
0x13ebbf68 : 0x378337 (0x2df17d0 0x2716cb8 0x2716cfc 0x0) 
0x13ebbfc8 : 0x19acae (0x25ce26c 0x0 0x8 0x25ce26c) No mapping exists for frame pointer
Backtrace terminated-invalid frame pointer 0xbffff228

(gdb) back
#0  Debugger (message=0x3c9540 "panic") at
#1  0x00128d1f in panic (str=0x3d100c "getbufzone: incorect size = %d") at
#2  0x001c0d97 in allocbuf (bp=0x25aa5180, size=-3072) at
#3  0x001c1d85 in buf_getblk (vp=0x2ee3ad4, blkno=454033632,
                  size=-3072, slpflag=0, slptimeo=0, operation=1)
                  at /SourceCache/xnu/xnu-792.13.8/bsd/vfs/vfs_bio.c:2254
#4  0x001c1fdc in bio_doread (vp=0x0, blkno=0, size=-3072, cred=0x2b9b404,
                  async=0, queuetype=1) at
#5  0x001c227b in buf_bread (vp=0x2ee3ad4, blkno=454033632, size=-3072,
                  cred=0x2b9b404, bpp=0x13ebba8c) at
#6  0x002e19b6 in ffs_mountfs (devvp=0x2ee3ad4, mp=0x2ec3d00, context=0x13ebbf40) at
#7  0x002e2172 in ffs_mount (mp=0x2ec3d00, devvp=0x2ee3ad4, data=3221221904,
                  context=0x13ebbf40) at
#8  0x001e6147 in VFS_MOUNT (mp=0x2ec3d00, devvp=0x2ee3ad4, data=3221221904,
                  context=0x13ebbf40) at
#9  0x001d394c in mount (p=0x2df17d0, uap=0x2716cb8, retval=0x2716cfc) at
#10 0x00378337 in unix_syscall (state=0x25ce26c) at
#11 0x0019acae in lo_unix_scall ()
Cannot access memory at address 0xbffff22c

(gdb) list ffs_vfsops.c:645
640             fs = ump->um_fs;
641             fs->fs_ronly = ronly;
642             size = fs->fs_cssize;                              ----- !
643             blks = howmany(size, fs->fs_fsize);
644             if (fs->fs_contigsumsize > 0)
645                     size += fs->fs_ncg * sizeof(int32_t);      ----- !
646             size += fs->fs_ncg * sizeof(u_int8_t);             ----- !
647             space = _MALLOC((u_long)size, M_UFSMNT, M_WAITOK);
648             fs->fs_csp = space;
649             for (i = 0; i < blks; i += fs->fs_frag) {       ----
(gdb) list
650                     size = fs->fs_bsize;                       ----
651                     if (i + fs->fs_frag > blks)
652                             size = (blks - i) * fs->fs_fsize;  ----
653                     if (error = (int)buf_bread(devvp, (daddr64_t)((unsigned)
                                     fsbtodb(fs, fs->fs_csaddr + i)),
654                                                size, cred, &bp)) {
655                             _FREE(fs->fs_csp, M_UFSMNT);
656                             goto out;
657                     }
658                     bcopy((char *)buf_dataptr(bp), space, (u_int)size);
(gdb) i locals
ump = (struct ufsmount *) 0x2e74404
bp = (struct buf *) 0x0
fs = (struct fs *) 0x29c66000
dev = 234881029
cgbp = (struct buf *) 0x13ebbaa8
cgp = (struct cg *) 0x2cd4004
space = (void *) 0x2e41004
error = 49167060
i = -1929379836
ronly = 1
size = 4294964224
lp = (int32_t *) 0x0
cred = (kauth_cred_t) 0x2b9b404
dbsize = 0
rev_endian = 1
context = (vfs_context_t) 0x2e41004

(gdb) x/1x $ebp-36
0x13ebba84:     0xfffff400

See 'Exploitation conditions' for more information.


Exploitation conditions

Arbitrary code execution is possible, as we control the size parameter used for buffer allocation and data is being copied directly from the stream in the DMG image. The size value wraps, leading to a heap-based buffer overflow when more data is being copied into the buffer (fs->fs_csp / space) than it's prepared to handle.

Workaround or temporary solution

Don't attempt to mount untrusted DMG files, disable Safari 'Open safe files' in it's preferences dialog, wait for Apple to release a fix (this issue was confirmed to them via e-mail after public availability of the MoKB FreeBSD issues, > month ago).

Resistance is really futile.