Vulnerability Development mailing list archives

Re: Perl exploit (was: Some work needed)


From: Rafal Wojtczuk <nergal () IDEA AVET COM PL>
Date: Mon, 7 Aug 2000 12:37:43 +0200

Hello,
a) If you'll try to fool perl, forcing it to execute one file instead
   of another (quite complicated condition, refer to source code), it
[cut]
  This condition is quite easy to reach - my code is extermely ugly and
  slow (it's written in bash), so it requires reasonably fast machine
  (like pII/pIII x86 box). It can be optimized, of course.
        At last, some non-fully-trivial local vulnerability to study :)
Kudos to its discoverers.
        There are some interesting points regarding such (pseudo)race
conditions. Lets have a look at a fragment of "suidperl ./filename" strace
output:

open("./filename", O_RDONLY)            = 6
fcntl(6, F_SETFD, FD_CLOEXEC)           = 0
fstat(6, {st_mode=S_IFREG|S_ISUID|0755, st_size=51, ...}) = 0
setreuid(0, 500)                        = 0
getuid()                                = 0
geteuid()                               = 500
stat("./filename", {st_mode=S_IFREG|S_ISUID|0755, st_size=51, ...}) = 0

        Our exploit will succeed, if a context switch happens between open
and stat syscalls. The probability is small, but of course its enough if we
launch perl sufficient number of times. How can we improve our chances ?
        Everybody uses nice(1) in scripts exploiting race conditions. Am I
the only one who thinks it makes no sense ? On linux (I think on all sane
OSes) a niced process receives as large time quantum to execute as other
processes, only less often. So, the probability of the time slice to end
between "open" and "stat" remains the same.
        How about making "open" syscall consume all available time
quantum (or yield context) ? For instance, one can put "./filename" on NFS
mounted filesystem, this should be enough. But its even easier. Lets make a
script name (or, more precisely, a symlink which script name will point to)
contain a lot of path components, symlinks etc. The following script
#!/bin/sh
mkdir d
cd d
DOT=`perl -e 'print "../d/"x200'`
ln -s $DOT/2 1
ln -s $DOT/3 2
ln -s $DOT/4 3
ln -s $DOT   4
ONES=`perl -e 'print "1/"x500'`
ln -s `which passwd` passwd
ln -s $ONES/passwd ourlink
creats "ourlink" symlink. Resolved, it would have 400000 path components. On
my PC, stat("ourlink",&statbuf) call takes about 0.48 (!) seconds to execute
(to compare, a stat on a simple symlink takes about 1/230000 s). So, in
lcamtuf/sebastian's script, initially we need to point $FILENAME not
directly at $SUIDBIN, but at "ourlink".
        One more gotcha, we can't use standard rm and ln commands, as they
insist on stat-ing the file to be removed (therefore, they're slow). So,
instead of ln -sf, we must create a C program, which does simply
unlink($FILENAME);symlink("something",$FILENAME);
        Following these guidelines, I created a script (yes, bash is fast
enough :) ), which produces a root shell with only one perl execution - so,
this is really a pseudo race condition.

Save yourself,
Nergal


Current thread: