By Jake Edge
February 1, 2012
A recent sudo
advisory described a "format string vulnerability" that could be used
for privilege escalation. Since sudo runs as setuid-root, that means that
it could potentially be used by a regular user—not just one listed in
the /etc/sudoers file—to compromise the system. As with
many security flaws, format string vulnerabilities are the result of
improper handling of user-supplied input. Given this latest report, it's
probably worth taking a look at how these kind of vulnerabilities come about.
For those who aren't C programmers, a little background may be in order. The
standard C library function for printing things to stdout is printf()—other
functions in the same family can be used to print to
stderr, character buffers, or other files.
That function
takes a string as its first argument which can contain special formatting
characters that describe the types of the rest of the arguments. For
example:
printf("hello, world\n");
printf("%s\n", "hello, world");
printf("%s, %s\n", "hello", "world");
would all print the canonical string to
stdout. The "
%s"
is the format specifier for a string, so the function expects the corresponding
argument to be a pointer to a null-terminated array of characters.
Members of the printf() family use the "varargs" (variable
arguments) facility of the C language to take an arbitrary number of
arguments. When the formatting string is parsed, values are popped off the
stack in the order they are listed. Those values are expected to be
there by the function, but, given the existing ABI, the compiler does not (in fact cannot) enforce
that they be placed there by caller. That's where the problem can occur.
In the easy case, compilers can and do warn when there is a mismatch between
the format string and arguments. A call like:
printf("hello, %s\n");
will cause a warning if the warning level is set high enough (like
-Wall for GCC). But those kinds of problems are relatively easily
found. A trickier problem occurs with something like:
printf(str);
which is perfectly legal as long as
str contains no formatting
characters. If it
does, however, the function will happily pop
things off the stack that don't correspond to the arguments in that
formatting string. For GCC, the
"
-Wformat -Wformat-nonliteral" flags can be used to detect
this kind of thing. In the "best" case, having format specifiers in
str will lead to a program crash,
in the worst, it could end up executing code. If
str comes from
user-supplied input, an attacker may be able to arrange just the right
formatting string to execute code of their choosing.
That may be bad enough for a program run as an unprivileged user (as the
attacker's code might be the equivalent of
"rm -rf $HOME"), but it is far worse if the program has
root privileges as sudo does. According to Wikipedia,
format string bugs were noted in 1990, but were not recognized as a
security problem until a researcher auditing proftpd reported a way to exploit the bug.
That exploit used the "%n" format, which stores the number of
characters printed so far to an integer pointer it pops off the stack. By
arranging just the right format string, the exploit would
overwrite the current user ID.
In the sudo case, the program name (which is stored in argv[0] for
C programs) was being printed as part of an error message. As the advisory
from the finder describes, the program name was "printed"
into a buffer (using a variant of sprintf()), and that buffer was
then handed off to a vfprintf() as the format string. That meant
that the user-controlled program name—which could certainly contain
format specifiers—was used as the format string for the
vfprintf().
The fix for sudo is to ensure that the program name is printed with a
"%s" specifier in the final print statement, rather than
building it into the earlier buffer.
How can the user control the program name, especially for a setuid binary
like sudo? That's not very hard either:
$ ln -s /usr/bin/sudo %n
The sudo advisory notes that building sudo with
-D_FORTIFY_SOURCE=2 will prevent these kinds of exploits, though
the advisory from the finder notes an article
in Phrack that may make it possible to bypass that protection.
The problem in sudo was
introduced relatively recently, for version 1.8.0 released at the end of
February 2011. It has now been fixed in 1.8.3p2 and affected distributions are
starting to get updates out. These kinds of bugs are yet another lesson
in the need for great care when handling user-controlled input.
(
Log in to post comments)