Technology is evil.


A specially crafted UFS filesystem in a DMG image can cause the ufs_lookup() function to call ufs_dirbad() when a corrupted directory entry is being read, leading to a kernel panic (denial of service). This issue can't lead to arbitrary code execution.

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 also affected, although the functions have few differences.

Proof of concept, exploit or instructions to reproduce

The provided proof of concept will cause a so-called kernel panic ("bad dir" from ufs_dirbad()).

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

Note: Safari will mount DMG files automatically after download, 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 0x002E4D81): bad dir
Backtrace, Format - Frame : Return Address (4 potential args on stack) 
0x13fe3308 : 0x128d1f (0x3c9540 0x13fe332c 0x131df4 0x0) 
0x13fe3348 : 0x2e4d81 (0x3e35f4 0x2ec7da8 0x2 0x0) 
0x13fe3378 : 0x2e500b (0x2ee6ef0 0x0 0x3e35fc 0x0) 
0x13fe3418 : 0x1e2df1 (0x13fe3438 0x13fe344c 0x8 0x13fe3f40) 
0x13fe3468 : 0x1cab0b (0x2ee8f78 0x13fe39d8 0x13fe3aec 0x13fe3f40) 
0x13fe34d8 : 0x1cb331 (0x13fe39c0 0x13fe39e8 0x100 0x13fe39e0) 
0x13fe3588 : 0x1d2f15 (0x13fe39c0 0x3d207c 0x2ec7da8 0x3d2070) 
0x13fe3b38 : 0x1d3c48 (0x2ec7d00 0x13fe3f40 0x13fe3f40 0x0) 
0x13fe3f68 : 0x378337 (0x2df25dc 0x25caa68 0x25caaac 0x0) 
0x13fe3fc8 : 0x19acae (0x25cfdc4 0x0 0x19d0b5 0x25cfdc4) No mapping exists for frame pointer
Backtrace terminated-invalid frame pointer 0xbffff228

Kernel version:
Darwin Kernel Version 8.8.1: Mon Sep 25 19:42:00 PDT 2006;

(gdb) back
#0  Debugger (message=0x3c9540 "panic") at
#1  0x00128d1f in panic (str=0x3e35f4 "bad dir") at
#2  0x002e4d81 in ufs_dirbad (ip=0x2ee6ef0, offset=0, how=0x3e35fc "mangled entry")
              at /SourceCache/xnu/xnu-792.13.8/bsd/ufs/ufs/ufs_lookup.c:559
#3  0x002e500b in ufs_lookup (ap=0x13fe3438) at
#4  0x001e2df1 in VNOP_LOOKUP (dvp=0x2ee8f78, vpp=0x13fe39d8, cnp=0x13fe3aec,
               context=0x13fe3f40) at /SourceCache/xnu/xnu-792.13.8/bsd/vfs/kpi_vfs.c:2194
#5  0x001cab0b in lookup (ndp=0x13fe39c0) at
#6  0x001cb331 in namei (ndp=0x13fe39c0) at
#7  0x001d2f15 in enablequotas (mp=0x2ec7d00, context=0x13fe3f40) at
#8  0x001d3c48 in mount (p=0x2df25dc, uap=0x25caa68, retval=0x25caaac) at
#9  0x00378337 in unix_syscall (state=0x25cfdc4) at
#10 0x0019acae in lo_unix_scall ()
Cannot access memory at address 0xbffff22c

ufs_lookup() function calls ufs_dirbad() (which generated the panic) when a corrupted directory entry (struct direct) is being read. The following source code snippet comes from the current xnu-792 sources:

---------- bsd/ufs/ufs/ufs_lookup.c
264                 /*
265                  * Get pointer to next entry.
266                  * Full validation checks are slow, so we only check
267                  * enough to insure forward progress through the
268                  * directory. Complete checks can be run by patching
269                  * "dirchk" to be true.
270                  */
271                 ep = (struct direct *)((char *)buf_dataptr(bp) + entryoffsetinblock);
272                 if (ep->d_reclen == 0 ||
273                     dirchk && ufs_dirbadentry(vdp, ep, entryoffsetinblock)) {
274                         int i;
276                         ufs_dirbad(dp, dp->i_offset, "mangled entry");
277                         i = DIRBLKSIZ - (entryoffsetinblock & (DIRBLKSIZ - 1));
278                         dp->i_offset += i;
279                         entryoffsetinblock += i;
280                         continue;
281                 }

Again, most of the values in the inode and buf data structures are read from the UFS stream encapsulated in the DMG image:

(gdb) select-frame 3
(gdb) info locals 
i = 0
vdp = (struct vnode *) 0x2ee8f78
dp = (struct inode *) 0x2ee6ef0
bp = (struct buf *) 0x25aa5330
ep = (struct direct *) 0x2ee9000
entryoffsetinblock = 0
slotstatus = FOUND
slotoffset = -1
slotsize = 0
slotfreespace = 0
slotneeded = 0
numdirpasses = 1
endsearch = 1951164416
prevoff = 0
tdp = (struct vnode *) 0xe0
enduseful = 0
bmask = 4095
wantparent = 0
namlen = 0
error = 0
vpp = (struct vnode **) 0x13fe39d8
cnp = (struct componentname *) 0x13fe3aec
flags = 49216
rev_endian = 134217728

(gdb) p ep->d_reclen 
$3 = 0
(gdb) p bp->b_bufsize
$7 = 4096
(gdb) p bp->b_lflags
$12 = 1  (BL_BUSY = I/O in progress)

There are other potential issues in the ufs_lookup() function which will be covered by future MoAB releases. It must be noted that this particular issue doesn't represent any risk of code execution, although filesystem corruption has been observed when other UFS filesystems have been mounted and have pending read/write operations.


Workaround or temporary solution

Don't attempt to mount untrusted DMG files, disable Safari 'Open safe files' in it's preferences dialog.

Dreg Agreen is futile.