Using Root from Apps
Note: If you reached this article because you want applications to use root and don't care about the technical bits - just check out our repository sud, which contains the instructions needed.
Introduction
So, you're wanting to use su
from an app you're installing, huh? While our devices are modeled after a stock Ranchu (AOSP) userdebug device and do include the su
binary, it is not technically reachable by an application because of a few different security measures put in place by Android. These measures can be easily modified and persisted for allowing anything to use root on your device, but there are a few hindrances from the stock image we are required to get around.
Modifying su permissions
First, the shipped su
binary from the AOSP image is not accessible to everyone, which we can see here:
shell:/$ stat /system/xbin/su
File: /system/xbin/su
Size: 10352 Blocks: 24 IO Block: 4096 regular file
Device: fe00h/65024d Inode: 10909 Links: 1
Access: (4750/-rwsr-x---) Uid: ( 0/ root) Gid: ( 2000/ shell)
Access: 2020-12-17 16:09:26.000000000 -0500
Modify: 2020-12-17 16:09:26.000000000 -0500
Change: 2020-12-17 16:09:26.000000000 -0500
This is easy to fix, as we can just change permissions by chmod
ing it after remounting the system partition as read-write.
shell:/$ su
root:/# mount -orw,remount /system
root:/# chmod 06755 /system/xbin/su
root:/# stat /system/xbin/su
File: /system/xbin/su
Size: 10352 Blocks: 24 IO Block: 4096 regular file
Device: fe00h/65024d Inode: 10909 Links: 1
Access: (6755/-rwsr-sr-x) Uid: ( 0/ root) Gid: ( 2000/ shell)
Access: 2020-12-17 16:09:26.000000000 -0500
Modify: 2020-12-17 16:09:26.000000000 -0500
Change: 2021-01-19 20:14:22.305000000 -0500
At this point, we can switch to the userid
of any app process and try to use su
. This file is now executable and accessible; however, we will quickly see the second issue we must address:
root:/# su u0_a47
u0_a47:/$ su
su: not allowed
Remove the su check
This secondary issue in AOSP's bundled su
binary checks if the UID is AID_ROOT or AID_SHELL, and if it is not, it shows the above message of "not allowed". This can be seen in the source code for platform/system/extras/su/su.c
for the tag 7.1.2_r39 and below:
int main(int argc, char** argv) {
uid_t current_uid = getuid();
if (current_uid != AID_ROOT && current_uid != AID_SHELL) error(1, 0, "not allowed");
Now we can remove this check, and it will succeed from the command line when we are a different user.
Fixing Capabilities
However, there are still more hurdles we need to overcome. Since switching via su
from a shell instance doesn't change the whole context, we don't see the next issue until we try to run root
as an application. It turns out that the app_process
which launches the zygote will drop the ability for us to add or transfer capabilities to the process, as seen below:
if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) < 0) {
// Older kernels don't understand PR_SET_NO_NEW_PRIVS and return
// EINVAL. Don't die on such kernels.
if (errno != EINVAL) {
LOG_ALWAYS_FATAL("PR_SET_NO_NEW_PRIVS failed: %s", strerror(errno));
return 12;
}
}
This can be solved by compiling a new version of app_process32 and app_process64, though now we can see that this is getting to be quite a messy fix. Even when fixing this problem, we still end up having issues that are presented in the kernel - since the application namespaces do not have the CAP_SETGID capability, which is checked in the setgid
function seen below:
SYSCALL_DEFINE1(setgid, gid_t, gid)
{
struct user_namespace *ns = current_user_ns();
const struct cred *old;
struct cred *new;
int retval;
kgid_t kgid;
kgid = make_kgid(ns, gid);
if (!gid_valid(kgid))
return -EINVAL;
new = prepare_creds();
if (!new)
return -ENOMEM;
old = current_cred();
retval = -EPERM;
if (ns_capable(old->user_ns, CAP_SETGID))
new->gid = new->egid = new->sgid = new->fsgid = kgid;
else if (gid_eq(kgid, old->gid) || gid_eq(kgid, old->sgid))
new->egid = new->fsgid = kgid;
else
goto error;
return commit_creds(new);
error:
abort_creds(new);
return retval;
}
Looking to Open Source
We definitely don't want to have to recompile su
, app_process
, and the kernel - so let us look to the open-source world and see how this was solved on real devices. As we see from many projects like Superuser, SuperSU, and Magisk, they take a su
daemon approach. The reason they all take this approach is it appears they all originated from the same source code to begin with. Since all these projects aim to support many devices and install via recoveries, we're going to avoid just using their binaries. They're a bit over-complicated for what we're looking to do, and we would rather have a small, compact binary to do something simple.
Writing Our Own
Now, as previously mentioned, the aforementioned projects make use of a su
daemon. The design is as follows in the below diagram:
boot
|
init
|
init-sud.rc adb (etc)
| |
su-daemon su (cli)
| |
__main thread |
| | |
| accept(AF_LOCAL)<---------|
| |\ |
|__| \ |
| fork() |
| dup2(0, sock) |______
| dup2(1, sock) | | select(STDIO (client) | STDOUT (forked daemon))
| execvp(/vendor/bin/sh)| |
|<--------------------->|______|
Since Android doesn't want to share the root uid
with anyone who is an application, we just start a daemon process that already holds this permission. Then as applications require root, they utilize a socket to connect to the daemon and ask it to perform work for them. There isn't much new here as this has been leveraged for years - though with Corellium we can quickly change these files around and not worry about complexities that are introduced for real devices, such as "systemless" files and dm-verity.
This means that by utilizing our su
daemon, named sud, we can simply copy over the su
file, set the permissions, add an rc
file for initialization, and reboot. After this, the device is ready to go for your use. sud
is just a simplistic refactor of Superuser and can be found in our GitHub repository](https://github.com/corellium/sud). For those who are uninterested in compiling the binary, we have also released it in binary form at the same repository in the releases section.
Setting Up sud
To set up a device with this new daemon, we simply connect to the device using adb, push the files, and then move them to the proper locations and reboot. From a local machine or utilizing the Corellium web interface, simply push/upload the files to the /data/local/tmp/
directory:
adb push init.sud.rc /data/local/tmp/
adb push bin/su /data/local/tmp/
Then get a shell - either via adb or using the Console tab in our platform - and run the following commands. In both instances, you could simply copy and paste this, and it should work fine:
su
/system/bin/mount -orw,remount /system
/vendor/bin/cp /data/local/tmp/su /system/xbin/su
/vendor/bin/chown root /system/xbin/su
/system/bin/chcon u:object_r:su_exec:s0 /system/xbin/su
/vendor/bin/chmod 06755 /system/xbin/su
/vendor/bin/cp /data/local/tmp/init.sud.rc /system/etc/init/init.sud.rc
/vendor/bin/chown root /system/etc/init/init.sud.rc
/system/bin/chcon u:object_r:system_file:s0 /system/etc/init/init.sud.rc
/vendor/bin/chmod 644 /system/etc/init/init.sud.rc
/system/bin/reboot
After the devices are rebooted, all applications on the device can utilize su
.
Conclusion
Please note that this modified su
daemon SHOULD NOT be used on any real devices, as it is a large security risk. It allows anyone, without restrictions, to use root - it could be abused by any application or process. However, you could easily add restrictions back in or log usage. This binary could be quickly leveraged to work as a honeypot for malware, sort of as a poor man's "taint tracking" of what is being required by malware, etc. The template we built in the sud repository can also serve as a quick and easy way to get multiple services up and running on your Corellium devices as well!