一、off by one漏洞利用的前置知识点
1.什么是off by one
off by one是指在输入的时候没有严格控制输入的数据大小,导致溢出了一个字节长度的数据。通常来说循环写入数据时没有控制循环次数(例如在循环结束时补充了‘\x00’,但是循环的次数却是缓冲区的大小),字符串处理不当(例如组合利用strlen和strcpy)
2.如何利用off by one
首先我们回顾一下chunk的结构
在已经分配的chunk里,chunk会利用下一个堆块的pre_size区域去存储数据,此时我们如果能溢出一个字节便刚好可以覆盖到size区域,从而构造出chunk overlapping。
3.chunk overlapping
chunk overlapping是堆题中十分常见的一种攻击手段。在堆里,操作系统并不会返回堆头的地址给用户,而是返回chunk的data区域,对于原本用户不具备操作权限的区域,如果此时产生了堆块的重叠,使得fake chunk的data区域正好覆盖了我们不可操作的区域,那么系统就会误以为这是一个分配的chunk,从而使得我们可以有办法控制原本不可控的地方。
二、例题 [BUUCTF]hitcontraining_heapcreator
1.程序分析
menu函数:经典的菜单界面
create函数:先申请一个0x10大小的chunk作为heaparray,然后存储接下来用户申请堆块的大小以及申请返回的指针(在合天网安的wp里分析到这里是一个结构体,但我ida看不出来,只能大致猜测)
edit函数:修改内容,但在read_input里它读取的buf长度是size+1LL,所以这里存在着off-by-one的漏洞
show函数:一个输出函数,用于输出heaparray中存放的信息。
写到这里的时候突然间领悟到heaparry的真面目。它的printf输出的是heaparray里面的数据,其中包括size和content,其中size是通过heaparray指针直接输出一个整数,说明这个地方存放了一个整型变量,然后指针+1的地方输出了我们申请堆块的内容,说明我们申请堆块后返回的指针被储存在这里。如果我们能修改这个指针的值指向任意区域,那么操作系统会误认为它有一个chunk被分配到这,通过其他的函数即可做到任意内存的读写。
delete函数:简简单单的free掉两个堆块。
2.漏洞利用大致逻辑
1.off-by-one造成chunk overlapping
2.edit修改指针到free_got,并且show打印出free_got的地址
3.计算出system地址,将free的got表覆写成system
4.执行free函数,并且将提前构造好的chunk释放,getshell!
3.具体利用
首先执行三次created函数,申请出六个堆块,其中只有三个chunk的data区是我们可以控制的。
1 | create(24,"aaaa")#0 |
接下来通过edit函数修改第一个chunk的内容造成off-by-one,修改下一个chunk的size域。
1 | edit(0,"AAAAAAAAAAAAAAAAAAAAAAAAA")#overflow |
现在我们可以看到第三个chunk,即第二个heaparray的chunk的size域被expend。
接下来释放这个堆块再申请回来之后,我们可以看到此时堆块的地址为0x1a3b2d0。
查看该地址附近的内容我们可以看到我们用来存储heaparray的chunk被包含在了我们的fake chunk里面,因此我们可以操纵里面的内容,剩余的就是打出free_got的地址,libc偏移找system地址,覆写got表,最后执行一次free函数。
4.exp
1 | from pwn import* |
其中多个接受语句是因为在尝试过程中发现’\x7f’这里会卡住接收不到,后来我就直接接收不设接收标志。
三、后记
说实话这题非常简单,但是我学习堆以来第一次打出flag的题目。在做题过程中发现gdb的魅力确实大嗷,遇事不决gdb看一下。另外有个小插曲,就是我的exp打不出来我本地的libc版本,但打buu的远程docker畅通无阻。
最近对于自己的人生道路感到些许迷茫,不知道走下去会遇到什么,能不能有所成就,但一直纠结于我能否成功无济于事,好好走下去吧。