Containing Intrusions in Apache

In this fifth part to a six-part series on installing and configuring Apache, you will learn, among other things, how to put Apache in jail. This article is excerpted from chapter two of Apache Security, written by Ivan Ristic (O’Reilly; ISBN: 0596007248). Copyright © 2006 O’Reilly Media, Inc. All rights reserved. Used with permission from the publisher. Available from booksellers or direct from O’Reilly Media.

Removing Default Content 

The key to changing web server identity is consistency. The trouble we went through to change the web server make may be useless if we leave the default Apache content around. The removal of the default content is equivalent to changing one’s clothes when going undercover. This action may be useful even if we do not intend to change the server identity. Applications often come with sample programs and, as a general rule, it is a good practice to remove them from production systems; they may contain vulnerabilities that may be exploited later.

Most of the default content is out of reach of the public, since we have built our Apache from scratch, changed the root folder of the web site, and did not include aliases for the manual and the icons. Just to be thorough, erase the following directories:

  • /usr/local/apache/cgi-bin
  • /usr/local/apache/htdocs
  • /usr/local/apache/manual (Apache 2 only)

You will probably want to keep the original /usr/local/apache/logs directory though the logs are stored in /var/www/logs. This is because many modules use the logs/ folder relative to the Apache installation directory to create temporary files. These modules usually offer directives to change the path they use, but some may not. The only remaining bit of default content is the error pages Apache displays when errors occur. These pages can be replaced with the help of the ErrorDocument directive. Using one directive per error code, replace the error pages for all HTTP error codes. (A list of HTTP codes is given in Chapter 8; it can also be found at Protocols/rfc2616/rfc2616-sec10.html.)

  ErrorDocument 401 /error/401.htm l
  ErrorDocument 403 /error/403.html

  ErrorDocument 404 /error/404.html
  ErrorDocument 500 /error/500.html

An alternative to creating dozens of static pages is to create one intelligent script that retrieves the error code from Apache and uses it to display the appropriate message. A small bit of programming is required in this case, following guidance from the Apache documentation at

{mospagebreak title=Putting Apache in Jail}

Even the most secure software installations get broken into. Sometimes, this is because you get the attention of a skilled and persistent attacker. Sometimes, a new vulnerability is discovered, and an attacker uses it before the server is patched. Once an intruder gets in, his next step is to look for local vulnerability and become superuser. When this happens, the whole system becomes contaminated, and the only solution is to reinstall everything.

Our aim is to contain the intrusion to just a part of the system, and we do this with the help of the chroot(2) system call. This system call allows restrictions to be put on a process, limiting its access to the filesystem. It works by choosing a folder to become the new filesystem root. Once the system call is executed, a process cannot go back (in most cases, and provided the jail was properly constructed).

The root user can almost always break out of jail. The key to building an escape-proof jail environment is not to allow any root processes to exist inside the jail. You must also not have a process outside jail run ning as the same user as a process inside jail. Under some circumstances, an attacker may jump from one process to another and break out of jail. That’s one of the reasons why I have insisted on having a separate account for Apache.

The term chroot is often interchangeably used with the term jail. The term can be used as a verb and noun. If you say Apache is chrooted, for example, you are saying that Apache was put in jail, typically via use of the chroot binary or the chroot(2) system call. On Linux systems, the meanings of chroot and jail are close enough. BSD systems have a separate jail() call, which implements additional security mechanisms. For more details about the jail() call, see the following:

Incorporating the jail mechanism (using either chroot(2) or jail() ) into your web server defense gives the following advantages:


If the intruder breaks in through the server, he will only be able to access files in the restricted file system. Unable to touch other files, he will be unable to alter them or harm the data in any way.

No shell

Most exploits need shells (mostly /bin/sh) to be fully operative. While you cannot remove a shell from the operating system, you can remove it from a jail environment.

Limited tool availability

Once inside, the intruder will need tools to progress further. To begin with, he will need a shell. If a shell isn’t available he will need to find ways to bring one in from the inside. The intruder will also need a compiler. Many black hat tools are not used as binaries. Instead, these tools are uploaded to the server in source and compiled on the spot. Even many automated attack tools compile programs. The best example is the Apache Slapper Worm (see the sidebar “Apache Slapper Worm”).

Absence of suid root binaries

Getting out of a jail is possible if you have the privileges of the root user. Since all the effort we put into the construction of a jail would be meaningless if we allowed suid root binaries, make sure you do not put such files into the jail.

The chroot(2) call was not originally designed as a security measure. Its use for security is essentially a hack, and will be replaced as the server virtualization technologies advance. For Linux, that will happen once these efforts become part of a mainstream kernel. Though server virtualization falls out of the scope of this book, some information on this subject is provided in Chapter 9.

The following sections describe various approaches to putting Apache in jail. First, an example demonstrating use of the original chroot binary to put a process in jail is shown. That example demonstrates the issues that typically come up when attempting to put a process in jail and briefly documents tools that are useful for solving these issues. Next, the steps required for creating a jail and putting Apache in it using chroot are shown. This is followed by the simpler chroot(2) approach, which can be used in some limited situations. Finally, the use of mod_security or mod_chroot to chroot Apache is presented.

Apache Slapper Worm

The Apache Slapper Worm ( is arguably the worst thing to happen to the Apache web server as far as security goes. It uses vulnerabilities in the OpenSSL subsystem ( html) to break into a system running Apache. It proceeds to infect other systems and calls back home to become a part of a distributed denial of service (DDoS) network. Some variants install a backdoor, listening on a TCP/IP port. The worm only works on Linux systems running on the Intel architecture.

The behavior of this worm serves as an excellent case study and a good example of how some of the techniques we used to secure Apache help in real life.

  • The worm uses a probing request to determine the web server make and version from the Server response header and attacks the servers it knows are vulnerable. A fake server signature would, therefore, protect from this worm. Subsequent worm mutations stopped using the probing request, but the initial version did and this still serves as an important point.
  • If a vulnerable system is found, the worm source code is uploaded (to /tmp) and compiled. The worm would not spread to a system without a compiler, to a system where the server is running from a jail, or to a system where code execution in the /tmp directory is disabled (for example, by mounting the partition with a noexec flag).

Proper firewall configuration, as discussed in Chapter 9, would stop the worm from spreading and would prevent the attacker from going into the server through the backdoor.

{mospagebreak title=Tools of the chroot Trade}

Before you venture into chroot land you must become aware of several tools and techniques you will need to make things work and to troubleshoot problems when they appear. The general problem you will encounter is that programs do not expect to be run without full access to the filesystem. They assume certain files are present and they do not check error codes of system calls they assume always succeed. As a result, these programs fail without an error message. You must use diagnostic tools such as those described below to find out what has gone wrong.

Sample use of the chroot binary

The chroot binary takes a path to the new filesystem root as its first parameter and takes the name of another binary to run in that jail as its second parameter. First, we need to create the folder that will become the jail:

  # mkdir /chroot

Then, we specify the jail (as the chroot first parameter) and try (and fail) to run a shell in the jail:

  # chroot /chroot /bin/bash
  chroot: /bin/bash: No such file or directory

The above command fails because chroot corners itself into the jail as its first action and attempts to run /bin/bash second. Since the jail contains nothing, chroot com plains about being unable to find the binary to execute. Copy the shell into the jail and try (and fail) again:

  # mkdir /chroot/bin
  # cp /bin/bash /chroot/bin/bash
  # chroot /chroot /bin/bash

  chroot: /bin/bash: No such file or directory

How can that be when you just copied the shell into jail?

  # ls -al /chroot/bin/bash
  -rwxr-xr-x   1 root   root   605504 Mar 28 14:23 /chroot/bin/bash

The bash shell is compiled to depend on several shared libraries, and the Linux ker nel prints out the same error message whether the problem is that the target file does not exist or that any of the shared libraries it depends on do not exist. To move beyond this problem, we need the tool from the next section.

Using ldd to discover dependencies

The ldd tool—available by default on all Unix systems—prints shared library dependencies for a given binary. Most binaries are compiled to depend on shared libraries and will not work without them. Using ldd with the name of a binary (or another shared library) as the first parameter gives a list of files that must accompany the binary to work. Trying ldd on /bin/bash gives the following output:

  # ldd /bin/bash
 => /lib/ (0x0088a000)
  => /lib/ (0x0060b000)
  => /lib/tls/ (0x004ac000)
           /lib/ => /lib/ (0x00494000)

Therefore, bash depends on four shared libraries. Create copies of these files in jail:

  # mkdir /chroot/lib
  # cp /lib/ /chroot/lib
  # cp /lib/ /chroot/lib
  # cp /lib/tls/ /chroot/lib
  # cp /lib/ /chroot/lib

The jailed execution of a bash shell will finally succeed:

  # chroot /chroot /bin/bash

You are rewarded with a working shell prompt. You will not be able to do much from it though. Though the shell works, none of the binaries you would normally use are available inside (ls, for example). You can only use the built-in shell com mands, as can be seen in this example:

  bash-2.05b# pwd
  bash-2.05b# echo /*
  /bin /lib
  bash-2.05b# echo /bin/*
  bash-2.05b# echo /lib/*
  /lib/ /lib/
/lib/ /lib/

As the previous example demonstrates, from a jailed shell you can access a few files you explicitly copied into the jail and nothing else.

{mospagebreak title=Using strace to see inside processes}

The strace tool (truss on systems other than Linux) intercepts and records system calls that are made by a process. It gives much insight into how programs work, without access to the source code. Using chroot and ldd, you will be able to get programs to run inside jail, but you will need strace to figure out why they fail when they fail without an error message, or if the error message does not indicate the real cause of the problem. For that reason, you will often need strace inside the jail itself. (Remember to remove it afterwards.)

Using strace you will find that many innocent looking binaries do a lot of work before they start. If you want to experiment, I suggest you write a simple program such as this one:

  #include <stdarg.h>
#include <stdarg.h>

  int main(void) {
      puts("Hello world!");

Compile it once with a shared system support and once without it:

  # gcc helloworld.c -o helloworld.shared
  # gcc helloworld.c -o helloworld.static -static

Using strace on the static version gives the following output:

  # strace ./helloworld.static
  execve("./helloworld.static", ["./helloworld.static"], [/* 22 vars */]) = 0
uname({sys="Linux", node="ben", …})  = 0
  brk(0)                                 = 0x958b000
  brk(0x95ac000)                         = 0x95ac000
fstat64(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 0), …}) = 0
  old_mmap(NULL, 4096, PROT_READ|PROT_WRITE,
  MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xbf51a000
  write(1, "Hello world!n", 13Hello world!
  )          = 13
  munmap(0xbf51a000, 4096)               = 0

The strace output is ugly. Each line in the output represents a system call made from the process. It is not important at the moment what each line contains. Jailed bina ries most often fail because they cannot open a file. If that happens, one of the lines near the end of the output will show the name of the file the binary attempted to access:

  open("/usr/share/locale/locale.alias", O_RDONLY) = -1 ENOEN T
  (No such file or directory)

As an exercise, use strace on the dynamically compiled version of the program and compare the two outputs. You will see how many shared libraries are accessed even from a small program such as this one.

Please check back next week for the conclusion to this article.

[gp-comments width="770" linklove="off" ]

chat sex hikayeleri Ensest hikaye