MOKB-21-11-2006 OMG! PWNIES! M4C BUGS NOW COME IN P1NK!

Bug details
Title: Mac OS X Apple UDTO HFS+ Disk Image Denial of Service (1) Warning - wet floor!
Description: Mac OS X fails to properly handle corrupted UDTO HFS+ image structures (ex. bad sectors), leading to an exploitable denial of service condition. Although it hasn't been checked further, memory corruption is present under certain conditions (in this particular case, unlikely to allow arbitrary code execution).

This issue probably exists in FreeBSD as well, although it can't be trigerred in the same manner. Thus, a future MoKB release will cover related filesystem bugs verified for FreeBSD.

This issue is remotely exploitable as Safari loads DMG files from external sources (ex. visiting an URL). This can be prevented by changing the Preferences and deactivating the functionality for 'opening "safe" files after downloading'.
Author/Contributor: NA<NA[at] info-pull.com> - discovery, MoKB release, debugging.
References:
Proof of concept or exploit: The following DMG image can be used to reproduce the bug: MOKB-21-11-2006.dmg.bz2
bunzip2 MOKB-21-11-2006.dmg.bz2 && hdiutil attach MOKB-21-11-2006.dmg
For Safari users feeling lucky, at: MOKB-21-11-2006.dmg
Debugging information:

It's been tested on an up-to-date (21-11-2006) Mac OS X installation, running on an Intel "shipping" Mac.

brubg:/tmp feuckstevo$ gdb /Volumes/KernelDebugKit/mach_kernel -c core-xnu-792.13.8-172.16.0.10-2b057da7
GNU gdb 6.3.50-20050815 (Apple version gdb-573) (Fri Oct 20 15:50:43 GMT 2006)
Copyright 2004 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for details.
This GDB was configured as "i386-apple-darwin"...
#0  Debugger (message=0x3c9540 "panic") at /SourceCache/xnu/xnu-792.13.8/osfmk/i386/AT386/model_dep.c:770
Line number 770 out of range; /SourceCache/xnu/xnu-792.13.8/osfmk/i386/AT386/model_dep.c has 312 lines.
(gdb) bt
#0  Debugger (message=0x3c9540 "panic") at /SourceCache/xnu/xnu-792.13.8/osfmk/i386/AT386/model_dep.c:770
#1  0x00128d1f in panic (str=0x3d2090 "mount: lost mount") at /SourceCache/xnu/xnu-792.13.8/osfmk/kern/debug.c:202
#2  0x001d3a6e in mount (p=0x2d015dc, uap=0x2d9e720, retval=0x2d9e764)
    at /SourceCache/xnu/xnu-792.13.8/bsd/vfs/vfs_syscalls.c:626
#3  0x00378337 in unix_syscall (state=0x2ddd6c0) at /SourceCache/xnu/xnu-792.13.8/bsd/dev/i386/systemcalls.c:196
#4  0x0019acae in lo_unix_scall ()
Cannot access memory at address 0xbffffdf8
Cannot access memory at address 0xbffffdfc

(gdb) info registers
eax            0x0      0
ecx            0x0      0
edx            0x0      0
ebx            0x1      1
esp            0x13e6ba6c       0x13e6ba6c
ebp            0x13e6baf8       0x13e6baf8
esi            0x1      1
edi            0x1000   4096
eip            0x1a8674 0x1a8674 
eflags         0x0      0
cs             0x0      0
ss             0x0      0
ds             0x0      0
es             0x0      0
fs             0x0      0
gs             0x0      0

(gdb) source /Volumes/KernelDebugKit/kgmacros
Loading Kernel GDB Macros package.  Type "help kgm" for more info.

(gdb) showcurrentstacks
task        vm_map      ipc_space  #acts   pid  proc        command
0x025bfda0  0x013d6f3c  0x02589ef0   46      0  0x004d2200  kernel_task
            activation  thread      pri  state  wait_queue  wait_event
            0x025dec64  0x025dec64    0  IR
                reserved_stack=0x13e30000
                kernel_stack=0x14068000
                stacktop=0x1406bf18
                0x1406bf18  0x1a42f5 
                0x1406bf38  0x19d871 
                0x1406bf58  0x135f23 
                0x1406bfc8  0x19a74c 
                stackbottom=0x1406bfc8

task        vm_map      ipc_space  #acts   pid  proc        command
0x02e19bf0  0x02e0a17c  0x02589130    1   7849  0x02d015dc  mount_hfs
            activation  thread      pri  state  wait_queue  wait_event
            0x02ea4238  0x02ea4238   31  R
                kernel_stack=0x13e68000
                stacktop=0x13e6baf8
                0x13e6baf8  0x128d1f 
                0x13e6bb38  0x1d3a6e 
                0x13e6bf68  0x378337 
                0x13e6bfc8  0x19acae 
                stackbottom=0x13e6bfc8

(gdb) showkmod 0x29e32ee0
kmod        address     size        id    refs     version  name
0x29e32ee0  0x29e08000  0x0002c000   99      0       113.3  com.apple.AppleDiskImageController

(gdb) paniclog
panic(cpu 1 caller 0x001D3A6E): mount: lost mount
Backtrace, Format - Frame : Return Address (4 potential args on stack)
0x13e6baf8 : 0x128d1f (0x3c9540 0x13e6bb1c 0x131df4 0x0)
0x13e6bb38 : 0x1d3a6e (0x3d2090 0x13e6bf48 0x13e6bf40 0x0)
0x13e6bf68 : 0x378337 (0x2d015dc 0x2d9e720 0x2d9e764 0x0)
0x13e6bfc8 : 0x19acae (0x2ddd6c0 0x0 0x19d0b5 0x25d97a0) No mapping exists for frame pointer
Backtrace terminated-invalid frame pointer 0xbffffdf8

Kernel version:
Darwin Kernel Version 8.8.1: Mon Sep 25 19:42:00 PDT 2006; root:xnu-792.13.8.obj~1/RELEASE_I386

(gdb) frame 2
#2  0x001d3a6e in mount (p=0x2d015dc, uap=0x2d9e720, retval=0x2d9e764)
    at /SourceCache/xnu/xnu-792.13.8/bsd/vfs/vfs_syscalls.c:626
626                             proc_fdunlock(p);
(gdb) info locals
fdp_rvp = (struct vnode *) 0x1532c18
rdir_changed = 4901148
funnel_state = 3918896
fdp = (struct filedesc *) 0x13e6bba8
newdp = (struct vnode *) 0x0
p = (struct proc *) 0x0
tvp = (struct vnode *) 0x0
fdp_cvp = (struct vnode *) 0x3bc4c9
cdir_changed = 45347264
vp = (struct vnode *) 0x308bad4
devvp = (struct vnode *) 0x308bc60
device_vnode = (struct vnode *) 0x308bc60
mp = (struct mount *) 0x2df5400
vfsp = (struct vfstable *) 0x0
error = 0
flag = 0
va = {
  va_supported = 33544145,
  va_active = 128,
  va_vaflags = 0,
  va_rdev = 0,
  va_nlink = 205449675038703228,
  va_total_size = 95468807934630292,
  va_total_alloc = 1,
  va_data_size = 102,
  va_data_alloc = 512,
  va_iosize = 4096,
  va_uid = 502,
  va_gid = 80,
  va_mode = 16877,
  va_flags = 0,
  va_acl = 0x1531f98,
  va_create_time = {
    tv_sec = 1164141344,
    tv_nsec = 0
  },
  va_access_time = {
    tv_sec = 333889248,
    tv_nsec = 1
  },
  va_modify_time = {
    tv_sec = 1164141344,
    tv_nsec = 0
  },
  va_change_time = {
    tv_sec = 1164141344,
    tv_nsec = 0
  },
  va_backup_time = {
    tv_sec = 0,
    tv_nsec = 0
  },
  (...)
  },
  va_nchildren = 170040229198709304
}
context = {
  vc_proc = 0x2d015dc,
  vc_ucred = 0x2d74204
}
nd = {
  (...)
  }
}
nd1 = {
  ni_dirp = 3221225221,
  ni_segflg = UIO_USERSPACE32,
  ni_startdir = 0x0,
  (...)
  ni_loopcnt = 0,
  ni_cnd = {
    cn_nameiop = 0,
    cn_flags = 49216,
    cn_context = 0x13e6bf40,
    pad_obsolete2 = 0x15318b8,
    cn_pnbuf = 0x13e6bd20 "/dev/disk6s2",
    cn_pnlen = 256,
    cn_nameptr = 0x13e6bd25 "disk6s2",
    cn_namelen = 7,
    cn_hash = 1595256236,
    cn_consume = 0
  }
}
fstypename = "hfs\000\000\000\000\037\000 \000\000"
dummy = 4
devpath = 3221225221
fsmountargs = 3221224872
ronly = 0
mntalloc = 1
is_64bit = 0
is_rwlock_locked = 0
p = (struct proc *) 0x0

(gdb) list *0x001d3a6e
0x1d3a6e is in mount (/SourceCache/xnu/xnu-792.13.8/bsd/vfs/vfs_syscalls.c:627).
622             for (p = allproc.lh_first; p != 0; p = p->p_list.le_next) {
623                     proc_fdlock(p);
624                     fdp = p->p_fd;
625                     if (fdp == (struct filedesc *)0) {
626                             proc_fdunlock(p);
627                             continue;
628                     }
629                     fdp_cvp = fdp->fd_cdir;
630                     fdp_rvp = fdp->fd_rdir;
631                     proc_fdunlock(p);

(gdb) p fdp
$5 = (struct filedesc *) 0x13e6bba8
(gdb) p allproc
$8 = {
  lh_first = 0x2d015dc
}
(gdb) list 601,664
601     static void
602     checkdirs(olddp, context)
603             struct vnode *olddp;
604             vfs_context_t context;
605     {
606             struct filedesc *fdp;
607             struct vnode *newdp;
608             struct proc *p;
609             struct vnode *tvp;
610             struct vnode *fdp_cvp;
611             struct vnode *fdp_rvp;
612             int cdir_changed = 0;
613             int rdir_changed = 0;
614             boolean_t funnel_state;
615
616             if (olddp->v_usecount == 1)
617                     return;
618             if (VFS_ROOT(olddp->v_mountedhere, &newdp, context))
619                     panic("mount: lost mount");
620             funnel_state = thread_funnel_set(kernel_flock, TRUE);
621
622             for (p = allproc.lh_first; p != 0; p = p->p_list.le_next) {
623                     proc_fdlock(p);
624                     fdp = p->p_fd;
625                     if (fdp == (struct filedesc *)0) {
626                             proc_fdunlock(p);
627                             continue;
628                     }
629                     fdp_cvp = fdp->fd_cdir;
630                     fdp_rvp = fdp->fd_rdir;
631                     proc_fdunlock(p);
632
633                     if (fdp_cvp == olddp) {
634                             vnode_ref(newdp);
635                             tvp = fdp->fd_cdir;
636                             fdp_cvp = newdp;
637                             cdir_changed = 1;
638                             vnode_rele(tvp);
639                     }
640                     if (fdp_rvp == olddp) {
641                             vnode_ref(newdp);
642                             tvp = fdp->fd_rdir;
643                             fdp_rvp = newdp;
644                             rdir_changed = 1;
645                             vnode_rele(tvp);
646                     }
647                     if (cdir_changed || rdir_changed) {
648                             proc_fdlock(p);
649                             fdp->fd_cdir = fdp_cvp;
650                             fdp->fd_rdir = fdp_rvp;
651                             proc_fdunlock(p);
652                     }
653             }
654             if (rootvnode == olddp) {
655                     vnode_ref(newdp);
656                     tvp = rootvnode;
657                     rootvnode = newdp;
658                     vnode_rele(tvp);
659             }
660             thread_funnel_set(kernel_flock, funnel_state);
661
662             vnode_put(newdp);
663     }
664