From 307ba45bb4e1000677b6637479372905903dd4ae Mon Sep 17 00:00:00 2001 From: patrick-g2 Date: Sat, 16 Oct 2021 19:08:30 +0200 Subject: [PATCH] unhide-linux-bruteforce.c: move PID tables from stack to heap unhide-linux.c and unhide-posix.c: change default max_pid value (for 64 bits systems) unhide_rb.c: Increase the max number of PID so it doesn't crash in 64 bits systems. update version date update README.txt (build instruction) update NEWS file --- NEWS | 19 ++++++++ README.txt | 49 ++++++++++++--------- unhide-linux-bruteforce.c | 91 +++++++++++++++++++++++++++++++-------- unhide-linux.c | 5 ++- unhide-posix.c | 5 ++- unhide-tcp.c | 2 +- unhide_rb.c | 73 +++++++++++++++++++++++++------ 7 files changed, 189 insertions(+), 55 deletions(-) diff --git a/NEWS b/NEWS index ce310dd..90db331 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,22 @@ +Changes since 20210124 : +********************** + +BUG FIXES + - Correct two typo in english man pages + - Dirty hacks in unhide_rb to increase the max number of PID so it doesn't crash in 64 bits systems. + +ENHANCEMENTS + - In brute test, allocate PID tables on the heap instead of stack, as maxpid on 64 bits Linux may cause a stack overflow. + - unhide-linux and unhide-posix: set the default value of max_pid to 8388608. + +GUI + - N/A + +MISCELLANOUS + - Update README.txt (build instructions and some document layout) + - Clearly indicate in its display header of unhide_rb that it SHOULD NOT be used for serious work. + + Changes since 20130526 : ********************** diff --git a/README.txt b/README.txt index ec7f5b7..ef112ee 100644 --- a/README.txt +++ b/README.txt @@ -27,15 +27,20 @@ Detecting hidden processes. Implements six main techniques // --------- It's a back port in C language of the ruby unhide.rb -As the original unhide.rb, it is roughly equivalent to "unhide-linux quick reverse" : + +As the original unhide.rb, it is roughly equivalent to "unhide-linux quick reverse" but: - it makes three tests less (kill, opendir and chdir), -- it only run /bin/ps once at start and once for the double check, -- also, its tests are less accurate (e.g.. testing return value instead of errno), +- it only run /bin/ps once at start and once for the double check, this gives more false positives: + short live processes are seen as hidden. +- also, its tests are less accurate (e.g. testing return value instead of errno), +- it doesn't scale well when max_PID number increases, - processes are only identified by their exe link (unhide-linux also use cmdline and "sleeping kernel process" name), - there's little protection against failures (failed fopen or popen by example), - there's no logging capability. -It is very quick, about 80 times quicker than "unhide-linux quick reverse" + +On 32 bits system (with max_PID = 2^16) It is about 80 times quicker than "unhide-linux quick reverse" +On 64 bits system (with max_PID = 2^22) It is about 2 times quicker than "unhide-linux quick reverse" // Unhide-TCP // ---------- @@ -92,29 +97,33 @@ man/fr/unhide-tcp.8 -- French man page of unhide-tcp // Compiling // --------- -Build requires + Build requires : + -------------- glibc-devel glibc-static-devel -Require -- unhide-tcp under linux : - iproute2 - net-tools (for netstat) - lsof - psmisc (for fuser) -- unhide-tcp under freeBSD : - sockstat - lsof - netstat + Requires : + -------- + - unhide-tcp under linux : + iproute2 + net-tools (for netstat) + lsof + psmisc (for fuser) + - unhide-tcp under freeBSD : + sockstat + lsof + netstat -unhide-linux, unhide-posix, unhide_rb : - procps + - unhide-linux, unhide-posix, unhide_rb : + procps + +IMPORTANT : Notes that, as a forensic tool, unhide is built statically as the host system libraries may be compromised. If you ARE using a Linux kernel >= 2.6 - gcc -Wall -O2 --static -pthread unhide-linux*.c unhide-output.c -o unhide-linux - gcc -Wall -O2 --static unhide_rb.c -o unhide_rb - gcc -Wall -O2 --static unhide-tcp.c unhide-tcp-fast.c unhide-output.c -o unhide-tcp + gcc -Wall -Wextra -O2 --static -pthread unhide-linux*.c unhide-output.c -o unhide-linux + gcc -Wall -Wextra -O2 --static unhide-tcp.c unhide-tcp-fast.c unhide-output.c -o unhide-tcp + gcc -Wall -Wextra -O2 --static unhide_rb.c -o unhide_rb ln -s unhide unhide-linux Else (Linux < 2.6, *BSD, Solaris and other Unice) diff --git a/unhide-linux-bruteforce.c b/unhide-linux-bruteforce.c index 9f72b6f..3ee297e 100644 --- a/unhide-linux-bruteforce.c +++ b/unhide-linux-bruteforce.c @@ -66,32 +66,64 @@ void *functionThread (void *parametro) void brute(void) { int i=0; - int allpids[maxpid] ; - int allpids2[maxpid] ; + int* allpids; + int* allpids2; int x; int y; int z; msgln(unlog, 0, "[*]Starting scanning using brute force against PIDS with fork()\n") ; - // PID under 301 are reserved for kernel - for(x=0; x < 301; x++) + if ( NULL == (allpids = (int *)malloc(sizeof(int) * maxpid))) { - allpids[x] = 0 ; - allpids2[x] = 0 ; + die(unlog, "Error: Cannot allocate pid arrays ! Exiting."); } - for(z=301; z < maxpid; z++) + if(FALSE == brutesimplecheck) // allocate second table { - allpids[z] = z ; - allpids2[z] = z ; + if ( NULL == (allpids2 = (int *)malloc(sizeof(int) * maxpid))) + { + die(unlog, "Error: Cannot allocate pid arrays ! Exiting."); + } } + + + if(FALSE == brutesimplecheck) // Init the two tables + { + // PID under 301 are reserved for kernel + for(x=0; x < 301; x++) + { + allpids[x] = 0 ; + allpids2[x] = 0 ; + } + + for(z=301; z < maxpid; z++) + { + allpids[z] = z ; + allpids2[z] = z ; + } + } + else // Init only the first table + { + for(x=0; x < 301; x++) + { + allpids[x] = 0 ; + } + + for(z=301; z < maxpid; z++) + { + allpids[z] = z ; + } + } + + // printf("Maxpid : %06d\n", maxpid); for (i=301; i < maxpid; i++) { int vpid; int status; + // printf("Tested pid : %06d\r", i); errno= 0 ; if ((vpid = vfork()) == 0) @@ -143,21 +175,36 @@ void brute(void) msgln(unlog, 0, "[*]Starting scanning using brute force against PIDS with pthread functions\n") ; - // PID under 301 are reserved for kernel - for(x=0; x < 301; x++) + if(FALSE == brutesimplecheck) // Init the two tables { - allpids[x] = 0 ; - allpids2[x] = 0 ; + // PID under 301 are reserved for kernel + for(x=0; x < 301; x++) + { + allpids[x] = 0 ; + allpids2[x] = 0 ; + } + + for(z=301; z < maxpid; z++) + { + allpids[z] = z ; + allpids2[z] = z ; + } } - - - for(z=301; z < maxpid; z++) + else // Init only the first table { - allpids[z] = z ; - allpids2[z] = z ; + for(x=0; x < 301; x++) + { + allpids[x] = 0 ; + } + + for(z=301; z < maxpid; z++) + { + allpids[z] = z ; + } } + for (i=301; i < maxpid ; i++) { void *status; @@ -216,4 +263,12 @@ void brute(void) } } } + + if ( NULL != allpids) + free((void *)allpids) ; + + if ( NULL != allpids2) + free((void *)allpids2) ; + + } diff --git a/unhide-linux.c b/unhide-linux.c index 1962937..3d78805 100644 --- a/unhide-linux.c +++ b/unhide-linux.c @@ -51,14 +51,15 @@ along with this program. If not, see . // header const char header[] = - "Unhide 20210124\n" + "Unhide 20211016\n" "Copyright © 2010-2021 Yago Jesus & Patrick Gouin\n" "License GPLv3+ : GNU GPL version 3 or later\n" "http://www.unhide-forensics.info\n\n" "NOTE : This version of unhide is for systems using Linux >= 2.6 \n\n"; // defauly sysctl kernel.pid_max -int maxpid = 32768; +# define MAX_PID 8388608 +int maxpid = MAX_PID; // Threads id for sync int tid ; diff --git a/unhide-posix.c b/unhide-posix.c index 7b5d572..6f1d191 100644 --- a/unhide-posix.c +++ b/unhide-posix.c @@ -65,7 +65,8 @@ along with this program. If not, see . -int maxpid= 999999; +# define MAX_PID 8388608 +int maxpid = MAX_PID; // Temporary string for output char scratch[1000]; @@ -219,7 +220,7 @@ void checkgetsid() { int main (int argc, char *argv[]) { - strncpy(scratch,"Unhide-posix 20210124\n", sizeof(scratch)-1) ; + strncpy(scratch,"Unhide-posix 20211016\n", sizeof(scratch)-1) ; strncat(scratch, "Copyright © 2013-2021 Yago Jesus & Patrick Gouin\n", sizeof(scratch)-strlen(scratch)-1); strncat(scratch, "License GPLv3+ : GNU GPL version 3 or later\n", sizeof(scratch)-strlen(scratch)-1); strncat(scratch, "http://www.unhide-forensics.info\n\n", sizeof(scratch)-strlen(scratch)-1); diff --git a/unhide-tcp.c b/unhide-tcp.c index 9e873ae..7f193ac 100644 --- a/unhide-tcp.c +++ b/unhide-tcp.c @@ -38,7 +38,7 @@ along with this program. If not, see . // header const char header[] = - "Unhide-tcp 20210124\n" + "Unhide-tcp 20211016\n" "Copyright © 2013-2021 Yago Jesus & Patrick Gouin\n" "License GPLv3+ : GNU GPL version 3 or later\n" "http://www.unhide-forensics.info\n"; diff --git a/unhide_rb.c b/unhide_rb.c index af0fa1d..369948c 100644 --- a/unhide_rb.c +++ b/unhide_rb.c @@ -52,17 +52,21 @@ along with this program. If not, see . #define UNKNOWN -1 // sysctl kernel.pid_max -int maxpid = 32768; +# define MAX_PID 8388608 +int maxpid = MAX_PID; // Temporary string for output char scratch[1500] ; char cmdcont[1000] ; -unsigned int proc_parent_pids[65536] ; +unsigned int proc_parent_pids[MAX_PID] ; -char *proc_tasks[65536]; -char *ps_pids[65536]; -char *messages_pids[65536]; +// char *proc_tasks[65536]; +// char *ps_pids[65536]; +// char *messages_pids[65536]; +char *proc_tasks[MAX_PID]; +char *ps_pids[MAX_PID]; +char *messages_pids[MAX_PID]; char message[1000] ; char description[1100] ; int ps_count = 0 ; @@ -91,6 +95,7 @@ enum n_detector void setup(int phase) { + // printf("in : %s\n", __func__) ; // setup part of unhide.rb // ----------------------- @@ -214,9 +219,11 @@ void setup(int phase) myline[999] = 0; if (myline[0] != '\n') { + //printf("myline = --%s--\n", myline); mypid = strtol(myline, NULL,10) ; // printf("line+5 = --%s--\n", myline+5); // DEBUG - if (NULL == strstr(myline+5,UNHIDE_RB)) + // if (NULL == strstr(myline+5,UNHIDE_RB)) + if (NULL == strstr(myline+7,UNHIDE_RB)) { ps_count++ ; if (2 == phase) @@ -241,9 +248,11 @@ void setup(int phase) my_end-- ; } // printf("line = --%s--\n", myline); // DEBUG - ps_pids[mypid] = malloc(strlen(myline+5)+1) ; + //ps_pids[mypid] = malloc(strlen(myline+5)+1) ; + ps_pids[mypid] = malloc(strlen(myline+7)+1) ; // printf("ps_pids[%d] = %p for %d bytes\n", mypid,ps_pids[mypid],strlen(myline+5)+1); - strcpy(ps_pids[mypid],myline+5) ; + // strcpy(ps_pids[mypid],myline+5) ; + strcpy(ps_pids[mypid],myline+7) ; // printf("pid = %d\t", mypid); // DEBUG // printf("cmd = --%s--\n",ps_pids[mypid]); } @@ -264,6 +273,8 @@ void setup(int phase) int get_suspicious_pids(int pid_num) { + //printf("in : %s\n", __func__) ; + int pid_min, pid_max, my_pid ; int pid_exists[N_SCHED_RR_GET_INTERVAL+1]; char mypath[50] ; @@ -303,19 +314,23 @@ int get_suspicious_pids(int pid_num) pid_min = 1 ; pid_max = maxpid ; + printf("pid_max : %0d\n", pid_max) ; + } else pid_min = pid_max = pid_num ; for (my_pid = pid_min ; my_pid <= pid_max; my_pid++) { -// printf("pid_min = %d, pid_max = %d, my_pid = %d\n", pid_min, pid_max, my_pid) ; + // printf("pid_min = %d, pid_max = %d, my_pid = %d\n", pid_min, pid_max, my_pid) ; // ps + // printf("in : %s ps\n", __func__) ; if (ps_pids[my_pid] != NULL) pid_exists[N_PS] = TRUE ; else pid_exists[N_PS] = FALSE ; // proc + // printf("in : %s proc\n", __func__) ; sprintf(mypath,"/proc/%d",my_pid); statuscmd = stat(mypath, &buffer) ; if ((statuscmd == 0) && S_ISDIR(buffer.st_mode)) @@ -339,17 +354,20 @@ int get_suspicious_pids(int pid_num) pid_exists[N_PROC] = FALSE ; } // proc/#/task + // printf("in : %s proc/#/task\n", __func__) ; if (proc_tasks[my_pid] != NULL) pid_exists[N_PROC_TASK] = TRUE ; else pid_exists[N_PROC_TASK] = FALSE ; // proc_parent + // printf("in : %s proc_parent\n", __func__) ; if (proc_parent_pids[my_pid] != 0) pid_exists[N_PROC_PARENT] = TRUE ; else pid_exists[N_PROC_PARENT] = UNKNOWN ; // getsid() + // printf("in : %s getsid\n", __func__) ; if (-1 != getsid(my_pid)) { pid_exists[N_GETSID] = TRUE ; @@ -359,6 +377,7 @@ int get_suspicious_pids(int pid_num) pid_exists[N_GETSID] = FALSE ; } // getpgid() + // printf("in : %s getpgid\n", __func__) ; if (-1 != getpgid(my_pid)) { pid_exists[N_GET_PGID] = TRUE ; @@ -368,6 +387,7 @@ int get_suspicious_pids(int pid_num) pid_exists[N_GET_PGID] = FALSE ; } // getpriority() + // printf("in : %s getpriority\n", __func__) ; if (-1 != getpriority(PRIO_PROCESS, my_pid)) { pid_exists[N_GETPRIORITY] = TRUE ; @@ -377,6 +397,7 @@ int get_suspicious_pids(int pid_num) pid_exists[N_GETPRIORITY] = FALSE ; } // sched_getparam() + // printf("in : %s sched_getparam\n", __func__) ; if (-1 != sched_getparam(my_pid, ¶m)) { pid_exists[N_SCHED_GETPARAM] = TRUE ; @@ -386,6 +407,7 @@ int get_suspicious_pids(int pid_num) pid_exists[N_SCHED_GETPARAM] = FALSE ; } // sched_getaffinity() + // printf("in : %s sched_getaffinity\n", __func__) ; if (-1 != sched_getaffinity(my_pid, sizeof(cpu_set_t), &mask)) { pid_exists[N_SCHED_GETAFFINITY] = TRUE ; @@ -395,6 +417,7 @@ int get_suspicious_pids(int pid_num) pid_exists[N_SCHED_GETAFFINITY] = FALSE ; } // sched_getscheduler() + // printf("in : %s sched_getscheduler\n", __func__) ; if (-1 != sched_getscheduler(my_pid)) { pid_exists[N_SCHED_GETSCHEDULER] = TRUE ; @@ -404,6 +427,7 @@ int get_suspicious_pids(int pid_num) pid_exists[N_SCHED_GETSCHEDULER] = FALSE ; } // sched_rr_get_interval() + // printf("in : %s sched_rr_get_interval\n", __func__) ; if (-1 != sched_rr_get_interval(my_pid, &tp)) { pid_exists[N_SCHED_RR_GET_INTERVAL] = TRUE ; @@ -475,7 +499,7 @@ int get_suspicious_pids(int pid_num) strcpy(description, proc_exe) ; } } - sprintf(scratch, "\n %s %s%s", (pid_exists[index] ? "Seen by" : "Not seen by"), pid_detectors[index], description) ; + sprintf(scratch, "\n %s %s %s", (pid_exists[index] ? "Seen by" : "Not seen by"), pid_detectors[index], description) ; strcat(message, scratch) ; } // puts(message) ; //DEBUG @@ -502,13 +526,35 @@ int main (int argc, char *argv[]) int found_something = FALSE ; int phase1_ko = FALSE ; - strncpy(scratch,"Unhide_rb 20210124\n", sizeof(scratch)-1) ; + strncpy(scratch,"Unhide_rb 20211016\n", sizeof(scratch)-1) ; strncat(scratch, "Copyright © 2013-2021 Yago Jesus & Patrick Gouin\n", sizeof(scratch)-strlen(scratch)-1); strncat(scratch, "License GPLv3+ : GNU GPL version 3 or later\n", sizeof(scratch)-strlen(scratch)-1); strncat(scratch, "http://www.unhide-forensics.info\n\n", sizeof(scratch)-strlen(scratch)-1); strncat(scratch, "NOTE : This version of unhide_rb is for systems using Linux >= 2.6 \n\n", sizeof(scratch)-strlen(scratch)-1); - scratch[999] = 0 ; + strncat(scratch, "WARNING : \n", sizeof(scratch)-strlen(scratch)-1); + strncat(scratch, "TL;DR : This tool is a P.O.F. (proof of fake).\n", sizeof(scratch)-strlen(scratch)-1); + strncat(scratch, " DON'T USE IT for serious work.\n\n", sizeof(scratch)-strlen(scratch)-1); + strncat(scratch, " Back in time, the Dev of unhide.rb pretends that his tool, written in ruby\n", sizeof(scratch)-strlen(scratch)-1); + strncat(scratch, " do the same checks that unhide-linux, which is written in C, but is 10 times faster.\n", sizeof(scratch)-strlen(scratch)-1); + strncat(scratch, " This was evidently false:\n", sizeof(scratch)-strlen(scratch)-1); + strncat(scratch, " - unhide.rb makes less tests,\n", sizeof(scratch)-strlen(scratch)-1); + strncat(scratch, " - unhide.rb tests are less accurate,\n", sizeof(scratch)-strlen(scratch)-1); + strncat(scratch, " - unhide.rb displays only displays minimal information about hidden processes,\n", sizeof(scratch)-strlen(scratch)-1); + strncat(scratch, " - unhide.rb find lot of false positives when processes number is high,\n", sizeof(scratch)-strlen(scratch)-1); + strncat(scratch, " - unhide.rb find lot of false positives when there are short live processes,\n", sizeof(scratch)-strlen(scratch)-1); + strncat(scratch, " - unhide.rb don't log results of tests,\n", sizeof(scratch)-strlen(scratch)-1); + strncat(scratch, " - and so on.\n", sizeof(scratch)-strlen(scratch)-1); + strncat(scratch, " In order to verify assertion about speed, I backported unhide.rb to C language,\n", sizeof(scratch)-strlen(scratch)-1); + strncat(scratch, " in the more straight/dummy way:\n", sizeof(scratch)-strlen(scratch)-1); + strncat(scratch, " No optimisation, translation from line to line, exactly the same tests and treatments.\n", sizeof(scratch)-strlen(scratch)-1); + strncat(scratch, " The result is native unhide_rb.\n", sizeof(scratch)-strlen(scratch)-1); + strncat(scratch, " It is ONE THOUSAND times faster that the original ruby unhide.rb\n\n", sizeof(scratch)-strlen(scratch)-1); + strncat(scratch, " So, don't rely neither on unhide.rb nor on unhide_rb, they are just toys ! \n", sizeof(scratch)-strlen(scratch)-1); + strncat(scratch, " For a quick but quite accurate test, use the command 'unhide-linux quick reverse'\n\n", sizeof(scratch)-strlen(scratch)-1); + + + scratch[1499] = 0 ; fputs(scratch, stdout); fflush(stdout) ; @@ -516,6 +562,7 @@ int main (int argc, char *argv[]) if(getuid() != 0){ printf("You must be root to run %s !\n", argv[0]) ; fflush(stdout) ; + exit(1); } @@ -547,6 +594,7 @@ int main (int argc, char *argv[]) fflush(stdout) ; } + puts ("Phase 1...") ; phase1_ko = get_suspicious_pids(-1) ; // re-initializing memory pointers (were used as boolean in setup() phase 1) for (i = 0 ; i < maxpid; i++) @@ -556,6 +604,7 @@ int main (int argc, char *argv[]) } if (TRUE == phase1_ko) { + puts ("Phase 2...") ; setup(2); for (i=1; i