Harden your system against attacks with the grsecurity kernel patch.
Hardening
a Unix system can be a difficult
process. It typically involves setting up all the services that the
system will run in the most secure fashion possible, as well as
locking down the system to prevent local compromises. However,
putting effort into securing the services that
you’re running does little for the rest of the
system and for unknown vulnerabilities. Luckily, even though the
standard Linux kernel provides few features for proactively securing
a system, there are patches available that can help the enterprising
system administrator do so. One such patch is grsecurity
(http://www.grsecurity.net).
grsecurity
started out as a port of the OpenWall
patch (http://www.openwall.com)
to the 2.4.x series of Linux kernels. This patch added features such
as nonexecutable stacks, some filesystem security enhancements,
restrictions on access to /proc
, as well as some
enhanced resource limits. These features helped to protect the system
against stack-based buffer overflow attacks, prevented filesystem
attacks involving race conditions on files created in
/tmp
, limited a user to only seeing his own
processes, and even enhanced Linux’s resource limits
to perform more checks. Since its inception,
grsecurity
has grown to include many features
beyond those provided by the OpenWall patch.
grsecurity
now includes many additional memory
address space protections to prevent buffer overflow exploits from
succeeding, as well as enhanced chroot()
jail
restrictions,
increased randomization of
process and IP IDs, and increased auditing features that enable you
to track every process executed on a system.
grsecurity
adds a sophisticated
access
control list (ACL) system that makes use of Linux’s
capabilities system. This ACL system can be used to limit the
privileged operations that individual processes are able to perform
on a case-by-case basis.
Configuration of ACLs is handled through the
gradm
utility. If you already have
grsecurity
installed on your machine, feel free
to skip ahead to
[Hack #14]
.
To compile a kernel with grsecurity
, you will
need to download the patch that corresponds to your kernel version
and apply it to your kernel using the
patch
utility.
For example, if you are running Linux 2.4.24:
#cd /usr/src/linux-2.4.24
#patch -p1 < ~andrew/grsecurity-1.9.13-2.4.24.patch
While the command is running, you should see a line for each kernel
source file that is being patched. After the command has finished,
you can make sure that the patch applied cleanly by looking for any
files that end in .rej
. The patch program creates these
when it cannot apply the patch cleanly to a file. A quick way to see
if there are any .rej
files is to use the
find
command:
# find ./ -name \*.rej
If there are any rejected files, they will be listed on the screen. If the patch applied cleanly, you should be returned back to the shell prompt without any additional output.
After the patch has been applied,
you can configure the kernel to enable
grsecurity
’s features by
running make config
to use text prompts,
make
menuconfig
for a
curses-based interface, or
make xconfig
to use a Tk-based GUI. If you went the
graphical route and used make xconfig
, you should
then see a dialog similar to Figure 1-1. If you ran
make menuconfig
or make
config
, the relevant kernel options have the same
name as the menu options described in this example.
To configure which grsecurity
features will be
enabled in the kernel, click the button labeled Grsecurity. After
doing that, you should see a dialog similar to Figure 1-2.
To enable grsecurity,
click the y radio button.
After you’ve done that, you can enable predefined
sets of features with the Security Level drop-down list, or set it to
Custom and go through the menus to pick and choose which features to
enable.
Choosing Low is safe for any system and should not affect any
software’s normal operation. Using this setting will
enable linking restrictions in directories
with mode 1777. This prevents race conditions in
/tmp
from being exploited, by only following
symlinks to files that are owned by the process following the link.
Similarly, users won’t be able to write to
FIFOs that they do not own if they
are within a directory with permissions of 1777.
In addition to the tighter symlink and FIFO restrictions, the
Low
setting increases the randomness of process
and IP IDs. This helps to prevent attackers from using remote
detection techniques to correctly guess the operating system your
machine is running (as in
[Hack #40]
),
and it also makes it difficult to guess the process ID of a given
program. The Low security level also forces
programs that use chroot( )
to change their current working directory to
/
after the chroot()
call.
Otherwise, if a program left its working directory outside of the
chroot
environment, it could be used to break out
of the sandbox. Choosing the Low security level also prevents nonroot
users from using dmesg
,
a utility that can be used to view recent kernel messages.
Choosing Medium enables all of the same features as the Low security
level, but this level also includes features that make
chroot()
-based sandboxed environments more
secure. The ability to mount filesystems, call chroot( )
, write to sysctl
variables, or create
device nodes
within a chroot
ed environment are all restricted,
thus eliminating much of the risk involved in running a service in a
sandboxed environment under Linux. In addition, TCP source ports will
be randomized, and failed fork()
calls, changes
to the system time, and segmentation faults will all be logged.
Enabling the Medium security level will also restrict total access to
/proc
to those who
are in the wheel group. This hides each user’s
processes from other users and denies writing to
/dev/kmem
, /dev/mem
, and
/dev/port
. This makes it more difficult to patch
kernel-based root kits into the running kernel. Also,
process memory address space layouts
are randomized, making it harder for an attacker to successfully
exploit buffer overrun attacks. Because of this, information on
process address space layouts is removed from
/proc
as well. Because of these
/proc
restrictions, you will need to run your
identd
daemon (if you are running one) as
an account that belongs to the wheel group. According to the
grsecurity
documentation, none of these features
should affect the operation of your software, unless it is very old
or poorly written.
To enable nearly all of
grsecurity
’s features, you can
choose the High security level. In addition to the features provided
by the lower security levels, this level implements additional
/proc
restrictions by limiting access to device
and CPU information to users who are in the wheel group. Sandboxed
environments are also further restricted by disallowing
chmod
to set the SUID or SGID bit when operating
within such an environment. Additionally, applications that are
running within such an environment will not be allowed to insert
loadable modules, perform raw I/O, configure network devices, reboot
the system, modify immutable files, or change the
system’s time. Choosing this security level will
also cause the kernel’s stack to be laid out
randomly, to prevent kernel-based buffer overrun exploits from
succeeding. In addition, the kernel’s symbols will
be hidden—making it even more difficult for an intruder to
install Trojan code into the running kernel—and
filesystem
mounting, remounting, and
unmounting will be logged.
The High security level also enables
grsecurity
’s
PaX code,
which enables nonexecutable memory pages. Enabling this will
cause many buffer overrun exploits to fail, since any code injected
into the stack through an overrun will be unable to execute. However,
it is still possible to exploit a program with buffer overrun
vulnerabilities, although this is made much more difficult by
grsecurity
’s address space
layout randomization features. PaX can also carry with it some
performance penalties on the x86 architecture, although they are said
to be minimal. In addition, some programs—such as XFree86,
wine, and Java© virtual machines—will expect
that the memory addresses returned by malloc()
will be executable. Unfortunately, PaX breaks this behavior, so
enabling it will cause those programs and others that depend on it to
fail. Luckily, PaX can be disabled on a per-program basis with the
chpax
utility (http://chpax.grsecurity.net).
To disable PaX for a program, you can run a command similar to this one:
# chpax -ps /usr/bin/java
There are also other programs that make use of special
GCC features, such as
trampoline functions. This allows a
programmer to define a small function within a function, so that the
defined function is only in the scope of the function in which it is
defined. Unfortunately, GCC puts the trampoline
function’s code on the stack, so PaX will break any
programs that rely on this. However, PaX can provide emulation for
trampoline functions, which can be enabled on a per-program basis
with chpax
, as well by using the
-E
switch.
If you do not like the sets of features that are enabled with any of the predefined security levels, you can just set the kernel option to “custom” and enable only the features you need.
After you’ve set a security level or enabled the specific options you want to use, just recompile your kernel and modules as you normally would. You can do that with commands similar to these:
#make dep clean && make bzImage
#make modules && make modules_install
Then reboot with your new kernel. In addition to the kernel
restrictions already in effect, you can now use
gradm
to set up ACLs for your system.
We’ll see how to do that in
[Hack #14]
.
As you can see, grsecurity
is a complex but
tremendously useful modification of the Linux kernel. For more
detailed information on installing and configuring the patches,
consult the extensive documentation at http://www.grsecurity.net/papers.php.
Get Network Security Hacks now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.