Off-by-one漏洞是一种常见的编程错误,它发生在程序访问数组或缓冲区时,由于索引计算错误,导致访问了数组或缓冲区的最后一个元素之外的内存位置。这种错误可能使程序读取或写入相邻内存区域的数据,造成信息泄露、数据损坏或安全漏洞,如缓冲区溢出,允许攻击者执行任意代码。
roarctf_2019_easy_pwn
代码审计
//v4 = sub_E26(*(&unk_202044 + 4 * v3), v2); |
可以分析出,如果edit_size-add_size = 10,那么我们便可以写入,add_size+1字节的数据,造成_off-by-one_漏洞。
利用思路
泄漏_libc_
修改_chunk1_size_
先创建4个_chunk_
add(0x18) |
如图:
往_chunk0_中写数据,通过_off-by-one_篡改_chunk1_的_size_字段为0xa1,使得_chunk_hook1_与_chunk_hook2_指向的空间,部分重叠。
payload = b'a'*0x18+b'\xa1' |
如图:

写入_fake_chunk2_size_
现在,还需要解决一个问题:
_chunk1_的_size_被修改后,_real_chunk2_的部分空间与_chunk1_共享,所以现在会出现一个_fake_chunk2_以0x559e6a0a70c0这个地址作为首地址。又因为0x559e6a0a70c8处为0,故现在_fake_chunk2_的_size_为0,也因此,_chunk3_即使是正常的,也无法被划分空间。
我们现在需要在0x559e6a0a70c8处写上_fake_chunk2_的_size_,怎么写呢?不要忘了,_real_chunk2_并未改变,_chunk_hook2_也仍然是原来的_chunk_hook2_!edit(2,size,content),就可以很正常的往0x559e6a0a70c8里写数据。
payload = p64(0)*0xe+p64(0xa1)+p64(0x21) |
如图:
修改_chunk1_hook_size_
现在,还需要再解决一个问题:
本题中,能打印出多少字符,是由_chunk_hook_前面一个地址里的内容决定的。而现在,_chunk_hook1_前面的地址里,仍然是0x0000001000000001,也就是说当show(1),我们依然只能打印出0x10个字符。怎么解决呢?
不要忘了,实际上的_chunk1_的_size_已经是0xa0了!所以我们free一次_chunk1_,再malloc一次0x90的内存,_chunk_hook_区域的_size_自然会变成0x90。
delete(1) |
如图:

被刷新为0的_real_chunk2_size_:
重新写入的_real_chunk2_size_:
现在,我们终于可以,利用_chunk_hook1_与_chunk_hook2_这两个指针泄漏_libc_地址了。
delete(2) |
one_gadget_获取_shell
新设_real_chunk2_size_
再次申请_real_chunk2_,此时_real_chunk2_的所有数据都已被清除。需重新设置_real_chunk2_的_size_为0x71(如此才能落入_fast bin_中),_fake_chunk2_的_size_为0x21,继续对其利用。
add(0x90) |
修改_fast bin_中的_fd,bk_
释放_real_chunk2_到_fast bin_中,然后通过往_chunk1_里写数据,修改_real_chunk2_的_fd_和_bk_,为我们在_malloc_hook_附近构造的_chunk_。
delete(2) |
劫持两个_hook_
从_fast bin_中回收_real_chunk2_,及构造的_fake_chunk_。然后覆盖__realloc_hook的值为one_gadget,__malloc_hook的值为realloc+4。
add(0x60) #因为real_chunk2_size = 0x71 |
exp
from tools import * |
hitcontraining_heapcreator
_off-by-one_利用思路
==利用_off-by-one_,来改写下一个_chunk_的指针_chunk_的_size_,进而实现下一个_chunk_的指针_chunk_与内容_chunk_的部分重叠和互换。==
代码审计
一共有_create_heap_,_edit_heap_,_show_heap_,_delete_heap_四个函数,_edit_heap_函数中有一个堆溢出漏洞,只可以溢出1个字节。
本题中,每_malloc_一次会创建一个指针_chunk_和一个内容_chunk_(其实就是两个地址空间,分别存储着指向_chunk_中数据的指针,和_chunk_的数据),而_free_一次也就是把这两个地址空间都释放掉了。_show_heap_和_edit_heap_函数中打印和修改的内容都是由指针决定的,指针指向什么就打印和修改什么。
思路
改写_size_
free_got = elf.sym['free'] |
先创建两个_chunk_,0x18是为了在往_chunk0_中写内容时,直接覆盖掉_chunk1_的指针_chunk_的前8个字节,然后就可以利用溢出的一个字节改写_chunk1_的指针_chunk_的_size_。
如图,指针_chunk_的_size_值已经被改写成0x41

此时,_chunk1_的指针_chunk_与内容_chunk_,已经部分重叠(因为是指针_chunk_的_size_被改写成0x41,内容_chunk_的_size_还是0x21,所以如图
泄漏_free_addr_
delete(1) |
delete(1)后我们可以看到_fastbins_有两个不同大小的_fastbin_chunk_,如图

然后我们再申请0x30的_chunk_用来存储数据(还有0x10的空间存储_prve_size_和_size_),又因为_fastbins_中正好有0x40的_fastbins_chunk_,所以0x12c2040及下面0x40的内存空间就成了新_chunk_的内存_chunk_,0x12c2060及下面0x20的内存空间就成了新_chunk_的指针内存。
然后我们把_free@got_写到内存_chunk_的0x12c2078地址里,这个地址存储着指针_chunk_里的指针,所以现在指针就是_free@got_,而_show_函数打印出来的数据是该指针指向的内容,也就是_free@got_指向的内容。

覆盖_free@got_表
libc_base = free_addr-libc.sym['free'] |
同样,_edit_函数修改_chunk_内容,也是修改指针_chunk_的指针指向的区域。得到_system_的地址后,直接用_edit_函数修改_chunk1_的内容即可。

最后直接_free_掉_chunk0_,便能执行system("/bin/sh"),得到_shell_。
exp
from tools import * |
拿到_flag_
