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与程序运行,简要理解程序的功能。

 UAF原理与利用_第1张图片

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

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

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

 技术分享

链表节点结构如下

 UAF原理与利用_第2张图片

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

 2.       UAF漏洞代码

 UAF原理与利用_第3张图片

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

Delete函数如下:

 UAF原理与利用_第4张图片

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

Modify函数如下:

 UAF原理与利用_第5张图片

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

如下所示

 

UAF原理与利用_第6张图片

Content指向了msg结构本身

 UAF原理与利用_第7张图片

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

 

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

 UAF原理与利用_第8张图片

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

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

3.       漏洞利用执行命令

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

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

Tips:延迟加载

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

以strlen函数的调用为例

 技术分享技术分享

 UAF原理与利用_第9张图片

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

 UAF原理与利用_第10张图片

整个过程如下:

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)

 UAF原理与利用_第11张图片

Tips: 寻址system的真正地址

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

 UAF原理与利用_第12张图片

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

编写程序调用system函数

 UAF原理与利用_第13张图片

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

 UAF原理与利用_第14张图片

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

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

 UAF原理与利用_第15张图片

0xb7e4e360

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

 4.       poc运行效果

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

Poc片段

 UAF原理与利用_第16张图片

UAF原理与利用_第17张图片

技术分享

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

你可能感兴趣的:(WebSecurity)