Taking Advantage of Linux Capabilities

A common topic of discussion nowadays is
security, and for good reason. Security is becoming more important
as the world becomes further networked. Like all good systems,
Linux is evolving in order to address increasingly important
security concerns.

One aspect of security is user privileges. UNIX-style user
privileges come in two varieties, user and root. Regular users are
absolutely powerless; they cannot modify any processes or files but
their own. Access to hardware and most network specifications also
are denied. Root, on the other hand, can do anything from modifying
all processes and files to having unrestricted network and hardware
access. In some cases root can even physically damage
hardware.

Sometimes a middle ground is desired. A utility needs special
privileges to perform its function, but unquestionable god-like
root access is overkill. The ping utility is setuid root simply so
it can send and receive ICMP messages. The danger lies in the fact
that ping can be exploited before it has dropped its root
privileges, giving the attacker root access to your server.

Fortunately, such a middle ground now exists, and it's called
POSIX capabilities. Capabilities divide system access into logical
groups that may be individually granted to, or removed from,
different processes. Capabilities allow system administrators to
fine-tune what a process is allowed to do, which may help them
significantly reduce security risks to their system. The best part
is that your system already supports it. If you're lucky, no
patching should be necessary.

A list of all the capabilities that your system is, well,
capable of, is available in /usr/include/linux/capability.h,
starting with CAP_CHOWN. They're pretty self-explanatory and well
commented. Capability checks are sprinkled throughout the kernel
source, and grepping for them can make for some fun midnight
reading.

Each capability is nothing more than a bit in a bitmap. With
32 bits in a capability set, and 28 sets currently defined, there
are currently discussions as to how to expand this number. Some
purists believe that additional capabilities would be too
confusing, while others argue that there should be many more, even
a capability for each system call. Time and Linus will ultimately
decide how this exciting feature develops.

The Proc Interface

As of kernel 2.4.17, the file /proc/sys/kernel/cap-bound
contains a single 32-bit integer that defines the current global
capability set. The global capability set determines what every
process on the system is allowed to do. If a capability is stripped
from the system, it is impossible for any process, even root
processes, to regain them.

For example, many crackers' rootkits (a set of tools that
cover up their activities and install backdoors into the system)
will load kernel modules that hide illicit processes and files from
the system administrator. To counter this, the administrator could
simply remove the CAP_SYS_MODULE capability from the system as the
last step in the system startup process. This step would prevent
any kernel modules from being loaded or unloaded. Once a capability
has been removed, it cannot be re-added. The system must be
restarted (which means you might have to use the power button if
you've removed the CAP_SYS_BOOT capability) to regain the
full-capability set.

Okay, I lied. There are two ways to add back a
capability:

init can re-add capabilities, in theory; there's no
actual implementation to my knowledge. This is to facilitate
capability-aware systems in the event that init needs to change
runlevels.

If a process is capable of CAP_SYS_RAWIO, it can
modify kernel memory through /dev/mem. Among other things, it can
modify kernel memory to grant itself whatever access it desires.
Remove CAP_SYS_RAWIO, but be careful: by removing CAP_SYS_RAWIO,
programs such as X most likely will fail to run.

Editing cap-bound by hand is kind of tedious. Fortunately for
you, there's a utility called lcap that provides a friendlier
interface to cap-bound. Here's how one would remove CAP_SYS_CHOWN:

Here's how you would remove all capabilities except CAP_SYS_BOOT,
CAP_SYS_KILL and CAP_SYS_NICE:

lcap -z CAP_SYS_BOOT CAP_SYS_KILL CAP_SYS_NICE

One thing to note: modifying cap-bound restricts the capabilities
of future processes only. Okay, not exactly future processes but
any process that calls exec(2) (see the function compute_creds in
the kernel source file fs/exec.c). Currently running processes keep
the capabilities with which they started.

Modifying the capabilities of an existing process leads us
into the next section, and here's the catch I spoke about above.
Running lcap with no arguments lists what your system is capable
of. If you see that CAP_SETPCAP is disabled, you need to make a
change to your kernel. It's simple enough to describe here. In the
kernel source tree, edit include/linux/capability.h. You're
changing the lines:

There's actually a reason that CAP_SETPCAP is disabled by
default: it's deemed a security risk to leave it enabled on a
production system (a patch exists for this condition but has yet to
be applied as of this writing). To be on the safe side, make sure
to remove this capability when you're done playing.