首页
社区
课程
招聘
[原创]自己研究的一种检测Android SO的UAF和heap over-flow方法
发表于: 2019-7-20 22:29 10816

[原创]自己研究的一种检测Android SO的UAF和heap over-flow方法

2019-7-20 22:29
10816

 离职三个月在家闭门造车学习漏洞挖掘,开始的时候是使用各种开源的fuzz框架对github一顿挖,看各种fuzzer和asan实现的原理。直到挖到第一个UAF类型的漏洞,我终于感觉我可能已经入门了。于是便有了此文,分享一下我研究出来的android全局的UAF和heap over-flow检测的简单思路,供大家一同进步。

此时我们已经可以检测到UAF,并且捕获了异常

      https://github.com/mpv-player/mpv/issues/6808  这是我用honggfuzz发现mpv的一个UAF漏洞。调试完后我有了自己对UAF的理解。
      在我看来就是系统free的时候没有将数据清除导致之前存储的内容依然存在,使得该程序依然能够正常运行。但凡free后被其他申请的内存覆盖则程序才有可能报错。
     自己写了一个demo 来测试UAF
      
#include <stdio.h>
#include <memory.h>
#include <stdlib.h>
struct sample_1{
    char* name;
};
struct sample_2{
    char* name;
    struct sample_1* v0;
};
int main(int argc,char** argv){
    struct sample_1* smp1=(struct sample_1*)malloc(sizeof(struct sample_1));
    smp1->name="smp1";
    struct sample_2* smp2=(struct sample_2*)malloc(sizeof(struct sample_2));
    smp2->name="smp2";
    smp2->v0=smp1;
    free(smp2);
    printf("%s\n",smp2->name);
    printf("%s\n",smp2->v0->name);
    return 0;
}



我们看到在free(smp2)以后程序依然正常运行。

三、UAF检测思路

       看过asan的源码,他采取的是替换free和malloc函数,所以我这里也替换free和malloc,在malloc的时候多分配一个size_t大小用于存储malloc的buffer大小,并放置在buffer前。
     在free的时候获取存储的buffer大小进行memset,后在释放。
     为测试代码添加了signal处理函数(如果不添加处理函数会显示Segmentation fault: 11),当程序崩溃的时候打印堆栈。
#include <stdio.h>
#include <memory.h>
#include <stdlib.h>
#include <execinfo.h>
#include <signal.h>
#define STORESIZE sizeof(size_t)
struct sample_1{
    char* name;
};
struct sample_2{
    char* name;
    struct sample_1* v0;
};
void show_stack()
{
    int i;
    void *buffer[1024];
    int n = backtrace(buffer, 1024);
    char **symbols = backtrace_symbols(buffer, n);
    for (i = 0; i < n; i++) {
        printf("%s\n", symbols[i]);
    }
}
void signal_handler(int sig) {
    if(SIGSEGV==sig)
    {
        show_stack();
        exit(-1);
    }
    else{
        printf("signal with %d\n",sig);
    }
}
void my_free(void* addr){
    printf("free addr:%p size:%d append_size:%d\n",addr,*(size_t*)((size_t)addr-STORESIZE),STORESIZE);
    memset(addr,0xFF,*(size_t*)((size_t)addr-STORESIZE));
    free((void*)((size_t)addr-STORESIZE));
}
void* my_malloc(size_t len){
    void* addr=malloc(len+STORESIZE);
    printf("malloc addr:%p size:%d app_size:%d\n",(void*)((size_t)addr+STORESIZE),len,STORESIZE);
    *(size_t*)addr=len;
    return (void*)((size_t)addr+STORESIZE);
}
int main(int argc,char** argv){
    signal(SIGSEGV, signal_handler);
    struct sample_1* smp1=(struct sample_1*)my_malloc(sizeof(struct sample_1));
    smp1->name="smp1";
    struct sample_2* smp2=(struct sample_2*)my_malloc(sizeof(struct sample_2));
    smp2->name="smp2";
    smp2->v0=smp1;
    my_free(smp2);
    printf("%s\n",smp2->name);
    printf("%s\n",smp2->v0->name);
    return 0;
}
编译后运行结果如下:

此时我们已经可以检测到UAF,并且捕获了异常

四、heap over-flow 检测思路

 溢出的检测是在malloc的buffer前后分别添加一个buffer的size 类似[len][buffer][len],在free的时候检测头尾的len是否相等。如不相等则溢出。
void my_free(void* addr){
    printf("free addr:%p size:%d append_size:%d\n",addr,*(size_t*)((size_t)addr-STORESIZE),2*STORESIZE);
    memset(addr,0xFF,*(size_t*)((size_t)addr-STORESIZE));
    if(*(size_t*)((size_t)addr-STORESIZE)!=((size_t)addr+*(size_t*)((size_t)addr-STORESIZE)))
    {
        printf("heap over_flow!\n");
        show_stack();
        exit(-1);
    }
    free((void*)((size_t)addr-STORESIZE));
}
void* my_malloc(size_t len){
    void* addr=malloc(len+2*STORESIZE);
    printf("malloc addr:%p size:%d app_size:%d\n",(void*)((size_t)addr+STORESIZE),len,2*STORESIZE);
    *(size_t*)addr=len;
    *(size_t*)((size_t)addr+len+STORESIZE)=len;
    return (void*)((size_t)addr+STORESIZE);
}
int main(int argc,char** argv){
    char src[120]="";
    void* dst=my_malloc(100);
    memcpy(dst,src,120);
    my_free(dst);
    return 0;
}
运行结果如下:

五、实现android上的全局UAF和heap over-flow检测

       这里只说一下思路
       1、全局hook 具体方法可以在论坛找(比如注入app_process)
       2、hook  malloc 和 free函数将其替换
       3、在注入的so中添加signal处理函数,将崩溃或heap overflow的信息记录到文件。
       4、运行app (此时已经可以检测该app的so是否存在uaf或heap over-flow漏洞)

以上只是个人的想法与观点,如有不当之处,望各位大佬指出。共同学习进步。

#include <stdio.h>
#include <memory.h>
#include <stdlib.h>
struct sample_1{
    char* name;
};
struct sample_2{
    char* name;
    struct sample_1* v0;
};
int main(int argc,char** argv){
    struct sample_1* smp1=(struct sample_1*)malloc(sizeof(struct sample_1));
    smp1->name="smp1";
    struct sample_2* smp2=(struct sample_2*)malloc(sizeof(struct sample_2));
    smp2->name="smp2";
    smp2->v0=smp1;
    free(smp2);
    printf("%s\n",smp2->name);
    printf("%s\n",smp2->v0->name);
    return 0;
}




我们看到在free(smp2)以后程序依然正常运行。
       看过asan的源码,他采取的是替换free和malloc函数,所以我这里也替换free和malloc,在malloc的时候多分配一个size_t大小用于存储malloc的buffer大小,并放置在buffer前。
     在free的时候获取存储的buffer大小进行memset,后在释放。
     为测试代码添加了signal处理函数(如果不添加处理函数会显示Segmentation fault: 11),当程序崩溃的时候打印堆栈。
#include <stdio.h>
#include <memory.h>
#include <stdlib.h>
#include <execinfo.h>
#include <signal.h>
#define STORESIZE sizeof(size_t)
struct sample_1{
    char* name;
};
struct sample_2{
    char* name;
    struct sample_1* v0;
};
void show_stack()
{
    int i;
    void *buffer[1024];
    int n = backtrace(buffer, 1024);
    char **symbols = backtrace_symbols(buffer, n);
    for (i = 0; i < n; i++) {
        printf("%s\n", symbols[i]);
    }
}
void signal_handler(int sig) {
    if(SIGSEGV==sig)
    {
        show_stack();
        exit(-1);
    }
    else{
        printf("signal with %d\n",sig);
    }
}
void my_free(void* addr){
    printf("free addr:%p size:%d append_size:%d\n",addr,*(size_t*)((size_t)addr-STORESIZE),STORESIZE);
    memset(addr,0xFF,*(size_t*)((size_t)addr-STORESIZE));
    free((void*)((size_t)addr-STORESIZE));
}
void* my_malloc(size_t len){
    void* addr=malloc(len+STORESIZE);
    printf("malloc addr:%p size:%d app_size:%d\n",(void*)((size_t)addr+STORESIZE),len,STORESIZE);
    *(size_t*)addr=len;
    return (void*)((size_t)addr+STORESIZE);
}
int main(int argc,char** argv){
    signal(SIGSEGV, signal_handler);
    struct sample_1* smp1=(struct sample_1*)my_malloc(sizeof(struct sample_1));
    smp1->name="smp1";
    struct sample_2* smp2=(struct sample_2*)my_malloc(sizeof(struct sample_2));
    smp2->name="smp2";
    smp2->v0=smp1;
    my_free(smp2);
    printf("%s\n",smp2->name);
    printf("%s\n",smp2->v0->name);
    return 0;
}
编译后运行结果如下:

#include <stdio.h>
#include <memory.h>
#include <stdlib.h>
#include <execinfo.h>
#include <signal.h>
#define STORESIZE sizeof(size_t)
struct sample_1{
    char* name;
};
struct sample_2{
    char* name;
    struct sample_1* v0;
};
void show_stack()
{
    int i;
    void *buffer[1024];
    int n = backtrace(buffer, 1024);
    char **symbols = backtrace_symbols(buffer, n);
    for (i = 0; i < n; i++) {
        printf("%s\n", symbols[i]);
    }
}
void signal_handler(int sig) {
    if(SIGSEGV==sig)
    {
        show_stack();
        exit(-1);
    }
    else{
        printf("signal with %d\n",sig);
    }
}
void my_free(void* addr){
    printf("free addr:%p size:%d append_size:%d\n",addr,*(size_t*)((size_t)addr-STORESIZE),STORESIZE);
    memset(addr,0xFF,*(size_t*)((size_t)addr-STORESIZE));
    free((void*)((size_t)addr-STORESIZE));
}
void* my_malloc(size_t len){
    void* addr=malloc(len+STORESIZE);
    printf("malloc addr:%p size:%d app_size:%d\n",(void*)((size_t)addr+STORESIZE),len,STORESIZE);
    *(size_t*)addr=len;
    return (void*)((size_t)addr+STORESIZE);
}
int main(int argc,char** argv){
    signal(SIGSEGV, signal_handler);
    struct sample_1* smp1=(struct sample_1*)my_malloc(sizeof(struct sample_1));
    smp1->name="smp1";
    struct sample_2* smp2=(struct sample_2*)my_malloc(sizeof(struct sample_2));
    smp2->name="smp2";
    smp2->v0=smp1;
    my_free(smp2);
    printf("%s\n",smp2->name);
    printf("%s\n",smp2->v0->name);
    return 0;
}
编译后运行结果如下:

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

最后于 2019-7-20 22:39 被scxc编辑 ,原因:
收藏
免费 14
支持
分享
最新回复 (30)
雪    币: 6573
活跃值: (3888)
能力值: (RANK:200 )
在线值:
发帖
回帖
粉丝
2
此类文章很稀少,作者加油!
2019-7-20 22:34
1
雪    币: 4005
活跃值: (2188)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
3
大哥牛逼,大哥加油
2019-7-20 22:36
0
雪    币: 29
活跃值: (295)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
4
佩服
2019-7-20 23:00
0
雪    币: 341
活跃值: (138)
能力值: ( LV7,RANK:110 )
在线值:
发帖
回帖
粉丝
5
大奔哥v5
2019-7-20 23:53
0
雪    币: 2907
活跃值: (1301)
能力值: ( LV12,RANK:215 )
在线值:
发帖
回帖
粉丝
6
改aosp或kernel源码或许比全局注入更简单一些
2019-7-21 01:17
0
雪    币: 2375
活跃值: (433)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
楼主平时播放器用mpv么
2019-7-21 03:15
0
雪    币: 8715
活跃值: (8619)
能力值: ( LV13,RANK:570 )
在线值:
发帖
回帖
粉丝
8
收藏点赞评论三连
2019-7-21 09:18
0
雪    币: 2719
活跃值: (1595)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
9
羡慕离职 在家 研究技术的        
2019-7-21 12:22
0
雪    币: 584
活跃值: (11)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
看来,闭门造车不是贬义词,真的研究出来了,能力毅力都值得学习
2019-7-21 12:29
0
雪    币: 2907
活跃值: (1301)
能力值: ( LV12,RANK:215 )
在线值:
发帖
回帖
粉丝
11
Vn小帆 羡慕离职 在家 研究技术的
也不用羡慕 总有代价需要付的
2019-7-21 13:43
0
雪    币: 1636
活跃值: (653)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
12
我看懂了程序,但不懂原理。果然原理是一切的基础啊
2019-7-21 13:53
0
雪    币: 1636
活跃值: (653)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
13
我看懂了程序,但不懂原理。果然原理是一切的基础啊
2019-7-21 13:53
0
雪    币: 2719
活跃值: (1595)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
14
scxc 也不用羡慕 总有代价需要付的
我希望  以后跟你一样 优秀啊
2019-7-21 16:48
0
雪    币: 1392
活跃值: (5172)
能力值: ( LV13,RANK:240 )
在线值:
发帖
回帖
粉丝
15
scxc 也不用羡慕 总有代价需要付的
这是家里有矿啊~
2019-7-21 16:49
0
雪    币: 2907
活跃值: (1301)
能力值: ( LV12,RANK:215 )
在线值:
发帖
回帖
粉丝
16
IamHuskar 这是家里有矿啊~
家里没矿但有砖要搬
2019-7-21 16:51
1
雪    币: 562
活跃值: (4347)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
17
不错,很有人能分享漏洞挖掘的思路的文章,很赞!
2019-7-22 09:26
0
雪    币: 18
活跃值: (561)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
18
这些不都是ASAN已经实现的功能吗
2019-7-22 10:34
0
雪    币: 2907
活跃值: (1301)
能力值: ( LV12,RANK:215 )
在线值:
发帖
回帖
粉丝
19
ele7enxxh 这些不都是ASAN已经实现的功能吗
是的 asan的功能肯定比我这个更全面的 但也有局限性 类似ld_preload调用asan 和用asan编译aosp 一个不能做到全局hook 一个太消耗性能 我只是提出一种逆向修改的检测方法
最后于 2019-7-22 11:01 被scxc编辑 ,原因:
2019-7-22 10:57
0
雪    币: 2907
活跃值: (1301)
能力值: ( LV12,RANK:215 )
在线值:
发帖
回帖
粉丝
20
ele7enxxh 这些不都是ASAN已经实现的功能吗
结合全局hook可以对应用的so做到无源码检测,当然不仅限于so 只要能替换应该都能检测 
最后于 2019-7-22 11:09 被scxc编辑 ,原因:
2019-7-22 10:59
0
雪    币: 2907
活跃值: (1301)
能力值: ( LV12,RANK:215 )
在线值:
发帖
回帖
粉丝
21
ele7enxxh 这些不都是ASAN已经实现的功能吗
子诺大佬多指点几句吧。很崇拜你
2019-7-22 11:49
0
雪    币: 2141
活跃值: (7226)
能力值: ( LV11,RANK:180 )
在线值:
发帖
回帖
粉丝
22
dbg tql
2019-7-22 11:54
0
雪    币: 269
活跃值: (906)
能力值: ( LV12,RANK:345 )
在线值:
发帖
回帖
粉丝
23
这种帖子内容新颖,质量很赞
2019-7-22 14:00
0
雪    币: 18
活跃值: (561)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
24
你认识我?
2019-7-22 18:01
0
雪    币: 2907
活跃值: (1301)
能力值: ( LV12,RANK:215 )
在线值:
发帖
回帖
粉丝
25
ele7enxxh 你认识我?
以前群里交流过的。然后每月google安全公告你那一堆cve。印象挺深的
2019-7-22 18:40
1
游客
登录 | 注册 方可回帖
返回
//