TL; DR: This is a question about the final step, in a portable, developer-oriented rooting process, that works across all Android machines. It is not based on any exploit - it is something that we are legally and morally allowed to do, as developers, to our own machines. If I get an answer and manage to chroot inside my Debian, I will make a concise blog post detailing all the steps of this process for all the fellow developers that want root access to their tablets - and don't want to trust dubious-origin "one-click-roots" that do God-knows-what to their machines (botnet members?)... The only dependencies will be the machine's kernel sources (which the manufacturer is legally obligated to provide) and the boot partition image (boot.img
), which is 99% of the times inside the manufacturer-provided Over-the-air updates, or individually downloadable as a standalone flash-able image.
So, a week passed where I spent all my free time on my new Android tablet.
And I have almost completely succeeded - in creating a portable, developer-oriented process, for achieving root in my Android 5.0.2 tablet.
But there's one thing missing yet - I can't do a chroot (which I need to run my debootstrap
-ed Debian!)
What I did so far
- First, I did a minor patch in my tablet's (manufacturer-provided) kernel sources, and then compiled my own kernel - where I disabled the checks for changing SELINUX enforcing mode. Specifically...
In security/selinux/selinuxfs.c
:
...
if (new_value != selinux_enforcing) {
/* Commented out by ttsiodras.
length = task_has_security(current, SECURITY__SETENFORCE);
if (length)
goto out;
*/
audit_log(current->audit_context, GFP_KERNEL, AUDIT_MAC_STATUS,
"enforcing=%d old_enforcing=%d auid=%u ses=%u",
new_value, selinux_enforcing,
I then changed my initrd image's
/default.prop
to contain:ro.secure=0
andro.debuggable=1
Since my manufacturer's
initrd.img
was missing it, I also compiledsu.c
from https://android.googlesource.com/platform/system/extras/+/master/su/ and placed the resulting binary under/sbin/su
, making sure it is set to SUID root (chmod 04755 /sbin/su
).
After that, I packaged the new kernel and the new initrd, as I explained in Episode 2 of my previous post - and booted from my own image:
adb reboot boot-loader ; fastboot boot myboot.img
So, are you root?
Yes, it initially appeared to be successful:
$ adb shell
shell@K01E_2:/ $ id
uid=2000(shell) gid=2000(shell) groups=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@K01E_2:/ $ ls -l /sbin/su /sbin/_su
-rwxr-xr-x root root 131 2015-10-03 10:44 su
-rwsr-xr-x root root 9420 2015-10-03 01:31 _su
(the _su is the binary I compiled, set to SUID root, and "su" is
a script I wrote to tell "su" to add me to all these groups...)
shell@K01E_2:/ $ cat /sbin/su
#!/system/bin/sh
export PATH=/system/bin:$PATH
exec /sbin/_su 0,0,1000,1028,2000,2001,1004,1007,1011,1015,\
1028,3001,3002,3003,3006
And I have now achieved root:
shell@K01E_2:/ $ su
root@K01E_2:/ # id
uid=0(root) gid=0(root)
groups=1000(system),1004(input),1007(log),1011(adb),
1015(sdcard_rw),1028(sdcard_r),1028(sdcard_r),2000(shell),2001(cache),
3001(net_bt_admin),3002(net_bt),3003(inet),3006(net_bw_stats)
context=u:r:shell:s0
I am 100% sure I am root - not only because id
says so, but because I can also do things that normal processes definitely can't:
root@K01E_2:/ # ls -l /dev/block/platform/msm_sdcc.1/by-name/boot
lrwxrwxrwx root root 2015-10-03 10:47 boot -> /dev/block/mmcblk0p16
root@K01E_2:/ # dd if=/dev/block/mmcblk0p16 of=/dev/null bs=1M
16+0 records in
16+0 records out
16777216 bytes transferred in 0.569 secs (29485441 bytes/sec)
Lo and behold - I can finally read raw partitions out of my tablet!
And SELinux is indeed in "down, dog" mode:
root@K01E_2:/ # getenforce
Permissive
But... there are still things I can't do:
root@K01E_2:/ # mkdir /my_mnt
root@K01E_2:/ # mount -t ext4 /dev/block/mmcblk1p2 /my_mnt
mount: Operation not permitted
That is, I can't mount my EXT4-fs formatted 2nd partition of my external SD card.
I also can't chroot to my lovely debootstrap
-ed Debian:
root@K01E_2:/ # chroot /data/debian/ /bin/bash
chroot() fail
Operation not permitted
Is it because of SELinux?
I don't know - I am new (very new - one week old) to SELinux. I thought that when you put it to sleep (getenforce
reporting "Permissive") it no longer interferes...
Apparently, I was wrong. Down the rabbit hole we go again...
Could it be because of my process context?
Remember that id
returned... "uid=0(root) gid=0(root)... context=u:r:shell:s0"
Can I change that context? Being root and all, can I move away from shell
? And if so, move to what?
The answer to the first question is runcon
:
shell@K01E_2:/ $ runcon u:r:debuggerd:s0 /sbin/su
root@K01E_2:/ # id
uid=0(root) gid=0(root)... context=u:r:debuggerd:s0
Good. But what context will allow me to mount
and chroot
?
Reading some more about SELinux, back in my main machine, I parse the /sepolicy
file on the root of the initrd.img
:
linuxbox$ $ sesearch -A sepolicy | grep chroot
allow init_shell init_shell : capability { chown sys_chroot ...
allow init init : capability { chown dac_read_search sys_chroot ...
allow kernel kernel : capability { chown dac_override sys_chroot ...
allow asus-dbug-d asus-dbug-d : capability { chown sys_chroot ...
...
OK, a number of possibilities! Especially that kernel
one seems promising:
shell@K01E_2:/ $ runcon u:r:kernel:s0 /sbin/su
root@K01E_2:/ # id
uid=0(root) gid=0(root)... context=u:r:kernel:s0
root@K01E_2:/ # chroot /data/debian/ /bin/bash
chroot() fail
Operation not permitted
Darn.
Who the heck is blocking me from chroot
ing?
Any advice most welcome...
Answer
Who the heck is blocking me from chrooting?
It wasn't SELinux - that was a wild goose chase (getenforce
returning "Permissive" means that SELinux is indeed no longer in the picture).
The culprit - after adding quite a number of printk
in the kernel's source to trace the failures of both chroot
and mount
- turned out to be capabilities. More specifically, Android's "capability bounding set" - you can read all about them via your man
(man 7 capabilities
) and I confess I have never before bothered looking into them - my everyday UNIX tasks depended on them and I had no idea... try this in your linux box to see for yourself:
$ getfattr -d -m - /sbin/ping
getfattr: Removing leading '/' from absolute path names
# file: sbin/ping
security.capability=0s......
See? Ping is no longer SUID root - it uses information stored in the filesystem's extended attributes to know that it has access to the raw sockets layer (so it can do it's ICMP thing - at the IP level that is).
Anyway, I digress - the surgery point in my kernel where I stopped the "drop my capabilities set" - in an arguably disgusting, "let them all march in" manner - was this (security/commoncap.c
):
static long cap_prctl_drop(struct cred *new, unsigned long cap)
{
if (!capable(CAP_SETPCAP))
return -EPERM;
if (!cap_valid(cap))
return -EINVAL;
// ttsiodras: come in, everyone, the water's fine!
//cap_lower(new->cap_bset, cap);
return 0;
}
This means that capabilities are NEVER dropped - a very secure configuration, indeed :-)
$ adb shell
shell@K01E_2:/ $ su
root@K01E_2:/ # chroot /data/debian/ /bin/bash
root@localhost:/# export PATH=/bin:/sbin:/usr/bin:/usr/sbin:\
/usr/local/bin:$PATH
root@localhost:/# cat /etc/issue
Debian GNU/Linux 8 \n \l
Hello, my sweet Debian :-)
Oh, and "Root checker" works, too - I snipped "su.c", so everyone in my tablet can become root:
int main(int argc, char **argv)
{
struct passwd *pw;
uid_t uid, myuid;
gid_t gid, gids[50];
/* Until we have something better, only root and shell can use su. */
myuid = getuid();
//
// ttsiodras - Oh no, you don't :-)
//
//if (myuid != AID_ROOT && myuid != AID_SHELL) {
// fprintf(stderr,"su: uid %d not allowed to su\n", myuid);
// return 1;
//}
Now that it works, I must make it work properly - i.e. allow only my termux
and Terminal Emulator
users to invoke su
and chroot
, and not let everyone and their grandmother in :-)
No comments:
Post a Comment