| Title: | Mac OS X Universal Binary Loading Memory Corruption |
|
| Description: | Mac OS X fails to properly handle corrupted Universal Binaries, leading to an exploitable memory corruption condition with potential risk of kernel-mode arbitrary code execution. This particular vulnerability is caused by an integer overflow in the fatfile_getarch2() function. Local unprivileged users can abuse this issue with specially crafted Mach-O 'Universal' binaries. | |
| Author/Contributor: | NA<NA[at] info-pull.com> - discovery, MoKB release, debugging. | |
| References: | ||
| Proof of concept or exploit: |
The following Mach-O 'Universal' binary can be used to reproduce the bug:
MOKB-26-11-2006.bz2
bunzip2 MOKB-26-11-2006.bz2 && ./MOKB-26-11-2006 |
|
| Debugging information: |
It's been tested on an up-to-date (26-11-2006) Mac OS X installation, running on an Intel "shipping" Mac (x86).
yssupstae:/tmp evets$ gdb /Volumes/KernelDebugKit/mach_kernel -c core-xnu-792.13.8-172.16.0.10-79aa141d
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) source /Volumes/KernelDebugKit/kgmacros
Loading Kernel GDB Macros package. Type "help kgm" for more info.
(gdb) paniclog
panic(cpu 0 caller 0x001A3135): Unresolved kernel trap (CPU 0, Type 14=page fault), registers:
CR0: 0x80010033, CR2: 0x2524200c, CR3: 0x00d72000, CR4: 0x000006e0
EAX: 0x00000000, EBX: 0x3fffff35, ECX: 0x40000002, EDX: 0x00000000
CR2: 0x2524200c, EBP: 0x13fcb8a8, ESI: 0x2524200c, EDI: 0x00ffffff
EFL: 0x00010206, EIP: 0x00369de4, CS: 0x00000004, DS: 0x02ec000c
Backtrace, Format - Frame : Return Address (4 potential args on stack)
0x13fcb6c8 : 0x128d1f (0x3c9540 0x13fcb6ec 0x131df4 0x0)
0x13fcb708 : 0x1a3135 (0x3cf1f4 0x0 0xe 0x3cea24)
0x13fcb818 : 0x19a8d4 (0x13fcb828 0xe000002 0xe 0x48)
0x13fcb8a8 : 0x369f48 (0xff000000 0x13fcb928 0x2 0x0)
0x13fcb8e8 : 0x335826 (0x2ecf4a4 0x25241000 0x13fcb928 0x0)
0x13fcb958 : 0x335f09 (0x13fcb9b8 0x2ecf4a4 0x25241000 0x1000)
0x13fcbf68 : 0x378337 (0x2d9a1f4 0x2d8abf0 0x2d8ac34 0x0)
0x13fcbfc8 : 0x19acae (0x2dc7bd4 0x0 0x19d0b5 0x2dc7bd4) No mapping exists for frame pointer
Backtrace terminated-invalid frame pointer 0xbffff038
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) 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=0x3cf1f4 "Unresolved kernel trap (CPU %d, Type %d=%s), registers:\nCR0: 0x%08x,
CR2: 0x%08x, CR3: 0x%08x, CR4: 0x%08x\nEAX: 0x%08x, EBX: 0x%08x, ECX: 0x%08x, EDX: 0x%08x\nCR2: 0x%08x,
EBP: 0x%08x, ESI: 0x%08x, EDI"...) at /SourceCache/xnu/xnu-792.13.8/osfmk/kern/debug.c:202
#2 0x001a3135 in kernel_trap (state=0x13fcb828) at /SourceCache/xnu/xnu-792.13.8/osfmk/i386/trap.c:630
#3 0x0019a8d4 in trap_from_kernel ()
#4 0x00369f48 in fatfile_getarch_affinity (vp=0x2ecf4a4, data_ptr=623120384, archret=0x13fcb928, affinity=0)
at /SourceCache/xnu/xnu-792.13.8/bsd/kern/mach_fat.c:199
#5 0x00335826 in exec_fat_imgact (imgp=0x13fcb9b8) at /SourceCache/xnu/xnu-792.13.8/bsd/kern/kern_exec.c:499
#6 0x00335f09 in execve (p=0x2d9a1f4, uap=0x2d8abf0, retval=0x2d8ac34) at /SourceCache/xnu/xnu-792.13.8/bsd/kern/kern_exec.c:1118
#7 0x00378337 in unix_syscall (state=0x2dc7bd4) at /SourceCache/xnu/xnu-792.13.8/bsd/dev/i386/systemcalls.c:196
#8 0x0019acae in lo_unix_scall ()
Cannot access memory at address 0xbffff038
Cannot access memory at address 0xbffff03c
(gdb) info registers
eax 0x0 0
ecx 0x0 0
edx 0x0 0
ebx 0x19a760 1681248
esp 0x13fcb8b0 0x13fcb8b0
ebp 0x13fcb8e8 0x13fcb8e8
esi 0x2524200c 623124492
edi 0x13fcb828 335329320
eip 0x369f48 0x369f48 <fatfile_getarch_affinity+95>
eflags 0x0 0
cs 0x0 0
ss 0x0 0
ds 0x0 0
es 0x0 0
fs 0x0 0
gs 0x0 0
(gdb) frame
#4 0x00369f48 in fatfile_getarch_affinity (vp=0x2ecf4a4, data_ptr=623120384, archret=0x13fcb928, affinity=0)
at /SourceCache/xnu/xnu-792.13.8/bsd/kern/mach_fat.c:199
(gdb) printf "%x\n", data_ptr
25241000
(gdb) x/10x data_ptr
0x25241000: 0xbebafeca 0x02000040 0x07000000 0x03000000
0x25241010: 0x00100000 0xd433b500 0x0c000000 0x12f40000
0x25241020: 0x00000000 0x00500000
(gdb) frame
#5 0x00335826 in exec_fat_imgact (imgp=0x13fcb9b8) at /SourceCache/xnu/xnu-792.13.8/bsd/kern/kern_exec.c:499
(gdb) x/40x imgp->ip_vdata
0x25241000: 0xbebafeca 0x02000040 0x07000000 0x03000000
0x25241010: 0x00100000 0xd433b500 0x0c000000 0x12f40000
0x25241020: 0x00000000 0x00500000 0x98c90000 0x0c009e00
0x25241030: 0x00000000 0x00000000 0x00000000 0x00000000
0x25241040: 0x00000000 0x00000000 0x00000000 0x00000000
0x25241050: 0x00000000 0x000000ff 0x00000000 0x00000000
0x25241060: 0x00000000 0x00000000 0x00000000 0x00000000
0x25241070: 0x00000000 0x00000000 0x00000000 0x00000000
0x25241080: 0x00000000 0x00000000 0x00000000 0x00000000
0x25241090: 0x00007400 0x00000000 0x00000000 0x00000000
(gdb) p imgp->ip_strings
$11 = 0x25201000 "tmp/Mach-o_96"
(gdb) p imgp->ip_vp->v_name
$19 = 0x2660494 "Mach-o_96"
(gdb) p imgp->ip_arch_offset
$23 = 0
(gdb) p imgp->ip_arch_size
$24 = 34200
(gdb) printf "%x\n", imgp->ip_arch_size
8598
(gdb) p fat_arch.size
$25 = 8
(gdb) p fat_arch.offset
$26 = 11
-------------- exec_fat_imgact(struct image_params *imgp) bsd/kern/kern_exec.c
(we don't reach this point, these remain non assigned)
457 /* Success. Indicate we have identified an encapsulated binary */
458 error = -2;
459 imgp->ip_arch_offset = (user_size_t)fat_arch.offset;
460 imgp->ip_arch_size = (user_size_t)fat_arch.size;
-------------- bsd/kern/kern_exec.c
-------------- bsd/sys/imgact.h
62 struct image_params {
63 user_addr_t ip_user_fname; /* argument */
64 user_addr_t ip_user_argv; /* argument */
65 user_addr_t ip_user_envv; /* argument */
66 struct vnode *ip_vp; /* file */
67 struct vnode_attr *ip_vattr; /* run file attributes */
68 struct vnode_attr *ip_origvattr; /* invocation file attributes */
69 char *ip_vdata; /* file data (up to one page) */
70 int ip_flags; /* image flags */
71 int ip_argc; /* argument count */
72 char *ip_argv; /* argument vector beginning */
73 int ip_envc; /* environment count */
74 char *ip_strings; /* base address for strings */
75 char *ip_strendp; /* current end pointer */
76 char *ip_strendargvp; /* end of argv/start of envp */
77 int ip_strspace; /* remaining space */
78 user_size_t ip_arch_offset; /* subfile offset in ip_vp */
79 user_size_t ip_arch_size; /* subfile length in ip_vp */
80 char ip_interp_name[IMG_SHSIZE]; /* interpreter name */
81
82 /* Next two fields are for support of Classic... */
83 char *ip_p_comm; /* optional alt p->p_comm */
84 char *ip_tws_cache_name; /* task working set cache */
85 struct vfs_context *ip_vfs_context; /* VFS context */
86 struct nameidata *ip_ndp; /* current nameidata */
87 thread_t ip_vfork_thread; /* thread created, if vfork */
88 };
------------ bsd/sys/imgact.h
(gdb) x/10i 0x00369de4 <---- eip at fault time
0x369d94 <fatfile_getarch2+4>: push %esi
0x369d95 <fatfile_getarch2+5>: push %ebx
0x369d96 <fatfile_getarch2+6>: sub $0x2c,%esp
0x369d99 <fatfile_getarch2+9>: mov %ecx,-36(%ebp)
0x369d9c <fatfile_getarch2+12>: mov 4(%edx),%ecx
0x369d9f <fatfile_getarch2+15>: bswap %ecx
0x369da1 <fatfile_getarch2+17>: lea (%ecx,%ecx,4),%eax
0x369da4 <fatfile_getarch2+20>: lea 0(,%eax,4),%ebx
0x369dab <fatfile_getarch2+27>: lea 8(%ebx),%eax
0x369dae <fatfile_getarch2+30>: cmp $0x200,%eax
0x369db3 <fatfile_getarch2+35>: jg 0x369e6d <fatfile_getarch2+221>
0x369db9 <fatfile_getarch2+41>: lea 4103(%ebx),%eax
0x369dbf <fatfile_getarch2+47>: test $0xfffff000,%eax
0x369dc4 <fatfile_getarch2+52>: je 0x369e6d <fatfile_getarch2+221>
0x369dca <fatfile_getarch2+58>: lea 8(%edx),%esi
0x369dcd <fatfile_getarch2+61>: mov %ecx,%ebx
0x369dcf <fatfile_getarch2+63>: movl $0x0,-32(%ebp)
0x369dd6 <fatfile_getarch2+70>: movl $0x0,-28(%ebp)
0x369ddd <fatfile_getarch2+77>: mov 8(%ebp),%edi
0x369de0 <fatfile_getarch2+80>: not %edi
0x369de2 <fatfile_getarch2+82>: jmp 0x369e13 <fatfile_getarch2+131>
0x369de4 <fatfile_getarch2+84>: mov (%esi),%edx
0x369de6 <fatfile_getarch2+86>: bswap %edx
0x369de8 <fatfile_getarch2+88>: mov %edi,%eax
0x369dea <fatfile_getarch2+90>: and %edx,%eax
0x369dec <fatfile_getarch2+92>: cmp -36(%ebp),%eax
0x369def <fatfile_getarch2+95>: jne 0x369e0d <fatfile_getarch2+125>
0x369df1 <fatfile_getarch2+97>: mov 4(%esi),%eax
0x369df4 <fatfile_getarch2+100>: bswap %eax
0x369df6 <fatfile_getarch2+102>: mov %eax,4(%esp)
(gdb) x/10 0x2524200c
0x2524200c: Cannot access memory at address 0x2524200c
(gdb) x/10x 0x13fcb8a8 <--- ebp
0x13fcb8a8: 0x13fcb8e8 0x00369f48 0xff000000 0x13fcb928
0x13fcb8b8: 0x00000002 0x00000000 0x00000000 0x00000000
0x13fcb8c8: 0x25241000 0x02ecf4a4
(gdb) x/10x 0x25241000 <--- our binary
0x25241000: 0xbebafeca 0x02000040 0x07000000 0x03000000
0x25241010: 0x00100000 0xd433b500 0x0c000000 0x12f40000
0x25241020: 0x00000000 0x00500000
(gdb) x/20 0x25241fd0 <--- near the end
0x25241fd0: 0x00000000 0x00000000 0x00000000 0x00000000
0x25241fe0: 0x00700000 0x00000000 0x00000000 0xbd000000
0x25241ff0: 0x00000000 0x00000000 0x00000000 0x00000000
0x25242000: Cannot access memory at address 0x25242000 <--- out of bounds
--------------------- bsd/kern/mach_fat.c
114 /* This is beacuse we are reading only 512 bytes */ <--- typos in a rush?
115
116 if (end_of_archs > 512) <--- :-)
117 return(LOAD_BADMACHO);
118 /*
119 * Round size of fat_arch structures up to page boundry.
120 */
121 size = round_page_32(end_of_archs);
122 if (size == 0)
123 return(LOAD_BADMACHO);
124
125 /*
126 * Scan the fat_arch's looking for the best one.
127 */
128 addr = data_ptr;
129 best_arch = NULL;
130 best_grade = 0;
131 arch = (struct fat_arch *) (addr + sizeof(struct fat_header));
132 for (; nfat_arch-- > 0; arch++) {
133
134 /*
135 * Check to see if right cpu type.
136 */
137 if(((cpu_type_t)NXSwapBigIntToHost(arch->cputype) & ~mask_bits) != req_cpu_type)
138 continue;
139
140 /*
141 * Get the grade of the cpu subtype.
142 */
143 grade = grade_binary(
144 NXSwapBigIntToHost(arch->cputype),
145 NXSwapBigIntToHost(arch->cpusubtype));
146
147 /*
148 * Remember it if it's the best we've seen.
149 */
150 if (grade > best_grade) {
151 best_grade = grade;
152 best_arch = arch;
153 }
154 }
--------------------- bsd/kern/mach_fat.c
|