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 5)

Thomas Biege

Table of Contents Programming environment (not limited to C!)

Every program runs in a programming environment that can affect program behavior. The environment consists of a field of variables and its value, for example, TIMEZONE=GMT or USER=thomas.

For example:

environ[0] = "USER=thomas";
environ[1] = "HOME=/home/thomas";
environ[2] = "MAIL=/var/spool/mail/thomas"
.
.
.

Generally, a program should rely on its environment as little as possible, since a local user can change the environment as he sees fit.

Privileged processes that call other programs should always discard their environments and create a new secure one that the called programs inherit.

Below is a (probably incomplete) overview of several environment variables that can cause problems. In general, you should avoid using the environment as a source of information as much as possible. Depending on the type of Unix variant, the variables and their names differ, while others do not cause problems on all systems or on older systems only.

PATH

The PATH variable contains a list of directory names that the system uses to search for the correct path when program names are specified. The following library functions use the path variable: execlp(3), execvp(3), popen(3) and system(3). You should always use the complete path names in your programs, since a malicious user need only alter the list of path names in order to execute without permission his own program with the same name. Generally, you should not use popen(3) and system(3), since unpredictable interactions with complex command shells creates an unacceptable security risk!

IFS

IFS contains numerous letters used as separators for shell arguments. Normally, all of these characters are whitespaces. If an attacker defines IFS=o and the program executes the command "/bin/show", 'o' is considered the separator and the parameter "w" calls "/bin/sh". This means that even if you use the full path name to call a shell or popen(3) or system(3), you are still vulnerable to attack through IFS (popen(3) and system(3) also use a shell to perform their task). Fortunately, modern command shells ignore the value for IFS.

LD_PRELOAD / LD_LIBRARY_PATH

With LD_PRELOAD (the name may differ from system to system) you can explicitly specify the path name to a shared library (or DLL).
If an attacker now diverts the path name to the standard C library to his own library, he has free reign to control the functions' behavior as he wishes. For example, the attacker can use the privileged login program "/bin/login" to get to a command shell with additional access rights if he replaces the crypt(3) function so that the password generation process always returns the cipher text for the root user or so that all string comparison functions return the value 0. Fortunately, in more recent libc implementations, processes with additional access rights ignore this variable and similar ones. Keep in mind, however, that other processes not created by the privileged process and that do not have their own SetUID/ GID flags once again use LD_PRELOAD. This situation can create security risks if the additional rights are not revoked prior to the creation process.

You can also use unset(3) to delete environment variables. However, this method is not reliably secure, since the program environment can include multiple environment variables, with the potential result that the version set by the attacker may not be deleted.

For these reasons, you should always redefine the environment from the ground up prior to creating a new process.

 

User Input (not limited to C!)

You should limit the number of permissible characters wherever the system is reading user input. In general, users need to enter only a-z, A-Z and 0-9 characters as well as punctuation marks. This reduces the vulnerability to attacks, since users can no longer enter binary data, formatting characters or escape sequences.

 

Open Resources

You should close all file descriptors, including directories, IPC handles and sockets, before a privileged program creates a user-defined process. For maximum protection, we recommend that you set the close-on-exec flag for all security-specific file descriptors (with restrictions, of course) immediately after opening them.

 

0,1,2 - The Standard File Descriptors (not limited to C!)

A program's first three file descriptors are assigned to standard input, standard output and standard error output. However, if the parent process closes these file descriptors and the child process then opens a raw socket, the socket is assigned to the first available file descriptor. If this is the descriptor for standard output or standard error output, the system sends data over the network every time the program outputs data. If the attacker is able to control the output data, he does, in fact, control the socket himself.

Thus, you should always check the first three file descriptors and define them yourself, if needed:

For example:

[...]
int fd = 0;

while(fd 
< 2)
{
     if( (fd = open("/dev/null", 0600)) < 0)
          return(-1);

}
[...]

OpenBSD systems with newer kernels and Linux systems with newer glibc versions are no longer vulnerable to this kind of attack.

 

Access Rights (not limited to C!)

As open as needed and as restricted as possible. This should be the ground rule. Some mistakes or oversights often occur in connection with access rights:

Erroneous entries

With open(2) or similar functions, entries for the mode argument do not consist of S_* macros from sys/stat.h or the entered octal value is incomplete. If you enter 600 (decimal) as the value for mode instead of 0600 (octal), the system does not create the file as desired, that is, as a file that only the owner can read or write to.

umask(2)

You can use umask(2) to set masks for creating files. This means that the mode argument is linked to the mask as follows: mode & (~mask)

Thus, a mask with the value 0027 ensures that every entry created in the file system - that is, not just files, but FIFO's and device files as well - is protected from being edited, read and executed by any and all users. The group can only read and execute the file (which is actually the same thing, since the operating system kernel must read a file before executing it) but cannot edit it. An unmask value of 0027 is a good starting point.

IPC

Programmers often forget to set access rights for FIFO's, shared memory chunks, Unix domain sockets, etc., so that anyone can read or even edit them. Thus, a malicious user can read information he is not authorized to see or interfere with and manipulate at will the process that reads from the IPC structure.

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:22 MET by webmaster@suse.de