International * Contact  * Sitemap  * Links  * Register Software
Search  
 SUSE - simply change

Home Users

 Novell
  | Home  |  | Overview  |  | Products  |  | Support  |  | Downloads  |  | Distributors & Resellers  |
  SUSE LINUX Support   Online Help   License information   Security   Feedback
  Printable page

Security-specific Programming Errors (Part 4)

Thomas Biege

Table of Contents Dynamic Storage and free(3)

Shortly after the discovery of how format bugs could compromise security, another interesting bug was revealed. To better understand this bug, we will first provide a rough explanation of how [m,c,re]alloc(3) and free(3) function as implemented in most Linux systems.

Storage is managed using memory blocks in a doubly linked list. A block looks like this:

Structure of a 
memory block

Internally, malloc(3) works with the chunk pointer. The programmer gets the pointer mem = ((char *) chunk) + 8, which points directly to the dynamically allocated memory.

When a list element or memory block (chunk) is unused i.e., does not point to allocated memory, then fd and bk are used as pointers for the list elements, otherwise they contain data.

The size of the previous block is reflected in prev_size; size contains the size of the current block.

If free(3) is now used to free the memory, the chunk pointer first needs to be calculated with a simple subtraction (8 bytes on 32-bit architectures). This pointer is passed to the internal function chunk_free().

In this function, the block is re-inserted in the doubly linked list after ensuring that the current block's predecessor and successor are not being used, too. To determine whether or not a block is being used, a logical AND can be placed between the size element and the PREV_INUSE constant.

To reintegrate the block in the list, the fd and bk pointers of the previous block and of the following block are required. The unlink() macro is used to bend the pointers.

#define unlink(P, BK, FD) \
{                         \
    BK = P->bk;           \
    FD = P->fd;           \
    FD->bk = BK;          \
    BK->fd = FD;          \
}

If an attacker has the chance to freely define the argument for free(3), then he is capable of modifying the contents of the memory via the unlink() macro. By skilfully setting the fd and bk pointers, function pointers, return addresses, the contents of variables etc. can be modified in order to compromise the security of a system.

It is therefore important to make sure that free(3) calls are always based on chunks of memory that have actually been allocated!

 

Chroot environments (not limited to C!)

With the chroot(2) system call, the view of a process on the file system can be altered.

For security reasons, (server) daemon processes are often banished to a so-called chroot jail (but not often enough, unfortunately). This means that the UID and GID is modified by privileged processes, the process switches to a specially prepared directory, and this directory becomes the new root directory.

If a software bug in this server program is now exploited, the attacker can only act with the reduced user rights and only in the chroot directory. Access to higher-level directory structures is denied.

 

However, as the daemon process has to access specific resources in the file system at run-time, shared libraries (these are not required if the program was compiled statically), device, system and configuration files have to be copied into the chroot directory.

Serious errors are often made when setting up chroot environments.

Sensitive Data

Some programs such as FTP servers require the password file in order to correctly display the file owners and groups. This is why, in the past, the file /etc/passwd was often simply copied to ~fptd/etc/. An attacker could simply log in via anonymous FTP, transfer the passwd file to his computer and try to decipher the passwords.
Sensitive data should never be placed in the chroot environment unless absolutely necessary.

Device Files (Special Files)

When the server program requires device files, attention should be paid to the device types. Devices like /dev/zero and /dev/null are unproblematic; however, if Special Files exist, which allow access to the memory (e.g., /dev/kmem) or the hard drive (e.g., /dev/sdb3) and similar, then an attacker will be able to read sensitive data (depending on access rights), set up new accounts or modify the kernel of the operating system and thus gain further access to the system.

Privileges

If the server's rights are not completely dropped, which means the program still runs with root rights, breaking out of the chroot jail is no problem.

A lot of administrators and programmers feel safe when they assign their UID/GID to nobody or similar for their server. The problem arises when these IDs are not used explicitly by this server, but also by other programs in the system.

The attacker can now use signals to disrupt, pause or terminate the other programs. He can even use ptrace(2) to read and modify system calls and thus gain access to confidential information like passwords, or execute commands outside the chroot environment.
Moreover, dropping the additional GID's is often forgotten or the sequence of the system calls is wrong.

[...]
setpwent();
pwd_ptr = getpwnam(<user>);
if(chroot(<jail>) || chdir("/"))
     [EXIT]

if(setgroups(0, 0) < 0) // drop supplementary groups
     [EXIT]

if(setgid(pwd_ptr->gid) || setuid(pwd_ptr->uid)
     [EXIT]
endpwent();
[...]

The endpwent(3) is necessary to close the open file descriptor for the password database again. (see next section) For logging via Syslog, openlog(3) must of course be called before chroot(2).

Open File Descriptors

If the attacker succeeds in separating his own programs from the server process (with memory overflows, for example), he can access open file descriptors. He can read data outside the chroot jail if the file descriptors point to files that are not located in the chroot directory, or he can even break out of the chroot jail using fchdir(2) when the descriptors point to directories (opendir(3)).

If the file descriptors point to sockets, then it is possible for the attacker to read all the network packets of the collision domain, all the packets to the computer, or all the packets for the server depending on the socket type.

With a socket descriptor, the attacker may even be in a position to impersonate the server, exploit security flaws in the clients or at least disrupt operation.

Via open descriptors that point to TTY's it is possible to write commands to the TTY's input buffer with ioctl(2).

Network API

In the chroot environment, an attacker can use his own network-capable programs without any problem in order to bypass any packet filter restrictions or TCP/UDP wrappers by using the loopback interface for communication or simply because he is handled less restrictively by the access control lists (ACLs) owing to his IP address.
Of course, the attacker can also install his own server.

Directory Links

If the chroot directory contains links that point to directories outside the "jail", then breaking out via cd(1) is also child's play.

Access Rights

Only the process that is "locked inside" should have write access to the chroot directory, as otherwise the conditions (see above) could be modified "from the outside", which simplify the process of breaking out of the chroot jail.
Besides the access possibilities to files and directories, the rights of IPC structures e.g., shared memory should be as restrictive as possible.

/proc File System

If the /proc file system in the chroot jail is mounted, then it is possible to access the rest of the file system with a simple cd /proc/1/root/ or a similar command.

The security of chroot environments is thus based on correct programming and error-free configuration of the directory. Structure of a Chroot environment:

[...]
#ifdef OPEN_MAX
     static long maxopenfd = OPEN_MAX;
#else
     static long maxopenfd = 0L;
#endif
[...]

/* drop privileges */
if(setgid(<special GID>) < 0)
     [Exit]
if(setuid(<special UID>) < 0)
     [Exit]

/*
** We set the Close-on-Exec flag on all open file descriptors.
** If an attacker spawns a new process due to a buffer overflow
** s/he won't be able to access our open file/network
** handles.
*/
if(maxopenfd = 0)
     if((maxopenfd = sysconf(_SC_OPEN_MAX)) < 0)
          maxopenfd = OPEN_MAX_LINUX;

for (i = 3; i <= maxopenfd; i++)
{
     oldval = fcntl(i , F_GETFD, 0L);
     (void) fcntl(i, F_SETFD, (oldval != -1) ? (oldval |= FD_CLOEXEC) :
                                  FD_CLOEXEC);
}

if(chroot(<special Dir>) < 0)
     [Exit]
if(chdir("/") < 0)
     [Exit]

[...]

The configuration of the directory depends to a large extent on the application and is therefore not shown here.

Further Information

* Reseller
* Reviews
* Support Database
* Hardware Database
* Education Program

Quick Links

* Security
* Support Portal
* Mailing Lists
* Feedback
* SUSE LINUX eNewsletter

Subscribe now!

Get the Live DVD and Run Linux in Seconds!

SUSE LINUX 9.1 Personal Live CD

Want a hassle-free way to try Linux? Download SUSE LINUX Professional 9.2 Live DVD. It runs completely from your DVD drive. No need to install anything.

 This server is powered by NPS.
Linux is a registered trademark of Linus Torvalds.
Last changed: 05.12.2001 17:21 MET by webmaster@suse.de