[Click Android Development] bftb0:
If you write the milw0rm exploit as pseudo-code, you'll notice something:

main() {
fork
if (notparent) { exec_suid_proggie }
else { // must be the parent
if ( attach ) {
wait ;
if ( name_looks_ok ) { getregs; poke ; setregs ; detach ; wait }
else { /* MISSING */ }
}
else { /* nothing needed here */ }
}
}

Looking at the docs, it appears that (ptrace()) CONT is not needed - DETACH should have the same effect as signaling the child with SIGCONT. Note ( /* MISSING */ ) that the original code didn't properly release a child process when it was attached, but failed the name inspection test. When I ran it "as is", I ended up with lots of zombie processes (waiting for a signal that never arrives). Some of those may have had root credentials - I remember at one point seeing both "shell" and "root" proceses.

That can be fixed up - replace /* MISSING */ with kill(child, SIGCONT);

I agree with about 99.9% of what you say here - and the other 0.1% I don't really have enough information to have a meaningful opinion. ;)

I played with reading arbitrary lengths of the stack (+ offsets) - say 500 to 1000 bytes - to see if I could infer the frame layouts (to try a return pointer sploit) - and would notice that I would occasionally get PEEK failures in the middle of an otherwise working range of addresses (I would also occasionally run off the end of the stack too, and every address would fail in that case, not just a single word). The only thing that I can conclude about that is that the kernel is in a state where it can not provide an answer - the child is completely stopped, so it must be a kernel state issue - nothing to do with the child.

To re-iterate, I have never successfully injected shellcode (in the sense of correctly launching execve() via the shellcode)- even with a non suid executable called in the child process... and using shellcodes which are confirmed to operate correctly when you just call them directly in the parent. The one additional tidbit of information that I can provide here is that when injecting code near seg.seg.ARM_pc is that it does has an effect on the child process - usually it just dies silently rather than running to completion, but other times you will actually see a SIGBUS diagnostic in the system logs (On the Linux/Windows box, run "adb logcat" in a separate window while messing around on the phone).

The SIGBUS thing - or "failure to finish normally" suggests that the injected code is having some effect on the child - I would think that with a page protection mechanism we'd see a different fault (SIGBUS is usually an alignment error, not a protection error).

You might be right here - although I will say that if we are exploiting a race condition, it may not be evident from examining the output of "ps" what credentials a process has - or will acquire. There are defenses built into the kernel to avoid granting setuid based on a process' trace status - and also protections preventing trace attachments for processes that are already setuid.

kernel, behalf of child : if ( ! traced ) then { XX allow_tracing=0; setuid }

kernel, behalf of parent : if ( allow_tracing ) then { traced=1;}

That's sort of a grossly simplified conceptual sketch - obviously, locks need to be placed on shared variables... but the point is that the race happens in a small window of time (XX) after the "go to setuid" code in the kernel has already checked to see if the process is being traced, but before it has excluded other processes from tracing it. But... you are correct that if you somehow managed to attach the child before it has acquired setuid... you would need to inject code in such a way that it would run... umm... "later, not right now" :(

So anyway, a tiny glimmer of hope. I don't think it is an easy road, though. Note that if you pull the setuid binary off the phone (my case - Eris - /system/bin/ip), it appears to be compiled with stack protection techniques. (Inspect with arm-eabi-readelf) That might preclude simple stack smash and run jobs. And since the granting of process privileges based on attributes of the filesystem in which an executable it is stored is the job of of the loader/linker - none of the (dynamic) system libraries will be loaded yet, and the code won't be mapped for execution yet. It seems that polluting the mapped code space with shellcode could work... maybe... but that isn't going to be a quick hack.

Two more tidbits of information - I injected two words of code into an unprivileged child ( mov r7, #64 ; sti 0x0 ) and then watched the code with a ptrace SYSCALL... CONT loop. The child got stuck in an infinite loop! By itself, that means very little, and isn't relevant to the root-euid case - but it does seem to indicate that code pages can be modified.

I'm obviously still doing something wrong - unprivileged native processes can be debugged on the Eris, and that suggests that code modifications - breakpoint insertions at a minimum - can be injected. I'm wondering if I'm missing something, or if this is a difference due to me being on the Eris and zanfur being on the Tattoo.

zanfur - throw me a bone - where are you injecting your code - at the PC?

BTW, one other useful piece of information (but not for this specific case) - the NDK has the "gdbserver" code already compiled - you can debug native apps on the phone over TCP** (e.g. wifi): just push the gdbserver app over to the phone, and start your native app with it

push $(NDK)/build/prebuilt/linux-x86/arm-eabi-4.2.1/bin/gdbserver to your writeable, execable fs on the phone.

phone$ ./gdbserver anystring:2345 ./mynativeapp

PC$ arm-eabi-gdb ./mynativeapp

(gdb) target remote 192.168.1.3:2345

The "arm-eabi-gdb" app on the host PC actually does all the symbol reading, not the "gdbserver" stub running on the phone.

This isn't useful for this particular piece of code - tracing the parent interferes with tracing of the child - but I thought I would throw it out there, since someone else mentioned "it would be nice to have a debugger on the phone".

greets

bftb0

** In principle, this means that zanfur could debug an app running on my phone from across the globe - I just tried it, and was able to debug using the Mobile Network (I guess there's no firewall on my Eris.).