Explore Android security from NDK's debug principles on non-root phones
have been busy studying the security attack technology of Android recently, for a long time did not write a blog, ready to return to the old line of--read the funcking Android source code. These two days while looking at the NDK documentation, I saw a word "Native debugging ... does not require root or privileged access, Aslong as your application was Debugga Ble ". Hey, NDK debugging is not through the ptrace to achieve debugging? How does a ptrace on a non-root phone? With these two questions, we can introduce the security mechanism of Android.
Lao Luo's Sina Weibo: Http://weibo.com/shengyangluo, welcome attention!
Android is a Linux kernel-based mobile operating system. Linux is a system that supports multiple users, and the access rights of files in the system are controlled by the user ID (UID) and user group ID (GID). In other words, the security mechanism of Linux is based on UID and GID. Based on the UID-and GID-based security mechanisms provided by the Linux kernel, Android also implements a set of security mechanisms called permission, as shown in 1:
Figure 1 Linux uid/gid security mechanism with Android permission security mechanism
So how do these two security mechanisms correspond?
Let's first look at the Linux UID and GID security mechanism, which contains three basic roles: users, processes, and files, as shown in 2:
Figure 2 Linux three roles based on the Uid/gid security mechanism
Each user in Linux is assigned a UID, and then all users are divided into groups, each of which has a GID assigned to them. Note that a user can belong to more than one user group, that is, a UID can correspond to multiple GID. In one user group, one is called the primary user group, and the other is called the Supplemental user group.
Each file in Linux has three permissions: Read, Write, and execute. These three types of permissions are divided into three groups according to user attributes: Owner, group, and other. 3 is shown below:
Figure 3 File permissions partitioning for Linux
The file acct:1 can be seen from Figure 3. Owner is root, readable writable executable, 2. The primary user group to which the owner belongs is root, and other users in this group are readable and executable; 3. The rest of the user can read the executable.
Each process in Linux is associated with a user, that is, a uid,4 should be shown:
Figure 4 The Linux process
Since each user should have a primary user group and several supplemental user groups, each process has a primary GID and several supplementary gids in addition to a corresponding UID. These UID and GID determine the files that a process can access, or the system APIs that can be invoked. For example, in Figure 4, a process with a PID of 340 typically only accesses files with the owner U0_a19.
How does the UID of a process come about? By default, it is equal to the UID of the process that created it, that is, the UID of its parent process. The first process of Linux is the init process, which is created by the kernel after the boot is complete, and its UID is root. All other processes in the system are then created directly by the INIT process or indirectly by the child processes of the INIT process. So by default, the UID of all processes in the system should be root. This is not the case, however, because the parent process can invoke setuid to change its UID after the child process is created, that is, after the fork. For example, in a PC, after the init process is started, the user is logged on first. After the user has successfully logged in, there should be a shell process. The UID of the shell process is modified by setuid to the logged-on user. The UID of the remaining processes created in the system is then the logged-on user.
The UID of a process comes in addition to the parent process, and there is another way. As we said above, Linux files have three permissions, namely read, Wirte, and execute. In fact, there is another kind of authority, called SUID. For example, when we root the Android phone, we place a su file in it. This SU file has suid permissions, as shown in 5:
Figure 5 Su's suid and Sgid
Once an executable is set to the SUID bit, when it is loaded by a process through exec, the UID of the process becomes the UID of the owner of the executable file. That is, when the above SU is executed, the UID of the process it runs on is root, so it has the highest level of authority and does whatever it wants to do.
Like Sui, the file has another permission called Sgid, but it describes the user group. That is, once an executable is set with a GUID bit, when it is loaded by a process through exec, the main UID of the process becomes the owner's main uid of the executable file.
Now, the small partners should be able to understand the root principle of the Android phone: a normal process by executing Su, so as to obtain a root-privileged process. With this root-privileged process, you can do whatever you want. What Su does is very simple, it then fork another sub-process to do the real thing, that is, when we execute Su, the following parameters. Because the UID of the process that Su runs on is root, the UID of the child process that is forked out by it is also root. As a result, the sub-process can also do what they want to do.
However, the SU used for the root phone will also work with another app called Superuser. Su will start the superuser before fork to do the real thing, asking whether the user is allowed to fork a UID is the child process of root. This allows you to control root permissions and avoid being used secretly by malicious applications.
Here is Su's source code, the small partners can read according to the above-mentioned knowledge: https://code.google.com/p/superuser/source/browse/trunk/su/su.c?r=2.
In traditional Unix and Unix-like systems, the permissions of a process are divided into two types: privileged and non-privileged. Processes with UID equal to 0 are privileged processes that can be checked by all permissions. Processes with UID not equal to 0 are non-privileged processes that require permission checks when accessing some sensitive resources or invoking a sensitive API. This is a purely security mechanism for permission checks through the UID to be extensive. As a result, Linux starts from 2.2 and is subdivided from the permissions of the process, called capabilities. A process with capabilities can be set through system APIs such as Capset and Prctl. In other words, when a process invokes a sensitive system API, the Linux kernel considers its UID and whether it has a corresponding capability.
Here is a list of capabilities designed by Linux, and interested partners can read it again: http://man7.org/linux/man-pages/man7/capabilities.7.html.
These are the core elements of Linux's uid/gid-based security mechanism. Next we look at the Android permission-based security mechanism, which also has three roles: apk, signature, and permission,6 as shown:
Figure 6 Permission security mechanism for Android
Android APK after Packagemanagerservice installed, it is equivalent to Linux inside the user, they will be assigned to a UID and a master gid, The APK application permission is equivalent to the supplementary GID in Linux.
We know that the Android apk is running in a standalone application process, and these application processes are forked out by the zygote process. The zygote process was also forked by the Init process, and when it was forked out by the Init process, it was not setuid down, that is, its UID is still root. As we said earlier, when the application process is forked out by the zygote process, its UID should also be root. However, their UID will be modified by setuid to the UID of the loaded apk being assigned.
Referring to the analysis of the source code analysis of the Android application process startup process, Activitymanagerservice when requesting zygote to create the application process, The UID and GID (both the primary GID and the supplementary GID) assigned by the APK loaded by the application are collected and passed as parameters to the zygote process. The zygote process uses the execution function to fork the application process:
/* * Utility routine to fork Zygote and specialize the child process. */static pid_t Forkandspecializecommon (const u4* args, bool issystemserver) { pid_t pid; uid_t uid = (uid_t) args[0]; gid_t gid = (gid_t) args[1]; arrayobject* Gids = (Arrayobject *) args[2]; ...... PID = fork (); if (PID = = 0) { ... Err = Setgroupsintarray (gids); ...... Err = Setgid (GID); ...... Err = setuid (UID); ...... } ..... return PID;}
Parameters args[0], args[1], and args[] are the UID, primary GID, and supplementary gid assigned to the APK, respectively, by Setuid, Setgid and Setgroupsintarray are set to the current fork-out application process, so the application process no longer has root privileges.
So, what role does signature serve? Two functions: 1. Control which APK can share the same uid;2. Control which apk can apply which permission.
We know that if you want to have two apk share the same UID, then you need to configure the Android:shareduserid property in Androidmanifest. Packagemanagerservice when installing the APK, if you find that two apk has the same android:shareduserid attribute, then they will be assigned to the same UID. Of course, there is a premise that these two apk must have the same signature. This is important, otherwise, if I know someone else's apk set the Android:shareduserid property, then I also set the same Android:shareduserid property in my apk, I can go to the other people apk data.
In addition to allowing two apk to share the same UID through the Android:shareduserid property request, we can also set the value of the Android:shareduserid property to "Android.uid.system". So that the UID of an apk is set to 1000. The UID is 1000 of the user is system, and the system's key service is the UID of the process that is running on it. Its permissions are not the same as root, but they are big enough. We can take a look at the master key vulnerability to see how big.
When the Master key bug was released, it had a sensation on the entire Android world, and it was not analyzed in the specific situation, and many online, here is an official article: http://bluebox.com/corporate-blog/ bluebox-uncovers-android-master-key/. Now let's simply say how it's used:
1. Find an app with a system signature, and this app applies the UID Android.uid.system through the Android:shareduserid attribute.
2. Inject malicious code into the app via master key.
3. The malicious code injected into the app obtains the system user identity at run time.
4. Modify the/data/local.prop file to set the value of the attribute Ro.kernel.qemu to 1.
5. Restart the phone, because the value of Ro.kernel.qemu equals 1, this time the ADB process inside the phone will not be setuid stripped of root privileges.
6. Through the root-privileged ADB process, we can inject our familiar Su and superuser.apk into the system and complete the root process.
Note that the 1th step is to find a system signed app, because the Android:shareduserid attribute request Android.uid.system This UID requires a system signature, that is, not who can request the UID of system. In addition, the owner of the/data/local.prop file is system, so it can be modified only if the process of the system UID is obtained.
Besides, the relationship between signature and permission. Some permission, such as install_package, cannot be applied by anyone, and must have a system signature to control the allocation of suppementary GID, thus controlling the permissions of the application process. There are permission that can be applied with a system signature, and you can refer to the official documentation: Http://developer.android.com/reference/android/Manifest.html, which is labeled "not For use by Third-party applications "permission.
Once we understand the permission mechanism of Android, we can know:
1. Android APK is equivalent to the UID of Linux.
2. Android permission is the equivalent of a Linux gid.
3. Android signature is used to control the UID and GID allocation of the APK.
This is the relationship between Android permission-based security and the Linux Uid/gid-based security mechanism, which, in a nutshell, is what we often call the application sandbox:
Figure 7 Android Application Sandbox
Then we can finally get to the point of analyzing how the NDK debugs the app on a non-root phone. The first thing to know is that the NDK uses Gdbclient and Gdbserver to debug the app. Specifically, through the gdbserver through the ptrace attached to the target app process, and then gdbclient through the socket or pipe to link gdbserver, and give it a command to debug the app process. This specific process can refer to this article, which is very detailed: Http://ian-ni-lewis. blogspot.com/2011/05/ndk-debugging-without-root-access. HTML. Lao Luo hope that the small partners carefully read this article and then look at the next content, because next we only talk about the key points of this article.
The first key point is that every apk that needs to be debugged will take a gdbserver when it is packaged. Because the phone does not carry gdbserver this tool. This gdbserver is responsible for Ptrace to the app process to be dispatched.
The second key point is the invocation of the ptrace. In general, only root-privileged processes can be called. For example, if we want to inject a so into the target process through ptrace, then we need to apply root permissions to Su on the root phone. However, this is not absolute. If a process is the same as the UID of the target process, the process has permission to invoke Ptrace. We can look at the implementation of the Ptrace_attach function:
static int Ptrace_attach (struct task_struct *task, long request, unsigned long addr, unsigned long flags) { .. .... Task_lock (Task); retval = __ptrace_may_access (task, Ptrace_mode_attach); Task_unlock (Task); if (retval) goto unlock_creds; .... unlock_creds: mutex_unlock (&task->signal->cred_guard_mutex); out: ... return retval;}
Gdbserver before debugging an app, the first thing to do is to attach to the app process via Ptrace_attach. Ptrace_attach after performing the actual operation, the __ptrace_may_access is called to check the permissions of the calling process:
int __ptrace_ May_access (struct task_struct *task, unsigned int mode) {Const struct cred *cred = current_cred (), *tcred; ... if (task = = current) return 0; Rcu_read_lock (); tcred = __task_cred (Task); if (Cred->user->user_ns = = Tcred->user->user_ns && (cred->uid = = Tcred->euid && Cred->uid = = Tcred->suid && Cred->uid = = Tcred->uid && Cred->gid = = Tcred->egid && Cred->gid = = Tcred->sgid && cred->gid = tcred->gid)) Goto OK; if (Ptrace_has_cap (Tcred->user->user_ns, mode)) goto OK; Rcu_read_unlock (); Return-eperm;ok: ... return security_ptrace_access_check (task, mode);}
Here we can see that if the calling process has the same UID and GID as the target process, then the permission check is passed. Otherwise, the caller process is required to have the capability to execute Ptrace, which is checked by another function Ptrace_has_cap. If the UID of the calling process is root, then Ptrace_has_cap will definitely check through. Of course, after passing the two permission checks, but also to accept the kernel security module check, this is not through the UID or capability this set of mechanisms to control, we can ignore this topic.
The third key point is how to make the UID of the gdbserver process the same as the UID of the app process to be debugged. Because it is not possible to get root permissions on a phone that is not rooted, you can only choose to run this method with the same UID as the target process. This is going to use another tool: Run-as.
Runs-as is actually a tool similar to Su, it comes with the device, is located in the/system/bin directory, its suid bit is also set, and its owner is also root, we can see through the ls-l/system/bin/run-as:
[Email protected]:/# ls-l/system/bin/run-as -rwsr-s---root shell 9528 2013-12-05 05:32 run-as
However, unlike Su, Run-as does not allow a process to run as root, but instead allows a process to run with the specified UID, which is also achieved through setuid. Run-as is able to do this because when it runs, the UID obtained is root.
The fourth key point is that the APK being debugged must have the Android:debuggable property set to True in its androidmanifext.xml. What is this for? It turns out that when a process has Ptrace permissions to the target process, it is not yet able to debug the target process and requires the target process to set itself to be dumpable. Let's go back and take a closer look at the implementation of __ptrace_may_access:
int __ptrace_may_access (struct task_struct *task, unsigned int mode) { const struct cred *cred = current_cred (), *tcred ; ...... int dumpable = 0; ... OK: rcu_read_unlock (); SMP_RMB (); if (task->mm) dumpable = get_dumpable (task->mm); if (!dumpable &&!ptrace_has_cap (Task_user_ns (Task), mode)) return-eperm; Return Security_ptrace_access_check (task, mode);}
Let's see what happens when an APK has to set the Android:debuggable property to true in its androidmanifext.xml. Activitymanagerservice when the zygote process is requested to fork an application process, its Debug_enable_debugger flag bit is set to 1 and passed to the zygote process as a parameter. The zygote process is processed when it calls the function Forkandspecializecommon that we analyzed above to fork the application process, as follows:
Static pid_t Forkandspecializecommon (const u4* args, bool issystemserver) { pid_t pid; ...... U4 debugflags = args[3]; ...... PID = fork (); if (PID = = 0) { ... /* Configure additional debug options * /Enabledebugfeatures (debugflags); ...... } ...... return PID;}
Parameter Args[3] contains the debug flag bit, and the implementation of the function enabledebugfeatures is as follows:
void Enabledebugfeatures (U4 debugflags) {... if (DebugFlags & debug_enable_debugger)! = 0) {/* to let a n On-privileged Gdbserver Attach to this * process, we must set its dumpable bit flag. However * We is not interested in generating a coredump in * case of a crash, so also set the Coredump si Ze to 0 * To disable that */if (Prctl (pr_set_dumpable, 1, 0, 0, 0) < 0) {Aloge ("cou LD not set dumpable bit flag for PID%d:%s ", Getpid (), strerror (errno)); } else {struct rlimit rl; rl.rlim_cur = 0; Rl.rlim_max = rlim_infinity; if (Setrlimit (Rlimit_core, &RL) < 0) {Aloge ("Could not disable CORE file generation for PID%d:%s" , Getpid (), strerror (errno)); } } } ......}
So when an APK has to set the Android:debuggable property to True in its androidmanifext.xml, the process it runs on will set the pr_set_dumpable to 1 by Prctl. So gdbserver can debug it.
Now we understand how the NDK debugs the app on a non-root phone: gdbserver run-as the same UID as the target process and then ptrace to the target process to debug.
This look led to run-as this tool, seemingly very powerful appearance, then we can also use it to do bad things? For example, we can run run-as in the ADB shell (Run-as belongs to the shell group, so it can be executed) and specify that Run-as run with the UID of an apk, so it's not possible to read the APK's data? Thus breaking through the Android app sandbox. But it is impossible to do.
We can look at the source code of Run-as:
int main (int argc, char **argv) {const char* pkgname; int myUID, UID, GID; PackageInfo info; .../* check userid of caller-must be ' shell ' or ' root ' */myUID = Getuid (); if (myuid! = Aid_shell && myUID! = aid_root) {Panic ("only ' SHELL ' or ' ROOT ' users can run this program\n") ; }/* Retrieve package information from System */pkgname = argv[1]; if (Get_package_info (Pkgname, &info) < 0) {Panic ("package '%s ' is unknown\n", pkgname); return 1; }/* Reject system Packages */if (Info.uid < Aid_app) {Panic ("package '%s ' isn't an application\n", pkg name); return 1; }/* Reject any non-debuggable package */if (!info.isdebuggable) {Panic ("package '%s ' was not debuggable\n", Pkgname); return 1; }/* Ensure that we real/effective/saved IDs @ the * same time to avoid nasty surprises. */uid = GID = Info.uid; if (Setresgid (gid,gid,gid) | | \ SetResuid (Uid,uid,uid)) {Panic ("Permission denied\n"); return 1; } .../* Default exec shell. */EXECLP ("/system/bin/sh", "sh", NULL); Panic ("exec failed\n"); return 1;}
Here we can see that Run-as did a lot of security checks when it started, including:
1. Check whether the shell or root user is running.
2. Check that the value of the specified UID is the value assigned to the APK range, that is, you can specify only the UID of the APK, not the UID like system.
3. The Android:debuggable property of the apk that corresponds to the specified UID must be set to true.
After synthesizing the above three conditions, we can successfully execute the run-as.
One more thing to mention here is that when we run Run-as, the specified parameter is actually a package name. Run-as through the package name to/data/system/packages.xml to get the installation information for the APK, including its assigned UID, and its android:debuggable properties. The owner of the file/data/system/packages.xml is the root of the system,run-as when the file is read and therefore has permission to read it.
We also understand that it is not possible for you to do bad things through run-as. At the same time, this also reminds us that when releasing the APK, you must not set the value of the Android:debuggable property to True. Otherwise, it provides an opportunity for others to read your data, or to ptrace you.
To some, we through the NDK on the non-root mobile phone debugging principle completed the Android security mechanism of the discussion, do not know the small partners understand it? Do not understand the matter, you can pay attention to the old Luo Sina Weibo, there are a lot of dry share: Http://weibo.com/shengyangluo.
Original link This article by Bean John Blog Backup expert remote One click release
Explore the Android security mechanism from the NDK's debug principle on a non-root phone (reprint)