6

What are the fundamental differences between some arbitrary account and root? Is it just the UID being something other than zero?

So, what exactly does the su binary do and how does it elevate a user to root? I know a user must first be a part of the sudo group through what we find in /etc/sudoers.

# User privilege specification
root    ALL=(ALL:ALL) ALL

# Members of the admin group may gain root privileges
%admin ALL=(ALL) ALL

# Allow members of group sudo to execute any command
%sudo   ALL=(ALL:ALL) ALL

Taking a look at the su executable's permissions we find -rwsr-xr-x, or 4755 (i.e. setuid is set).

Is it the su binary that reads this configuration file and checks if the user requesting root permissions is part of either group sudo or admin? If so, does the binary spawn another shell as root (considering the setuid bit) assuming the user is part of the expected groups and knows the appropriate user's password that is attempting to be substituted (.e.g root, in particular)?


tl;dr Does the act of privilege elevation rely on the setuid bit in the su binary, or are there other mechnisms to change the UID of the current process? In the case of the former, it seems that only the EUID would change leaving UID != EUID. Is this ever a problem?

related How is all of this emulated in the Android environment? As far as I have read, access to root has been entirely stripped -- although processes still run at this privilege level.

If we removed sudo and su would that be enough to prevent privilege elevation or has Android taken further steps?

  • 3
    su does not use the sudoers configuration. – jordanm Mar 2 '16 at 19:06
  • @jordanm, I was not aware that su and sudo were mutually exclusive. – sherrellbc Mar 2 '16 at 19:49
  • Look up the source code of su. (For instance in the original archive of Debian's login package.) It's amazingly short. Yes, all the magic originates from UID == 0. – Bananguin Mar 2 '16 at 20:13
10

Root is user 0

The key thing is the user ID 0. There are many places in the kernel that check the user ID of the calling process and grant permission to do something only if the user ID is 0.

The user name is irrelevant; the kernel doesn't even know about user names.

Android's permission mechanism is identical at the kernel level but completely different at the application level. Android has a root user (UID 0), just like any other system based on a Linux kernel. Android doesn't have user accounts though, and on most setups doesn't allow the user (as in the human operating and owning the device) to perform actions as the root user. A “rooted” Android is a setup that does allow the device owner/user to perform actions as root.

How setuid works

A setuid executable runs as the user who owns the executable. For example, su is setuid and owned by root, so when any user runs it, the process running su runs as the root user. The job of su is to verify that the user that calls it is allowed to use the root account, to run the specified command (or a shell if no command is specified) if this verification succeeds, and to exit if this verification fails. For example, su might ask the user to prove that they know the root password.

In more detail, a process has three user IDs: the effective UID, which is used for security checks; the real UID, which is used in a few privilege checks but is mainly useful as a backup of the original user ID, and the saved user ID which allows a process to temporarily switch its effective UID to the real user ID and then go back to the former effective UID (this is useful e.g. when a setuid program needs to access a file as the original user). Running a setuid executable sets the effective UID to the owner of executable and retains the real UID.

Running a setuid executable (and similar mechanisms, e.g. setgid) is the only way to elevate the privileges of a process. Pretty much everything else can only decrease the privileges of a process.

Beyond traditional Unix

Until now I described traditional Unix systems. All of this is true on a modern Linux system, but Linux brings several additional complications.

Linux has a capability system. Remember how I said that the kernel has many checks where only processes running as user ID 0 are allowed? In fact, each check gets its own capability (well, not quite, some checks use the same capability). For example, there's a capability for accessing raw network sockets, and another capability for rebooting the system. Each process has a set of capabilities along side its users and groups. The process passes the check if it is running as user 0 or if it has the capability that corresponds to the check. A process that requires a specific privilege can run as a non-root user but with the requisite capability; this limits the impact if the process has a security hole. An executable can be setcap to one or more capabilities: this is similar to setuid, but works on the process's capability set instead of the process's user ID. For example, ping only needs raw network sockets, so it can be setcap CAP_NET_RAW instead of setuid root.

Linux has several security modules, the best known being SELinux. Security modules introduce additional security checks, which can apply even to processes running as root. For example, it's possible (not easy!) to set up SELinux so as to run a process as user ID 0 but with so many restrictions that it can't actually do anything.

Linux has user namespaces. Inside the kernel, a user is in fact not just a user ID, but a pair consisting of a user ID and a namespace. Namespaces form a hierarchy: a child namespace refines permissions within its parent. The all-powerful user is user 0 in the root namespace. User 0 in a namespace has powers only inside that namespace. For example, user 0 in a user namespace can impersonate any user of that namespace; but from the outside all the processes in that namespace run as the same user.

  • There is so much great information here; thank you for taking the time to write this up. So, then, if I create a binary owed by root with setuid set containing a single call to setreuid() with the passed ID, then is this analogous to what su does modulo any password authentication? – sherrellbc Mar 3 '16 at 22:26
  • @sherrellbc That's the git of it. If you want to see what su does more precisely, you can look at the source. Or you can watch it live: in a terminal, get the shell's PID (echo $$), e.g. 1234; in another terminal, as root, run strace -p1234; in the first terminal, run exec su. – Gilles Mar 3 '16 at 22:36
4

In Linux there are 4 UIDs: RUID (real), EUID (effective), SUID (saved), FSUID (filesystem).

These are nothing more than numbers and are properties of processes, which are stored in a control block in the process table of the Kernel.

The UID '0' has a special characteristic, as it denotes the user root, which has generally unrestricted access rights.

su and sudo are programs to change the user's effective access rights, by starting a new sub-process with the EUID set to the new UID through the SetUID bit of su. This su process then spawns a new shell again in a new sub-process with the 4 UIDs set to the new UID value.

The following example should demonstrate this. Let's say a user rda is logged in over an ssh terminal. ps fax will show the following processes involved:

472 ?        Ss     0:00 /usr/sbin/sshd -D
9151 ?        Ss     0:00  \_ sshd: rda [priv]
9153 ?        S      0:00  |   \_ sshd: rda@pts/1
9154 pts/1    Ss+    0:00  |       \_ -bash

4 processes, the ssh daemon, two processes for the ssh session (and terminal?), the last process is a login shell (denoted by a - in front of bash)

ps faw -eo euser,ruser,suser,fuser,f,comm will show the UIDs of the processes:

EUSER    RUSER    SUSER    FUSER    F COMMAND
...
root     root     root     root     4 sshd
root     root     root     root     4  \_ sshd
rda      rda      rda      rda      5  |   \_ sshd
rda      rda      rda      rda      0  |       \_ bash

Invoking su followed by a successful authentication will result in the following:

EUSER    RUSER    SUSER    FUSER    F COMMAND
...
root     root     root     root     4 sshd
root     root     root     root     4  \_ sshd
rda      rda      rda      rda      5  |   \_ sshd
rda      rda      rda      rda      0  |       \_ bash
root     rda      root     root     4  |           \_ su
root     root     root     root     4  |               \_ bash

The 'bash' process starts a new 'su' child-process with EUID set by SetUID bit to '0' (root), RUID is still set to rda's UID at this point. The 'su' process again starts a new child-process with a new shell, granting the user root access (RUID is now set to '0' too). The user remains at his working directory and the new shell will use the same environment as the parent shell, for example:

server:/home/rda# echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
server:/home/rda# pwd
/home/rda

The shell can be closed with exit and the user will be in the parent shell with his original access rights.

The situation is different if 'su' is invoked with a hyphen '-' parameter:

rda@server:~$ su -
Password:
server:~# echo $PATH
/root/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
server:~# pwd
/root

The shell environment has changed, as the new shell is a login shell, see '-su', which executes some additional configuration scripts:

 9151 ?        Ss     0:00  \_ sshd: rda [priv]
 9153 ?        S      0:00  |   \_ sshd: rda@pts/1
 9154 pts/1    Ss     0:00  |       \_ -bash
 9613 pts/1    S      0:00  |           \_ su -
 9614 pts/1    S+     0:00  |               \_ -su

A login shell should be closed with logout.

In theory, I think it is possible to prevent the user from gaining elevated privileges by removing sudo and su and give no possibility to log on to the system directly (via terminal, ssh, etc) and with no physical access to the device.

Update: Rooting process on Android

As explained in detail here, the possible methods for rooting an Android device depend on the bootloader and the Android system property ro.secure.

The target is always the same, to install the su binary in /system and make it setuid(0).

Device with unlocked bootloader:

Pull the stock ROM with dd from the device, add su, repackage (or download such a modified ROM), reboot the device in flash mode and flash the modified ROM.

Device with ro.secure=0:

This system property controls whether commands typed in an adb shell are run as root (ro.secure=0) or as an unprivileged user (ro.secure=1) The value of ro.secure is set at boot time from the default.prop file in the root directory, which is only accessible by user root and thus, secure.

In most cases this ro.secure is set to 1, but some manufacturers set this to 0. This can be checked by running the command getprop ro.secure in a terminal emulator on the device or in an adb shell. If it is set to 0, rooting is quite easy, connect it to a computer, run adb, mount /system as read-write, install su.

Device with locked bootloader:

Such a device has a recovery that does not allow to flash a custom ROM, which has not been signed by the manufacturer. The only way to gain root access in this situation is by exploiting a security vulnerability in one of the running system processes, which run in privileged mode, and hack it to allow the execution of "arbitrary code". This code usually mounts /system and installs su permanently.

  • Lots of great information here. Thank you for the visuals. As for your final thought, you are correct; "Rooting" a device in android involves patching the system and putting a su binary to control access. The more interesting question is what the patches are. That is, what exactly is the patching doing to uncover access to the UID 0? SuperSu is closed source, so we cannot know for sure how it was done. Given your insight do you have any speculation as to what it might be doing? – sherrellbc Mar 4 '16 at 2:00
  • I made some research and an update im my answer explaining the process of rooting an Android device. My first answer was not correct, the package SuperSU is not the same as the binary su, it is just an app, that controls root access to other apps which request root acces, after a device has been rooted. – rda Mar 4 '16 at 12:28
2

In general, it's just that the effective uid is 0. The "setuid" bit on executables actually sets the effective uid of the process. If the effective uid is not zero and the real uid is not zero, the program is running as an "unprivileged" user. The following apply:

Unprivileged processes may only set the effective user ID to the real user ID, the effective user ID, or the saved set-user-ID. Unprivileged users may only set the real user ID to the real user ID or the effective user ID.

As far as Android is concern, no, I don't think removing sudo and su is enough -- if any program can have the seteuid bit set, that program can potentially run with uid = 0. If there is any possibility of having access to the internal filesystem as root at any time, then such a program may be introduced and root access is feasible.

  • So then it seems all su should do is authenticate the entered password and the OS handles setting the EUID to 0 (i.e. now we have UID != EUID). A test of this showed that proper authentication with su sets both the EUID and UID to 0 -- at least on Linux Mint. – sherrellbc Mar 2 '16 at 21:14
  • The kernel sets the euid to 0, and once authenticated, su does a setuid. (actually, iirc, setreuid and setreguid) – Otheus Mar 2 '16 at 21:22
  • 1
    You can verify what it does for yourself by running strace -f su root -c true . Make sure you give your terminal a very large buffer. – Otheus Mar 2 '16 at 21:24
  • 1
    The kernel is handling this transition. Every process has associated with it a real uid, an effective id, and saved ids. The kernel knows which is which. When a program image file has the seteuid bit set, and the kernel is asked to exec that image, it detects the bit, changes the effective id accordingly for the duration of that process. When that process exits, it is no longer relevant. Now, there are also controlling terminals, but for Android development, I'm quite sure that is irrelevant. – Otheus Mar 2 '16 at 21:38
  • 1
    su is a program; it's not in the kernel. It's implementation is distribution specific. The kernel source tree would contain entry points for the setreuid syscall. lxr.free-electrons.com/source/kernel/sys.c#L580 – Otheus Mar 2 '16 at 22:18

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.