next up previous
Next: Juggling with UIDs Up: Setuid applications Previous: Setuid-ness is a Design

Be Careful with that Axe, Eugene

One of the crucial considerations in the design of secure applications is, where does this piece of data come from, and can I trust it? And one of the axioms of setuid applications is, anything that can be manipulated by the user invoking the program is not trustworthy.3.3 Any user could be an attacker! They're out to get you!

Let's start with a little easy-going exploit of an environment variable. Consider a setuid root application that does many useful things, including displaying a lengthy help message loaded from some external file. Because the program should support different languages, there's one help file for each language. The program extracts the preferred language from the environment variable LANG and uses that value to construct the path of the help file to display:

    char      name[1024], *lang;

    if ((lang = getenv("LANG")) == NULL)
        lang = DEFAULT_LANGUAGE;
    snprintf(name, sizeof(name), "/usr/share/foo/%s/help", lang);
    display_helpfile(name);

This looks reasonable: the LANG environment variable usually takes values such as C (for the default locale), en for English, fr for French, etc.

The key word is ``usually'' here. In a security context, you should never assume that anything the user gives you behaves normally.

So, if we start to think like an attacker, what can we come up with if we think about ``creative'' uses of the LANG variable? Definitely something with ../ in it, so we can make the program access files outside of /usr/share/foo. One common goal in attacking a setuid program this way is to make it display files only root can read by default, such as the /etc/shadow file that contains the encrypted user passwords.3.4 In the case of our hypothetical little application, an attacker would probably go about it like this:

$ ln -s /etc/shadow /tmp/help
$ export LANG=../../../tmp
$ the_little_app_that_couldnt --help
[... contents of shadow file go here ...]

What we've just demonstrated is that it is bad to trust that environment variables contain sane values. The environment is under the complete control of the user invoking the application, and he can modify it as he pleases.

Of course, what we've shown here is just one possible attack involving environment variables. Other applications lend themselves to other types of attacks. For instance, there have been cases where an application would write data to a file, and the name of that file could be controlled by an environment variable. You can easily imagine that the problems caused by such a bug are even worse than the ability to display /etc/shadow!

There are several ways in which one can deal with a situation like this. In the case of our broken setuid program, it would suffice to make sure that whatever the contents of the LANG variable, the user is never able to display a file outside of /usr/share/foo. The only way he can do it is by using ``..'' somewhere in there, so the following little change stops the attack for good:

    char      name[1024], *lang;

    if ((lang = getenv("LANG")) == NULL
     || strstr(lang, "..") != NULL)
        lang = DEFAULT_LANGUAGE;
    snprintf(name, sizeof(name), "/usr/share/foo/%s/help", lang);
    display_helpfile(name);

Whenever the language specified by LANG contains ``..'', we simply ignore it and fall back to the default locale. Note that looking for /../ does not accomplish the same, because it would still allow an attacker to specify LANG=../otherdir which could also be problematic.

This fix is very specific to the problem of our application. It's an easy fix because we know exactly the files we can display safely (those below /usr/share/foo), and those we can't. However, in many cases you do not have a priori knowledge of which files are ``safe'' and which aren't. What we need is some sort of mechanism that tells us whether the calling user would be allowed to read or write the file in question.

There are several such mechanisms. Let me start with one such mechanism that is broken, the access system call. Well, it's not generally broken, but it is subject to a certain type of attack in so many cases that it's better to forget about it completely. We will return to access and why it deserves such a harsh verdict in chapter 4.

Briefly describe why access is bad, but refer to the temp races chapter for a discussion of why race conditions are a real threat

Another mechanism is to open the file with the privilege of the invoking user, along the lines of this:

    char    *filename;

    filename = getenv("FOOBAR_FILE");

    magically_become_the_calling_user();
    display_file_contents(filename);
    magically_become_setuid_again();

Now the call to fopen happens with the privilege of the user who invoked the setuid application. Now he can modify the environment any way he wants, but the best he can hope for is to make our application open files he could view himself using much more convenient tools such as vi.

This magic mechanism is called dropping privilege, because your program (at least temporarily) gives up the additional privilege it acquired by being setuid. The mechanics of this will be discussed in the next section, but we will later come across many more uses of this technique.


next up previous
Next: Juggling with UIDs Up: Setuid applications Previous: Setuid-ness is a Design
Olaf Kirch 2002-01-16