timwr / CVE-2016-5195

CVE-2016-5195 (dirtycow/dirtyc0w) proof of concept for Android
942 stars 392 forks source link

How do you spawn a shell after exploit? #9

Closed naikel closed 6 years ago

naikel commented 7 years ago

I've tried execpl, system, even executing chmod(const char *pathname, mode_t mode) in the code but nothing seems to work.

I do get getuid() == 0, but I can't do anything with that privilege.

EDIT: My device does NOT have /system/bin/run-as with setuid and I have seen NO device that has it set.

joel0 commented 7 years ago

The setuid bit was changed in Android 4.3(?) to Linux capabilities. run-as has the Linux capability to set the UID to 0 (root), but in my experimenting, it seems to lack other useful capabilities, such as writing files.

I would start by looking at the AOSP implementation of run-as and finding how it executes the packages and remove some safety checks.

rhcp011235 commented 7 years ago

You need to disable SELinux first

naikel commented 7 years ago

10-24 23:40:22.933 10954 10954 W run-as : type=1400 audit(0.0:728): avc: denied { setenforce } for uid=0 scontext=u:r:runas:s0 tcontext=u:object_r:kernel:s0 tclass=security permissive=0

Not that trivial, to me at least.

rhcp011235 commented 7 years ago

what do you mean? You didn't spawn a shell did you?

check the binary - -rwxr-xr-x 1 root shell 18K 2009-01-01 03:00 app_process64

naikel commented 7 years ago

I can't spawn a shell from the run-as context.

I have app_process32 (same thing). It has the zygote context. The one I pasted before was using the runas context. From that context I can't disable SELinux either:

10-24 23:44:58.603 11542 11542 W app_process: type=1400 audit(0.0:738): avc: denied { setenforce } for uid=0 scontext=u:r:zygote:s0 tcontext=u:object_r:kernel:s0 tclass=security permissive=0

lucasa831 commented 7 years ago

what if we just use the exploit to replace install-recovery.sh with a script that paste su into it's folder then overwrites itself to the original superuser install recovery file then it'll load itself

naikel commented 7 years ago

You can't. Remember the whole /system is reconstructed before boot. If you reboot you'll lose all your changes.

lucasa831 commented 7 years ago

what about using it to replace something like the reboot binary (that may be ran as root) with a binary that mount system as rw then paste everything needed by superuser

naikel commented 7 years ago

The problem is the SELinux context. The best choice is replacing app_process32. You can get root, that's no problem. That's easy.

But then you have a root that can't do anything. When you try to write a file it says denied, because you can only write files that the SELinux context allows you to. That is, for example, the dalvik cache. You can create a file there. Let's say you copy the su there. You run it: SAME PROBLEM. Now you have a shell, with the same SELinux context.

What you want is:

So what else you can do? disable SELinux, so you can do all this. But if you could disable SELinux in a SELinux context, SELinux wouldn't exist in the first place....

I think I lost hope with dirty cow and Android >= 5.

rhcp011235 commented 7 years ago

Jcase has rooted the latest HTC 10 running latest android using this. so i assume there is a way :)

rhcp011235 commented 7 years ago

You can disable selinux in selinux. The init context can enable (or disable) it. So wherever init is. Over write it

Manouchehri commented 7 years ago
PS C:\Users\david> adb shell
shell@flo:/ $ id
uid=2000(shell) gid=2000(shell) groups=2000(shell),1004(input),1007(log),1011(adb),1015(sdcard_rw),1028(sdcard_r),3001(net_bt_admin),3002(net_bt),3003(inet),3006(net_bw_stats) context=u:r:shell:s0
shell@flo:/ $ run-as id
uid=0(root) gid=0(root) groups=0(root),1004(input),1007(log),1011(adb),1015(sdcard_rw),1028(sdcard_r),3001(net_bt_admin),3002(net_bt),3003(inet),3006(net_bw_stats) context=u:r:runas:s0
shell@flo:/ $ ls -lZ /sbin/
opendir failed, Permission denied
1|shell@flo:/ $ run-as ls -lZ /sbin/
-rwxr-x--- root     root              u:object_r:rootfs:s0 adbd
-rwxr-x--- root     root              u:object_r:rootfs:s0 healthd
lrwxrwxrwx root     root              u:object_r:rootfs:s0 ueventd -> ../init
lrwxrwxrwx root     root              u:object_r:rootfs:s0 watchdogd -> ../init
shell@flo:/ $ cat /init.flo.diag.rc
/system/bin/sh: cat: /init.flo.diag.rc: Permission denied
1|shell@flo:/ $ run-as cat /init.flo.diag.rc
# This file gets copied as /init.flo.diag.rc

on post-fs-data
    rm /dev/diag
naikel commented 7 years ago

The problem is this:

shell@flo:/ $ run-as id uid=0(root) gid=0(root) groups=0(root),1004(input),1007(log),1011(adb),1015(sdcard_rw),1028(sdcard_r),3001(net_bt_admin),3002(net_bt),3003(inet),3006(net_bw_stats) context=u:r:runas:s0

That's a useless context. Can you execute something in, say, /data/local/tmp as root using that context?

Or better yet, can you remount /system as rw?

Because I couldn't. I can read files like you, but I can write/execute anything.

Using the app_process32 (zygote) context I can do a little more, but at the end of the day they are still useless things.

EDIT: Show me your selinux_android_setcontext() in your run-as.c ;)

Manouchehri commented 7 years ago

Pick a different target so the context isn't an issue anymore.

The answer is here. https://source.android.com/security/selinux/implement.html

GeneBlue commented 7 years ago

So, how to bypass selinux? Could you give some hint? @Manouchehri

ghost commented 7 years ago

can you post the source code of the run-as you are using to get root access?

naikel commented 7 years ago

In my device these are the only targets I can pick:

shell@victara:/system/bin $ ( ls -lZ 2>/dev/null ) | grep -v system_file
-rwxr-xr-x root     shell             u:object_r:zygote_exec:s0 app_process32
-rwxr-xr-x root     shell             u:object_r:dex2oat_exec:s0 dex2oat
-rwxr-xr-x root     shell             u:object_r:logcat_exec:s0 logcat
-rwxr-xr-x root     shell             u:object_r:motobox_exec:s0 motobox
-rwxr-xr-x root     shell             u:object_r:dex2oat_exec:s0 patchoat
-rwxr-x--- root     shell             u:object_r:runas_exec:s0 run-as
-rwxr-xr-x root     shell             u:object_r:shell_exec:s0 sh
-rwxr-xr-x root     shell             u:object_r:toolbox_exec:s0 toolbox
-rwxr-xr-x root     shell             u:object_r:toolbox_exec:s0 toybox

The best context is zygote_exec, but still not very useful.

Manouchehri commented 7 years ago

You have access to way more files than just /system/bin/.

run-as is just spawning toybox there, still limited by SELinux.

rhcp011235 commented 7 years ago

We will need to kill SELinux though to get a real shell

naikel commented 7 years ago

What about rewriting the SELinux files like /file_contexts and somehow running "restorecon"... that would "kill" it.

The zygote process (app_process32) has a lot of privileges. Somebody could get an idea by just reading the zygote.te file (you can Google that if you don't want to download the whole Android source).

Interesting things are:

# Override DAC on files and switch uid/gid.
allow zygote self:capability { dac_override setgid setuid fowner };

# Switch SELinux context to app domains.
allow zygote system:process dyntransition;

allow zygote dalvikcache_data_file:file { create_file_perms x_file_perms };

# Read /seapp_contexts and /data/security/seapp_contexts
security_access_policy(zygote)

And many many other things.

SquallATF commented 7 years ago

can we replace kernel module and find a way to trigger insmod to bypass selinux? my device have follow kernel module

shell@F8132:/ $ ls /system/lib/modules/
ansi_cprng.ko
backlight.ko
bcmdhd.ko
br_netfilter.ko
core_ctl.ko
ecryptfs.ko
generic_bl.ko
kscl.ko
lcd.ko
mmc_block_test.ko
mmc_test.ko
msm-buspm-dev.ko
rdbg.ko
spidev.ko
test-iosched.ko
texfat.ko
ufs_test.ko
wil6210.ko

only follow module inserted

shell@F8132:/ $ lsmod  
Module                  Size  Used by
texfat                157455  0
wlan                  871035  0
kscl                   15531  0
ecryptfs               84851  0
alien0x00 commented 7 years ago

@naikel I have an issue when I try to use app_process32 as target. I'm able to apply the "patch" but then, when I try to execute /system/bin/app_process32 I'm not able to become root:

>>> /system/bin/app_process32 Running as uid 2000 Could not set capabilities: Operation not permitted setresgid/setresuid failed

This is weird! The zygote.pe file contains: allow zygote self:capability { dac_override setgid setuid fowner chown }; Could you help me?

naikel commented 7 years ago

Remember to always use logcat | grep audit to know exaclty what is going on.

Same thing happens to me with app_process32, but then I can see in the logs that some other root process executes it... and then the phone GUI dies but the shell through adb shell still works.

But I have other problems since zygote can't execute anything on /system/bin:

10-26 10:58:17.712 30113 30113 W app_process: type=1400 audit(0.0:8400): avc: denied { read } for uid=0 path="/system/bin/sh" dev="mmcblk0p38" ino=371 scontext=u:r:zygote:s0 tcontext=u:object_r:shell_exec:s0 tclass=file permissive=0

alien0x00 commented 7 years ago

On the logcat, as soon as I "patch" app_process32, I can actually see several processes crashing (as normal to be)

I suppose we need to use another process as target, but... which one?

umntkid commented 7 years ago

I'm getting permission denied on everything on Nexus 4 Android 5.0

char *envp[] = { NULL };
  char *argvv[] = { "-l", "/data/local/tmp/", NULL };
  execve("/system/bin/ls", argvv, envp);
  printf("Something went wrong! %s\n", strerror(errno));
adb shell /system/bin/run-as
running as uid 2000
uid 0
euid 0
Something went wrong! Permission denied
naikel commented 7 years ago

@umntkid you should read the thread first before posting... We already stated u:r:runas:s0 context has no execute permission. You can't do anything in that context.

rhcp011235 commented 7 years ago

Not 100% true.

@naikel @umntkid this works just fine:

include

include

include <sys/capability.h>

include

int main(int argc, char *argv) { DIR dir; struct __user_cap_header_struct capheader; struct __user_cap_data_struct capdata[2]; struct dirent *ent;

printf("running as uid %d\n", getuid());

memset(&capheader, 0, sizeof(capheader)); memset(&capdata, 0, sizeof(capdata)); capheader.version = _LINUX_CAPABILITY_VERSION_3; capdata[CAP_TO_INDEX(CAP_SETUID)].effective |= CAP_TO_MASK(CAP_SETUID); capdata[CAP_TO_INDEX(CAP_SETGID)].effective |= CAP_TO_MASK(CAP_SETGID); capdata[CAP_TO_INDEX(CAP_SETUID)].permitted |= CAP_TO_MASK(CAP_SETUID); capdata[CAP_TO_INDEX(CAP_SETGID)].permitted |= CAP_TO_MASK(CAP_SETGID); if (capset(&capheader, &capdata[0]) < 0) { printf("Could not set capabilities: %s\n", strerror(errno)); }

if(setresgid(0,0,0) || setresuid(0,0,0)) { printf("setresgid/setresuid failed\n"); }

if ((dir = opendir ("/sbin/")) != NULL) { while ((ent = readdir (dir)) != NULL) { printf ("%s\n", ent->d_name); } closedir (dir); } return 0; }

naikel commented 7 years ago

Yes, you can read files in the runas context.

Manouchehri commented 7 years ago

You can use u:r:runas:s0 to run dirtycow recursively for fun, so you can modify /init*rc files that way (but of course that isn't persistent, probably not what you want).

ghost commented 7 years ago

@Manouchehri did you manage to write in /dev/block or to completely root a stock locked android 6.0 Selinux phone?

naikel commented 7 years ago

The recursive ability of dirtycow within the u:r:runas:s0 context is an immediate conclusion of @rhcp011235 comment (that is running dirtycow inside run-as executable to be able to modify other files that the shell user couldn't read).

But I would not do that for the init files, that's just useless. We can already modify those files running dirtycow once, but they will never be executed.

ghost commented 7 years ago

We can use dirtycow to modify libc.so or other shared libraries, it will effect all current running processes (I guess).

naikel commented 7 years ago

Or to modify /system/bin/linker to try to spawn a shell via socket only if it's in the u:r:kernel:s0 context before linking the executable and hope for the best...

ghost commented 7 years ago

I'm thinking at a version on Android of https://gist.github.com/scumjr/17d91f20f73157c722ba2aea702985d2

naikel commented 7 years ago

@xcodevn we already tested that and I thought I already explained it (maybe in another thread?). That doesn't work because if you read the code you'll see that it patches libc.so to make getuid() = 0, so when you execute su (the Linux su binary) it doesn't ask for a password.

For that exploit you need:

And that's not how it works on Android.

Arinerron commented 7 years ago

Hmm... Anyone?

DavidBuchanan314 commented 7 years ago

@naikel @xcodevn I ported that version to android: https://github.com/DavidBuchanan314/cowroot

As you say, it's quite useless because there is no su binary.

However, since it lets us patch libc, we can execute arbitrary code in the context of any privilidged process which happens to call into libc. Just need to figure out exactly what to patch...

naikel commented 7 years ago

@DavidBuchanan314 patch /system/bin/linker carefully.

When I say carefully is that the linker should still work, even if it opens a backdoor. The linker is called whenever a process needs any dynamic linked library. It's the equivalent of /lib/ld-linux.so for x86.

This is exactly what I'm trying to do but unfortunately to do this I have to download the whole Android source and build it for ARM, and it's taking a while.

naikel commented 7 years ago

Check this guy's approach: https://github.com/jcadduono/android_external_dirtycow

He patches app_process to change the system property to start a flash recovery service. He also patches /system/bin/applypatch to flash his custom recovery. After that you reboot the phone in recovery and your custom recovery is loaded and install su binary from there.

Haven't tested it myself, though.

alien0x00 commented 7 years ago

I tried and I wasn't able to set the selinux context as u:r:system_server:s0...

naikel commented 7 years ago

@alien0x00 what version of Android are you testing it? It should work on >= 6.0:

# Switch SELinux context to app domains.
allow zygote self:process setcurrent;
allow zygote system_server:process dyntransition;
alien0x00 commented 7 years ago

I tried on a Nexus 6p with Android 6.0.1:

shell@angler:/ $ /system/bin/app_process64
***********************************************
*   wotzit doing this ain't no app_process64  *
***********************************************
Current selinux context: u:r:shell:s0
Unable to set security context to 'u:r:system_server:s0' (ret = -1)!

This is the logcat message: W app_process: type=1400 audit(0.0:140): avc: denied { setcurrent } for scontext=u:r:shell:s0 tcontext=u:r:shell:s0 tclass=process permissive=0

naikel commented 7 years ago

You are doing it wrong... Is your phone 64 bits? because you're running it on the u:r: shell:s0 context, and it should run on the u:r:zygote:s0 context.

What's the binary of your zygote context? (grep zygote /file_contexts)

EDIT: You are not supposed to call app_process from the shell!!! You just have to wait it runs itself looking at the logcat!

ghost commented 7 years ago

how you trigger app_process?

naikel commented 7 years ago

@christianrodher you don't. It spawns itself. You just have to look at the logcat and wait a couple of seconds.

turnerrocks1 commented 7 years ago

solution is to include execl("/system/bin/sh", "sh", "-i", NULL);

uid_t uid=getuid(), euid=geteuid(); if (uid<0 || uid!=euid) { /* We might have elevated privileges beyond that of the user who invoked * the program, due to suid bit. Be very careful about trusting any data! */ } else { printf("we got root!"); } return 0; }

btw working on the browser base root the domain is UNIXbrowserroot.com and it will have a database and javascript im done with all all i need to do is find a source code of previous remote code execution with js to execute the c from the database i popped a root shell but when i used gcc it worked on linux and 32 bit from the day one code with little modifications but when i tried to execute on 64 bit instead of 32 bit arm android it said cant execute 64 bit elf file so that means i need a ndk compiled version but i deleted that after i popped a root shell and pushed a boot.img and rebooted the phone with js from the c code and a little adb sided client tcp i was able to boot into it and remount the file system as rw pushed su and pwn.sh previously from weaksauce decompiled apk and chmodded both as 777 then i mounted all as ro but i realized if i reboot all of this would be worthless because we are just booted into stock boot.img that is world 777 ex. read,write, and execute and is only temporary like a cache and will be cleard on boot so i add another trick up my sleeve i used the .sh files from supersu.zip extracted them to the raw file system that holds all files and subsystems because the boot.img i was booted into was world writeable and so once on i change the init.rc and install-recovery.sh and added the execution of these new untethered root shell commands inside sh to be executed on boot and last but not least once finished i uses dd command to replace dd=/dev/mmcblk0p10 to replace to the re and i rebooted and i ran adb and id was 0 and i ranned "su" and it came with the # i knew this was tnot the last step i needed this opertunity to replace the install-recovery.sh and i used the sh from supersu.zip replaced it because they have the same name and chmod 775 and i was finished i rebooted and ranned root checker and it was fully functional

naikel commented 7 years ago

@turnerrocks1 you should read the thread before posting...

turnerrocks1 commented 7 years ago

@naikel i did and if you read before posting.............

refi64 commented 7 years ago

@turnerrocks1 Could you separate your sentences a bit? That's kinda hard to read...

turnerrocks1 commented 7 years ago

have you tried // Set SELinux to permissive private static final String COMMAND = "su 0 setenforce 0"; try { Runtime.getRuntime().exec(COMMAND); } catch (IOException e) { e.printStackTrace(); } to disable selinux alltogether