ycb_2020_babypwn
总结
根据本题,学习与收获有:
-
stdout
结构体上方和malloc_hook
上方均能伪造大小为0x70
的chunk
。一个用来泄露libc
地址,一个用来getshell
。 -
当程序没有
show
功能的时候,可以利用fastbin attack
,这时候,可伪造大小为0x70
的fastbin chunk
到stdout
结构体的上方,将flag
修改为0x0FBAD1887
,将_IO_write_base
的低字节修改一下,比如修改为0x58
。 -
有时候,直接劫持
malloc_hook
为one_gadget
可能无法滿足条件,这个时候,可以利用malloc_hook
上方的realloc_hook
,利用realloc
函数开头的几个pop
指令,来调整栈帧。这个时候,设置realloc_hook
为one_gadget
,malloc_hook
为realloc
函数地址加上一个偏移,这里的偏移可以慢慢调试,选取2、4、6、12
等。 -
构造
overlapped
的chunk
的时候,有时候并不一定需要完全改写整个fd
指针的内容,可以根据偏移只改写部分低字节。 -
main_arena+88
或者main_arena+96
距离stdout
上方的fake chunk
地址很近,只需修改低2
位的字节,低1
位的字节,固定为\xdd
。
题目分析
checksec
可以看到,保护全开。
函数分析
main
同样的,函数我均已经重命名过了。方便做题。很典型的菜单题。
menu
选项很简单,只有添加和删除。
Add
有几个点需要注意一下:
- 最多只能分配
20
次 - 每次分配用户指定大小的
chunk
前,会分配一个0x30
大小的chunk A
用来管理后面的chunk B
- 用户指定的大小不能超过
0x70
,也就是说,所有的为用户分配的chunk
,范围都在fastbin
A[0]
写的是1
,A[1]
写的是chunk B
的地址,A[2]
开始,写的是messgae
,且没有溢出。
Delete
这里需要注意:
- 只释放了上面的
Add
函数中的chunk B
, 没有释放有管理功能的chunk A
,但是把A[0]
写为了0
。 - 释放后指针没有置空,存在
uaf
。
漏洞点
题目很精炼,漏洞点也比较好找。就是在Delete
函数中,存在的一个uaf
漏洞。由于靶机的环境是ubuntu 16.04
,使用的libc
版本为libc-2.23.so
,因此,很显然就想到了使用fastbin double free attack
。
利用思路
知识点
fastbin
对double free
的检测,是有一定的缺陷的。不像后来的tcache bin
的检测,会去检查整条链中是否存在一样的被释放的chunk
,fastbin
只会去检查上一个chunk
与当前的要释放的chunk
是不是一样的。fastbin double free
利用的过程为free A ----> free B ----> free A
。这里的A、B
的大小要一样。之后,分配第一次的时候,改写fd
指针为指定地址,然后连续分配两次,第四次分配,就能到指定地址获取chunk
。也就是说,这里需要分配4
次,才能分配到fake chunk
。
利用过程
由于题目没有edit
的功能,所以利用起来还是很麻烦的,需要反复地进行malloc
与free
。
整体的利用思路如下:
- 构造出
A-->B-->A
的overlapped
的fastbin chunk
,同时做好堆内容的填写,便于使用fake chunk
- 修改
A
的fd
指针的低字节,分配到fake chunk C
处,让这个chunk C
能修改到chunk B
的size
域和fd
域 - 修改
chunk B
的size
域,使其大于等于0x90
,保证释放后能被放在unsorted bin
中去,且fd
和bk
指针被写入一个libc
地址 - 修改上面
chunk B
的fd
的低2
个字节,分配到stdout
结构体上方,这里需要爆破一下。 - 修改
stdout
的flag
字段和write_base
的低字节,获取到libc
地址 - 利用
fastbin double free
分配到malloc_hook
,利用realloc + one_gadget
来get_shell
详细利用步骤:
- 分配两个
0x70
大小的chunk 0
和chunk 1
,并把内容填充为0x0000000000000071
,方便后续伪造chunk
- 依次释放
chunk 0--->1--->0
,然后分配大小为0x70
的chunk 2
,修改fd
的低字节为0x20
,继续分配chunk 3、4
- 分配
chunk 5
,那么chunk 5
就能改写chunk 0
的size
和fd
域 - 先释放
chunk 0
,再释放chunk 5
,然后分配chunk 6
,修改chunk 0
的size
域为0x91
- 再释放
chunk 0
,这样就得到了一个unsorted bin
,且把释放了的chunk 0
的fd
写为了一个堆地址 - 分配一个
0x30
大小的chunk 7
,避免后续分配管理的chunk
的时候,从unsorted bin
里面切割。 - 再次释放
chunk 6
,分配chunk 8
,修改chunk 0
的size
为0x71
和fd
的低2
个字节,使其fd
指向stdout
结构体上方的那个fake chunk
- 分配到
stdout
上方的fake chunk
,修改stdout
结构体的flag
和write_base
,泄露出堆地址 - 利用
double free
分配到malloc_hook
附近,结合realloc
调整栈帧,利用one_gadget
获取shell
EXP
调试过程
这里展示本地调试的过程,手动输入需要爆破的那个字节大小。
首先准备好各个函数:
|
|
分配两个chunk
并释放,构造overlapped chunk
:
|
|
修改低字节为0x20
:
|
|
修改chunk 0
的size
域为0x91
,得到unsorted bin chunk
,并构造出fastbin
与unsorted bin
重合的堆布局,准备好0x30
大小的chunk
,避免切割unsorted bin
:
|
|
修改chunk 0
的size
为0x71
,修改fd
指针的低2个字节,释放掉好0x30
大小的chunk
:
|
|
首先看要修改的那个fake chunk
的地址:
可以顺便看一下stdout
结构体:
这里我们输入0x75
就能分配到这个fake chunk
处。
分配到stdout
结构体上方,泄露出libc
地址:
|
|
再次分配到malloc_hook
,并利用realloc
调整栈帧,这里选则的偏移是0xd
:
|
|
完整exp
完整的exp
是需要爆破的:
|
|
爆破的效果如图:
引用与参考
无