The iPhone Wiki is no longer updated. Visit this article on The Apple Wiki for current information. |
Difference between revisions of "Incomplete Codesign Exploit"
(comex is a Mach-o man :)) |
m (→Initializers exploit (pf2/hfs legacy): Minor edits.) |
||
Line 50: | Line 50: | ||
In iOS 4.1, dyld does a range check on the interposition targets to make sure that a dylib only redirects symbols to its own code segments, preventing the use of this feature to control code flow (since we cannot have executable code segments without a valid signature). |
In iOS 4.1, dyld does a range check on the interposition targets to make sure that a dylib only redirects symbols to its own code segments, preventing the use of this feature to control code flow (since we cannot have executable code segments without a valid signature). |
||
− | == Initializers exploit ( |
+ | == Initializers exploit (Packet Filter/HFS Legacy Volume Name)== |
For the iOS 4.1 [[Packet Filter Kernel Exploit]], comex introduced another technique to get code execution, still using libgmalloc.dylib but in a less convoluted manner. |
For the iOS 4.1 [[Packet Filter Kernel Exploit]], comex introduced another technique to get code execution, still using libgmalloc.dylib but in a less convoluted manner. |
||
− | A Mach-O binary can declare an initializers section holding function pointers to be called upon loading (just like the ELF constructors section). This feature allows immediate control of the instruction pointer. Initializers calls are made in <code>ImageLoaderMachO::doModInitFunctions()</code> (note that the |
+ | A Mach-O binary can declare an initializers section holding function pointers to be called upon loading (just like the ELF constructors section). This feature allows immediate control of the instruction pointer. Initializers calls are made in <code>ImageLoaderMachO::doModInitFunctions()</code> (note that the iOS version of dyld is slightly different than the open-source version). The following code shows this function on iOS 4.1 : |
<pre> |
<pre> |
||
Line 79: | Line 79: | ||
</pre> |
</pre> |
||
− | Unlike Spirit and Star kernel exploits, the [[Packet Filter Kernel Exploit]] is not done in the ROP payload. Instead, the ROP payload is shorter and performs the following calls to run the exploit in an unsigned binary : |
+ | Unlike Spirit's and Star's kernel exploits, the [[Packet Filter Kernel Exploit]] is not done in the ROP payload. Instead, the ROP payload is shorter and performs the following calls to run the exploit in an unsigned binary : |
<pre> |
<pre> |
||
Line 93: | Line 93: | ||
Setting the <code>security.mac.proc_enforce</code> and <code>security.mac.vnode_enforce</code> variables to 0 allows running unsigned binaries, with some side effects (see [http://www.saurik.com/id/8]). The <code>vnode_enforce</code> is reset to 0 as soon as the kernel exploit completes. In iOS 4.3 beta, those variables are now read only. |
Setting the <code>security.mac.proc_enforce</code> and <code>security.mac.vnode_enforce</code> variables to 0 allows running unsigned binaries, with some side effects (see [http://www.saurik.com/id/8]). The <code>vnode_enforce</code> is reset to 0 as soon as the kernel exploit completes. In iOS 4.3 beta, those variables are now read only. |
||
− | Starting with iOS 4.2.1, dyld does a range check on the initializers so that the previous trick does not work (look for the <code>"dyld: ignoring out of bounds initializer function %p in %s"</code> string). However, for some unknown reason this check is only made if <code>ImageLoaderMachO::isDylib()</code> returns true. Hence, in [[Greenpois0n_(jailbreak)|Greenpois0n]] RC5, a crafted executable with an initializers section was used to replace the launchd binary and kickstart [[User:pod2g|pod2g]]'s [[ |
+ | Starting with iOS 4.2.1, dyld does a range check on the initializers so that the previous trick does not work (look for the <code>"dyld: ignoring out of bounds initializer function %p in %s"</code> string). However, for some unknown reason this check is only made if <code>ImageLoaderMachO::isDylib()</code> returns true. Hence, in [[Greenpois0n_(jailbreak)|Greenpois0n]] RC5, a crafted executable with an initializers section was used to replace the launchd binary and kickstart [[User:pod2g|pod2g]]'s [[HFS Legacy Volume Name Stack Buffer Overflow]] kernel exploit. The original launchd binary is renamed to punchd and is run as soon as the kernel exploit is done. |
In iOS 4.2.1 dyld the <code>inits</code> pointer is not stored in R11 anymore but at [SP+4] : |
In iOS 4.2.1 dyld the <code>inits</code> pointer is not stored in R11 anymore but at [SP+4] : |
||
Line 127: | Line 127: | ||
* the second initializer pointer has to be stored at offset 0x1004 (segPreferredLoadAddress(0) + 4) |
* the second initializer pointer has to be stored at offset 0x1004 (segPreferredLoadAddress(0) + 4) |
||
* a pointer to a return 0 gadget must be present at offset 0x78 in the Mach-O file (context.programVars->mh[0x78]) |
* a pointer to a return 0 gadget must be present at offset 0x78 in the Mach-O file (context.programVars->mh[0x78]) |
||
− | |||
== Sources for information == |
== Sources for information == |
Revision as of 01:23, 14 February 2011
Incomplete Codesign is a technique introduced by Comex in the Spirit jailbreak that allows untethered userland code execution. The idea is to plant a crafted Mach-O binary on the filesystem and have it loaded early during the boot process. This technique must be used in conjunction with another exploit to first plant the binary on the filesystem (like the MobileBackup Copy Exploit used in Spirit, or one of the DFU mode exploits Pwnage 2.0/Steaks4uce/Limera1n). Since executable pages must be signed, the crafted binary will have to abuse the loader or the dynamic linker functionalities to transfer execution to a ROP payload that will use existing (signed) code fragments (gadgets). The endgame is to have the userland code trigger and exploit a kernel vulnerability to achieve the jailbroken state.
Contents
Credit
Interposition exploit (Spirit & Star)
The first technique used in the Spirit and Star jailbreaks involves loading a custom shared library (dylib) in the first userland process (launchd). The library is loaded using the launchd libgmalloc debugging feature that can be enabled by creating the /var/db/.launchd_use_gmalloc
file.
if (pid1_magic && g_use_gmalloc) { if (!getenv("DYLD_INSERT_LIBRARIES")) { setenv("DYLD_INSERT_LIBRARIES", "/usr/lib/libgmalloc.dylib", 1); setenv("MALLOC_STRICT_SIZE", "1", 1); execv(argv[0], argv); } else { unsetenv("DYLD_INSERT_LIBRARIES"); //this call is hijacked through interposition unsetenv("MALLOC_STRICT_SIZE"); } }
The crafted libgmalloc.dylib does not contains any executable segments, but instead uses the dyld interposition feature to redirect several exported functions to code fragments in the launchd binary. The interposed functions and their replacement addresses are chosen to force launchd to perform a stack pivot, and have SP pointing to a data segment in the shared library, allowing ROP code execution. The following functions are interposed to allow the stack pivot : _unsetenv, _launch_data_new_errno, _setrlimit, __exit, _audit_token_to_au32, _launch_data_unpack, _launch_data_dict_iterate
(a few other functions are also interposed to create some gadgets used by the ROP payload).
Once launchd has restarted itself with the crafted libgmalloc.dylib, the unsetenv function call will execute the following "interposition gadgets" :
LDR R0, =aDyld_insert_li ; "DYLD_INSERT_LIBRARIES" BL _unsetenv LDR R0, [R0] #R0 = 0x444c5944 = "DLYD" = little endian "DYLD" BL _launch_data_new_errno MOV R0, R0, LSR#2 #R0 = R0 / 4 BL _setrlimit ADD R0, R0, #3 #R0 = R0 + 3 BL __exit LDMIA R0, {R0-R3} #R0 = 0x11131654 (__heap section in libgmalloc.dylib =[0,0, 0x1113000C, STACK_PIVOT_GADGET) BL _audit_token_to_au32 STR R2, [SP+4] #R2 = 0x1113000C BL _launch_data_unpack STR R3, [SP+8] #R3 = STACK_PIVOT_GADGET BL _launch_data_dict_iterate LDMFD SP!, {R4,R7,PC} #=> R7 = 0x1113000C, PC = STACK_PIVOT_GADGET STACK_PIVOT_GADGET: SUB SP, R7, #0xC #SP = 0x11130000 (start of ROP stack in __heap section) LDMFD SP!, {R4-R7,PC} #ROP starts here
The ROP payloads in Spirit and Star exploit respectively the BPF and IOSurface kernel vulnerabilities in order to patch the kernel, and then restart launchd to continue the normal boot process.
In iOS 4.1, dyld does a range check on the interposition targets to make sure that a dylib only redirects symbols to its own code segments, preventing the use of this feature to control code flow (since we cannot have executable code segments without a valid signature).
Initializers exploit (Packet Filter/HFS Legacy Volume Name)
For the iOS 4.1 Packet Filter Kernel Exploit, comex introduced another technique to get code execution, still using libgmalloc.dylib but in a less convoluted manner.
A Mach-O binary can declare an initializers section holding function pointers to be called upon loading (just like the ELF constructors section). This feature allows immediate control of the instruction pointer. Initializers calls are made in ImageLoaderMachO::doModInitFunctions()
(note that the iOS version of dyld is slightly different than the open-source version). The following code shows this function on iOS 4.1 :
__text:2FE0BFE6 LDR.W R6, [R11,R5,LSL#2] ; Initializer func = inits[i]; __text:2FE0BFEA CBZ R3, loc_2FE0BFFA __text:2FE0BFEC LDR R3, [SP,#0x30+var_2C] __text:2FE0BFEE LDR R0, =(aDyldCallingIni - 0x2FE0BFF6) __text:2FE0BFF0 MOV R1, R6 __text:2FE0BFF2 ADD R0, PC ; "dyld: calling initializer function %p i"... __text:2FE0BFF4 LDR R2, [R3,#4] __text:2FE0BFF6 BL __ZN4dyld3logEPKcz ; dyld::log(char const*,...) __text:2FE0BFFA __text:2FE0BFFA loc_2FE0BFFA __text:2FE0BFFA ADD.W R12, R4, #0x58 __text:2FE0BFFE LDR R0, [R4,#0x44] __text:2FE0C000 LDR R1, [R4,#0x48] __text:2FE0C002 LDR R2, [R4,#0x4C] __text:2FE0C004 LDR R3, [R4,#0x50] __text:2FE0C006 STR.W R12, [SP] __text:2FE0C00A BLX R6 ; func(context.argc, context.argv, context.envp, context.apple, &context.programVars)
Since R11 points to the start of the section containing the initializers function pointers (inits
), comex uses the following uncommon gadget to perform the stack pivot :
0x499ba000 LDMIBMI R11, {SP, PC} #increments R11 by 4, then pops SP and PC
Unlike Spirit's and Star's kernel exploits, the Packet Filter Kernel Exploit is not done in the ROP payload. Instead, the ROP payload is shorter and performs the following calls to run the exploit in an unsigned binary :
int zero = 0; char *params[] = {"/usr/lib/pf2", NULL}; char *env[] = {NULL}; /* these 3 function calls are done as ROP */ sysctlbyname("security.mac.proc_enforce", NULL, 0, &zero, sizeof(zero)); sysctlbyname("security.mac.vnode_enforce", NULL, 0, &zero, sizeof(zero)); execve("/usr/lib/pf2", params, env);
Setting the security.mac.proc_enforce
and security.mac.vnode_enforce
variables to 0 allows running unsigned binaries, with some side effects (see [1]). The vnode_enforce
is reset to 0 as soon as the kernel exploit completes. In iOS 4.3 beta, those variables are now read only.
Starting with iOS 4.2.1, dyld does a range check on the initializers so that the previous trick does not work (look for the "dyld: ignoring out of bounds initializer function %p in %s"
string). However, for some unknown reason this check is only made if ImageLoaderMachO::isDylib()
returns true. Hence, in Greenpois0n RC5, a crafted executable with an initializers section was used to replace the launchd binary and kickstart pod2g's HFS Legacy Volume Name Stack Buffer Overflow kernel exploit. The original launchd binary is renamed to punchd and is run as soon as the kernel exploit is done.
In iOS 4.2.1 dyld the inits
pointer is not stored in R11 anymore but at [SP+4] :
__text:2FE0C03C loc_2FE0C03C __text:2FE0C03C LDR R3, [SP,#4] __text:2FE0C03E MOV R0, R6 __text:2FE0C040 LDR.W R4, [R3,R8,LSL#2] ;Initializer func = inits[i]; __text:2FE0C044 LDR R3, [R6] __text:2FE0C046 LDR R3, [R3,#0x78] __text:2FE0C048 BLX R3 ; ImageLoaderMachO::isDylib(void) __text:2FE0C04A CMP R0, #0 __text:2FE0C04C BEQ loc_2FE0C0EE ;bypass range check ... __text:2FE0C088 loc_2FE0C088 __text:2FE0C088 ADD.W R12, R5, #0x5C __text:2FE0C08C LDR R0, [R5,#0x48] __text:2FE0C08E LDR R1, [R5,#0x4C] __text:2FE0C090 LDR R2, [R5,#0x50] __text:2FE0C092 LDR R3, [R5,#0x54] __text:2FE0C094 STR.W R12, [SP] ; &context.programVars->mh __text:2FE0C098 BLX R4 ; func(context.argc, context.argv, context.envp, context.apple, &context.programVars)
The stack pivot is done using two initializers :
POP {R6,R7} ; BX LR #R6=&context.programVars->mh, R7=inits SUB SP, R7, #0 ; POP {R7,PC} #do the stack pivot
Since the first initializer clobbers R6 and shuffles the local variables by incrementing SP by 8, some conditions must be met for dyld to reach the second initializer call without crashing :
- the second initializer pointer has to be stored at offset 0x1004 (segPreferredLoadAddress(0) + 4)
- a pointer to a return 0 gadget must be present at offset 0x78 in the Mach-O file (context.programVars->mh[0x78])
Sources for information
- https://github.com/comex/spirit/blob/master/igor/one.py
- https://github.com/comex/spirit/blob/master/igor/configdata.py
- http://books.google.fr/books?id=K8vUkpOXhN4C&lpg=PA73&ots=OJqiYTUwVD&dq=dyld%20interpose&pg=PA73#v=onepage&q&f=false
- http://launchd.macosforge.org/trac/browser/trunk/launchd/src/launchd.c
- http://blogs.embarcadero.com/eboling/2010/01/29/5639/
- http://opensource.apple.com/source/dyld/dyld-132.13/src/ImageLoaderMachO.cpp
- https://github.com/comex/starn/blob/43989121a0f74639cf8cc3aa57514e6ef0c97dbd/goo/one.py
- https://github.com/comex/starn/blob/43989121a0f74639cf8cc3aa57514e6ef0c97dbd/config/configdata.py