2013/01/15
tags: solaris issetugid(2) vulnerability
Child processes forget that they were once privileged in spite of the
fact that they continue to possess differing real and effective user
IDs. This could be a security problem for any program that relies on the
issetugid(2)
system call to return valid results.
Oracle have subsequently fixed this in Solaris 11.1 but not in earlier releases. In particular Solaris 10 has not been fixed and therefore applications should avoid using this system call on this version of the operating system.
issetugid(2)
was a new system call for Solaris 10. It was intended
to track the "setuidness" of an invoked program, no matter whether or
not it subsequently called setuid()
or if it fork()
ed or exec()
ed
another program. The manual page says:
The result of a call to issetugid() is unaffected by calls
to setuid(), setgid(), or other such calls. In case of a
call to fork(2), the child process inherits the same status.
Unfortunately issetugid()
fails to preserve its value after forking
children from a setuid program when uid != euid
in both Solaris 10
and 11.
Here is a simple demonstration program foo.c
:
#include <stdio.h> #include <sys/types.h> #include <unistd.h> #include <sys/wait.h> #include <stdlib.h> int main(int argc, const char *argv[]) { pid_t pid = 0; int stat_loc = 0; const char *execname = getexecname(); printf("%s: main: issetugid: %u\n", execname, issetugid()); pid = fork(); switch(pid) { case -1: perror("fork"); break; case 0: printf("%s: child: issetugid: %u\n", execname, issetugid()); printf("%s: child: uid: %u\n", execname, getuid()); printf("%s: child: euid: %u\n", execname, geteuid()); break; default: printf("%s: parent: issetugid: %u\n", execname, issetugid()); printf("%s: parent: uid: %u\n", execname, getuid()); printf("%s: parent: euid: %u\n", execname, geteuid()); (void)wait(&stat_loc); break; } return 0; }
And the results of running it:
alice@sol10:~$ gcc -o foo foo.c alice@sol10:~$ alice@sol10:~$ ./foo foo: main: issetugid: 0 foo: parent: issetugid: 0 foo: parent: uid: 1000 foo: parent: euid: 1000 foo: child: issetugid: 0 foo: child: uid: 1000 foo: child: euid: 1000 alice@sol10:~$ alice@sol10:~$ sudo chown root foo alice@sol10:~$ sudo chmod 4755 foo alice@sol10:~$ ./foo foo: main: issetugid: 1 foo: parent: issetugid: 1 foo: parent: uid: 1000 foo: parent: euid: 0 foo: child: issetugid: 0 # oops foo: child: uid: 1000 foo: child: euid: 0 alice@sol10:~$
Since the genealogy of this system call is BSDish, I tested the same
program on FreeBSD 9.0 where issetugid()
returns 1 in both the parent
and child, as expected.
Fortunately issetugid()
is relatively new and not greatly used. I
took a quick tour through OpenSolaris and found it in the following
libraries/library calls:
catopen(3)
libscf(3LIB)
libuutil(3LIB)
(calling function appears unused)libumem(3LIB)
(implements own issetugid()
?)fork()
/catopen()
occur in, for example, the following setuid root
binaries:
sdtcm_convert(1)
dtprintinfo(1)
None of these look directly exploitable, principally because conditions required are quite strict:
issetugid()
in a way that can be exploitedfork()
/exec()
/catopen()
certainly feels like it should be a good
lever though, in particular when programs are executed from su(1)
or
sudo(1)
.
Since a direct exploit could not be found Oracle fixed this under the Security in Depth programme as noted in the January 2013 Critical Patch Update. A CVE was not allocated.