COMMAND
/usr/X11R6/bin/xterm
SYSTEMS AFFECTED
Linux Debian 1.2.8 (possibly others)
PROBLEM
David Luyer posted about another security vulnerability in xterm.
Firstly, the bug. What a joke. A segfault from xterm this
easily.
Putting a large string into in LC_CTYPE or LANG will cause xterm
from Debian-1.2.8 (the latest and supposedly stable and secure)
Linux to segfault.
Finding these was quite easy - here's a little script (this will
run and compile things under Linux and probably other unicies as
long as you have "sh" and "gcc"; the TestProgram currently uses
the "rc" shell but it's easy enough for someone to change) which
basically lets you overflow an arbitrary getenv call based on an
environment variable. The test script ran it overflowing each
getenv call under xterm to find the two mentoined above.
----------------------- begin script -----------------------------
#!/bin/sh
cat >getenv2.c <<EOF
extern char **__environ;
void *stdout;
char *getenv(register const char *name) {
register const int len = strlen(name);
register char **ep;
static int i;
static char *big_string_buf = 0;
if(!big_string_buf) {
if(!(big_string_buf = (char *)malloc(70000))) {
big_string_buf = "mallocfailed";
printf("Failed to malloc test string buffer.\n");
} else {
for(i=0;i<70000/4;i++)
memcpy(big_string_buf+i*4, "f00l", 4);
big_string_buf[70000] = '\0';
}
for (ep = __environ; *ep != 0; ++ep)
if (!strncmp(*ep, "ENV_TEST_VAR=", 13))
i = atoi(&(*ep)[13]);
}
printf(">> %s << ", name);
if(--i)
printf("- countdown %d.\n", i);
else
printf("- the lucky variable!\n");
fflush(stdout);
if(i) {
for (ep = __environ; *ep != 0; ++ep)
if (!strncmp(*ep, name, len) && (*ep)[len] == '=')
return &(*ep)[len + 1];
return 0;
} else
return big_string_buf;
}
EOF
cat >getenv.c <<EOF2
extern char **__environ;
char *getenv(register const char *name) {
register const int len = strlen(name);
register char **ep;
printf(">> %s <<\n", name);
for (ep = __environ; *ep != 0; ++ep)
if (!strncmp(*ep, name, len) && (*ep)[len] == '=')
return &(*ep)[len + 1];
return 0;
}
EOF2
cat >Makefile <<EOF3
all: getenv.so getenv2.so
getenv.so: getenv.o
@gcc -shared -Wl,-soname,getenv.so getenv.o -o getenv.so
getenv2.so: getenv2.o
@gcc -shared -Wl,-soname,getenv2.so getenv2.o -o getenv2.so
getenv.o: getenv.c
@gcc -c -fPIC getenv.c -o getenv.o
getenv2.o: getenv2.c
@gcc -c -fPIC getenv2.c -o getenv2.o
neat:
@rm -f getenv.c getenv2.c getenv.o getenv2.o Makefile
EOF3
make all
make neat
cat >TestProgram <<EOF4
#!/usr/bin/rc
# Using rc because my copy of it 'segmentation violation' to stdout
# and my /bin/sh doesn't. Stock debian rc may perform differently.
MAX=\$1
shift
LD_PRELOAD=()
for (ENV_TEST_VAR in \`{awk 'BEGIN{for(i=0;i<'\$MAX';i++){print i};exit}'}) {
echo Testing \$ENV_TEST_VAR -
LD_PRELOAD=./getenv2.so
\$*
}
##!/bin/sh
#MAX=\$1
#shift
#for ENV_TEST_VAR in \`awk 'BEGIN{for(i=0;i<'\$MAX';i++){print i};exit}'\`
#do
# export LD_PRELOAD=./getenv2.so
# export ENV_TEST_VAR
# \$*
#done
EOF4
cat >TestXterm << EOF5
#!/bin/sh
./TestProgram 60 /usr/X11R6/bin/xterm -exec /bin/false -geometry 1x1+1+1 > log.xterm 2>&1
EOF5
cat >testprog.c <<EOF6
main() {
getenv("AWAY");
if(strlen((char *)getenv("HOME")) > 50000) {
raise(11);
}
}
EOF6
gcc testprog.c -o testprog
./TestProgram 3 ./testprog > log.testprog 2>&1
rm testprog testprog.c
echo Executing \"grep -3 segmentation log.testprog\"
echo ++++++++++
grep -3 segmentation log.testprog
echo ++++++++++
echo Make sure you have X authentification and \$DISPLAY set up and
echo then run \"TestXterm\" to check for environment variable buffer
echo overflows in xterm \(they\'ll show up in log.xterm if the above
echo test worked\).
What this creates:
TestProgram - the getenv insecurity checker, usage eg:
./TestProgram 60 /usr/X11R6/bin/xterm -exec /bin/false -geometry \ 1x1+1+1 > log.xterm 2>&1
TestXterm - the above line in a script :)
getenv.so - LD_PRELOAD to this to list all getenv calls
getenv2.so - LD_PRELOAD to this and ENV_TEST_VAR to a variable
number to attempt an overflow on that variable number
log.testprog - the output from the sample test (a program
compiled to segfault easily to make sure these
scripts work on your system)
Here's the proper contents of log.testprog if it all works on
your system (ie, if you have the rc shell installed and handling
signals properly - sh doesn't log signals to stdout or stderr on
tested Debian system, but rc does. David's not sure if the
Debian default rc manages it tho)
---
z# cat log.testprog
Testing 0 -
>> MALLOC_CHECK_ << - countdown -1.
>> AWAY << - countdown -1.
>> HOME << - countdown -2.
Testing 1 -
>> MALLOC_CHECK_ << - the lucky variable!
>> AWAY << - the lucky variable!
>> HOME << - countdown -1.
Testing 2 -
>> MALLOC_CHECK_ << - countdown 1.
>> AWAY << - countdown 1.
>> HOME << - the lucky variable!
segmentation violation
z#
---
If that's all fine, chances are you can run "./TestXterm" and then
"grep -3 segmentation log.xterm" to find:
---
z# grep -3 segmentation log.xterm
>> RESOURCE_NAME << - countdown 1.
>> LC_CTYPE << - the lucky variable!
>> XLOCALEDIR << - countdown -1.
segmentation violation
Testing 27 -
>> MALLOC_CHECK_ << - countdown 26.
>> XLOCALEDIR << - countdown 25.
--
>> LC_CTYPE << - countdown 1.
>> LANG << - the lucky variable!
>> XLOCALEDIR << - countdown -1.
segmentation violation
Testing 28 -
>> MALLOC_CHECK_ << - countdown 27.
>> XLOCALEDIR << - countdown 26.
z#
---
SOLUTION
Well, let's see. This program is going to tell you if you're
vulnerable or not. If you are, apply new (upgrade, patch or
whatever) software. If you are not, sleep well.
This bug is fixed for x86 unices in XFree86 3.2 and for others in
X11R6.3. Or at least documentation and developers claim so. It
has been tested it on XFree86 3.2, and it doesn't segfault xterm
(while test program, of course, gets its SIGSEGV).