zoukankan      html  css  js  c++  java
  • UAF学习原理及利用

    0x00 UAF原理

     

    如上代码所示,指针p1申请内存,打印其地址,值

    然后释放p1

    指针p2申请同样大小的内存,打印p2的地址,p1指针指向的值

    Gcc编译,运行结果如下:

     

    p1与p2地址相同,p1指针释放后,p2申请相同的大小的内存,操作系统会将之前给p1的地址分配给p2,修改p2的值,p1也被修改了。

    由此我们可以知道:

    1.在free一块内存后,接着申请大小相同的一块内存,操作系统会将刚刚free掉的内存再次分配。

    根本原因是dllmalloc:

    参考资料:http://blog.csdn.net/ycnian/article/details/12971863

    当应用程序调用free()释放内存时,如果内存块小于256kb,dlmalloc并不马上将内存块释放回内存,而是将内存块标记为空闲状态。这么做的原因有两个:一是内存块不一定能马上释放会内核(比如内存块不是位于堆顶端),二是供应用程序下次申请内存使用(这是主要原因)。当dlmalloc中空闲内存量达到一定值时dlmalloc才将空闲内存释放会内核。如果应用程序申请的内存大于256kb,dlmalloc调用mmap()向内核申请一块内存,返回返还给应用程序使用。如果应用程序释放的内存大于256kb,dlmalloc马上调用munmap()释放内存。dlmalloc不会缓存大于256kb的内存块,因为这样的内存块太大了,最好不要长期占用这么大的内存资源。

    2.通过p2能够操作p1,如果之后p1继续被使用(use after free),则可以达到通过p2修改程序功能等目的。

    0x01 一个利用场景

    在网上找了一个有UAF漏洞的ctf程序,参考博客如下:

    http://www.syjzwjj.com/use-after-free-tutorial/

    作者已经分析的很详细了,通过对整个程序的调试,来理解UAF的利用。

    1.       结合IDA与程序运行,简要理解程序的功能。

     

    选择1可以留言,信息会存到1个链表中

    选择2将会遍历链表找到对应的节点,打印节点信息

    打印完,可以对其进行删除修改等操作

     

    链表节点结构如下

     

    Tips:在逆向代码中应用数据结构参考《IDA Pro权威指南》第8章数据类型与数据结构

     2.       UAF漏洞代码

     

    链表节点被删除后,可以继续进入modify函数,modify函数之后可以继续进入modify函数。

    Delete函数如下:

     

    Delete函数中对节点的进行了free操作,如果在循环代码中,进行delete操作,释放节点之后,再选择2进入modify函数。

    Modify函数如下:

     

    Modify函数从用户读取数据,然后拷贝到对应的指针中,但此时使用的是一个已经释放的指针。当输入content时,会取content的长度作为大小分配内存,当分配内存大小等于msg结构大小(48字节,通过前面的结构获得)时,会将刚才释放的内存分配给content指针。

    如下所示

    Content指向了msg结构本身

     

    接着将content拷贝到content指针中,即我们的输入会拷贝到这个被释放的节点内存中。

    在循环代码中,modify完之后可以继续进入modify。 此时会再对msg结构的author,title,content指针指向的地址进行拷贝。 由于上一步已经能够对msg结构进行随意更改了,所以将几个memcpy的目的地址修改成想要的地址即可进行任意内存(属于该程序的合法内存)的修改了。

     

    至此我们已经能够完成任意内存地址的修改了。

    下面需要考虑的就是完成一些命令执行的利用。

    3.       漏洞利用执行命令

    要执行命令,需要调用system函数,但是代码中并没有system函数,需要如何完成命令执行呢?

    可以利用linux的延迟加载功能,改变strlen函数的指向,将原本要执行的strlen,改成执行system。

    Tips:延迟加载

    当调用标准函数时,需要从其他so文件中将标准函数加载进来,并不直接调用函数的地址,而是通过一张中间表跳转到函数的真正地址。

    以strlen函数的调用为例

     

     

    在程序调试中,打印0x804c04c的信息

     

    整个过程如下:

    Call strlen跳转到strlen函数,里面只有一句jmp ds:off_804c04c

    当程序运行起来时0x804c04c里的值为0xb7658210,才是strlen的真正地址

    即0x804c04c中存储libc库中的strlen的真正地址。

    如果将0x804c04c的值改掉,改成system的地址0xb7614360。 虽然看起来调用的是strlen,但真正执行的是system函数。

    修改前:

    Call strlen  

                        Strlen:

                                Jmp 0x804c04c

                                                             0x804c04c: 0xb7658210(strlen)

    修改后:

    Call strlen  

                        Strlen:

                                Jmp 0x804c04c

                                                             0x804c04c: 0xb7614360 (system)

     

    Tips: 寻址system的真正地址

    由于整个程序并没有调用system函数,所以在程序的重定位表中找不到system。 所以需要自己定位一下system在这个程序中的真正地址。

     

    Libc被装到0xb75d6000-0xb777a000 地址空间,大小为0x1a4000

    编写程序调用system函数

     

    调试运行查看其libc地址空间

     

    被装入到0xb7e10000-0xb7fb4000,大小也为0x1a4000。

    所以system在漏洞程序中的地址应为 =(system在调用程序中的地址-调用程序libc起始地址+漏洞程序libc起始地址)

     

    0xb7e4e360

    System在漏洞程序中地址= 0xb7e4e360-0xb7e10000+0xb75d6000= 0xb7614360

     4.       poc运行效果

    执行一个mkdir hack命令建立一个hack目录

    Poc片段

     

    5.       过程回顾

    1)  delete函数中释放节点

    2)  modify函数传入被释放的指针

    3)  modify函数中分配内存大小可控,通过分配与节点相同的大小,取得被释放内存的控制权

    4)  修改将要被拷贝的目的地址msg->author指针指向将要被执行的函数strlen的中间表地址

    5)  将strlen指向的真实strlen地址,修改为system的真实地址

    6)  看似执行call strlen,实则执行了system函数

    0x02 总结

    在指针释放后再申请相同大小的内存,系统会将释放的地址进行分配,以提高系统运行速度,因此可以修改到被释放的内存数据,如果被释放的指针继续被使用,则会造成UAF漏洞。

    通过UAF漏洞,可能可以造成一些任意内存的修改,结合代码特点,可能会造成任意内存的读取或者,严重的能够造成任意命令的执行,获得shell。 取决于被释放的指针是怎么使用的。

    相关知识点:gdb调试(如何调试fork出来的程序),延迟加载(plt与got),标准函数在内存中的定位,UAF修改被释放指针内容的原因dllmalloc

  • 相关阅读:
    Java对象转型
    .Net之路(十二)Cookie对象
    java学习笔记-包
    MongoDB的安装和简单使用
    考试系统维护中对项目管理的一点体会
    .Net之路(十一)StringBuilder和string
    考试系统调试优化总结
    我的2013——走过就有收获
    针对:Arraylist集合无法修改,下一次枚举无法操作的解决方案
    函数第二部分:为什么说动态参数是没有计划好的参数-Python基础前传(11)
  • 原文地址:https://www.cnblogs.com/alert123/p/4918041.html
Copyright © 2011-2022 走看看