[Forensics-changes] [SCM] debian-forensics/unhide branch, debian, updated. debian/20100201-1-5-gae84138
Christophe Monniez
christophe.monniez at fccu.be
Thu Dec 23 09:14:10 UTC 2010
The following commit has been merged in the debian branch:
commit ae369576fd89bc75a5da4991def321642951ffd2
Author: Christophe Monniez <christophe.monniez at fccu.be>
Date: Thu Dec 23 09:46:46 2010 +0100
Merging upstream version 20100819.
diff --git a/README.txt b/README.txt
index 6830c67..9acb4d9 100644
--- a/README.txt
+++ b/README.txt
@@ -1,17 +1,25 @@
**-Unhide-** yjesus at security-projects.com
-Unhide is a forensic tool to find hidden processes and TCP/UDP ports by rootkits / LKMs
+Unhide is a forensic tool to find hidden processes and TCP/UDP ports by rootkits / LKMs
or by another hidden technique.
//Unhide (ps)
-Detecting hidden processes. Implements three techniques
+Detecting hidden processes. Implements six techniques
-* Compare /proc vs /bin/ps output
+1- Compare /proc vs /bin/ps output
-* Compare info gathered from /bin/ps with info gathered from syscalls (syscall scanning).
+2- Compare info gathered from /bin/ps with info gathered by walking thru the procfs. ONLY for Linux 2.6 version
-* Full PIDs space ocupation (PIDs bruteforcing)
+3- Compare info gathered from /bin/ps with info gathered from syscalls (syscall scanning).
+
+4- Full PIDs space ocupation (PIDs bruteforcing)
+
+5- Compare /bin/ps output vs /proc, procfs walking and syscall. ONLY for Linux 2.6 version
+ Reverse search, verify that all thread seen by ps are also seen by the kernel.
+
+6- Quick compare /proc, procfs walking and syscall vs /bin/ps output. ONLY for Linux 2.6 version
+ It's about 20 times faster than tests 1+2+3 but maybe give more false positives.
// Unhide-TCP
@@ -21,7 +29,7 @@ of all TCP/UDP ports availables.
// Files
-unhide.c --> Hidden processes, for generic Unix systems (*BSD, Solaris, linux 2.2 / 2.4)
+unhide.c --> Hidden processes, for generic Unix systems (*BSD, Solaris, linux 2.2 / 2.4)
It doesn't implement PIDs brute forcing check yet. Needs more testing
unhide-linux26.c --> Hidden processes, Linux 2.6.x
diff --git a/changelog b/changelog
new file mode 100644
index 0000000..b8f3211
--- /dev/null
+++ b/changelog
@@ -0,0 +1,53 @@
+2010-08-19
+- Add GPL v3 Disclaimer in unhide-linux26.c
+- Add new test 'procfs' (via readdir & chdir)
+- Add new test 'reverse'
+- Add new test 'quick'
+- Add option verbose (-v) to allow warning display
+- Add option morecheck (-m), only affect procfs test for now
+- Add option help (-h)
+- Displace usage in usage() function
+- Add Changelog file (this file)
+- Rewamp command line parsing in main()
+- Change checkps() parameter to allow more scalability
+- Minor optimization in brute(), we tried to create 300 more processes than available.
+- Minor optimization : avoid to test our own PID
+- Update the man page and README.txt to reflect changes.
+
+2010-02-01
+- Threads Brute Force added
+- Add needed stuff (includes, defines, ...) to eliminate compilation warning. (Thanks to J. Walles)
+- Correct a typo in checkps() where fich_tmp is used in place of fich_pgid (Thanks to P. Gouin)
+- Corrected several FD leaks where files or pipes are read and closed even if they have failed to open. (Thanks to W. Doekes & P. Gouin)
+- Add warning messages if file or pipe fails to open (compatible with rkhunter use of unhide) (Thanks to W. Doekes & P. Gouin)
+- Add warning messages if a test is skipped (compatible with rkhunter use of unhide). (Thanks to P. Gouin)
+- Correct removing of leading spaces which tests one char too far for end of string in checkps(). (Thanks to P. Gouin)
+- Close fd in get_max_pid(). (Thanks to P. Gouin)
+- Close cmd_file in printbadpid(). (Thanks to P. Gouin)
+- Add display of test name in checkallnoprocps(). (Thanks to P. Gouin)
+- Close fich_processo in checksysinfo() (Thanks to W. Doekes)
+- Avoid potential buffer overflow in checksysinfo() (Thanks to W. Doekes)
+- Correct allpids[] initialization in brute() (Thanks to W. Doekes)
+- Modify brute as modifying allpid from within the forked process may have undefined results (Linux vfork() man page) (Thanks to P. Gouin)
+- Add return to main() (Thanks to W. Doekes)
+- Optimizations (Thanks to P. Gouin)
+
+2009-08-10 (BETA)
+-Improved maxpid routine (Thanks to Jan Iven)
+-Improved false positives detection (Thanks to Jan Iven)
+-Kill() syscall added (Thanks to Jan Iven)
+-Fixed sched_getaffinity() bug (Thanks to Jan Iven)
+-Some minor bug fixes
+
+2008-05-19
+-Fixed a race condition bug that showed false positives (Thanks to Johan Walles)
+-Added manpages (Thanks to Francois Marier)
+
+02-11-2007
+-Minor bugfixes
+-License added
+-sysinfo() syscall added
+
+28-12-2005
+-Initial Release
+
diff --git a/man/unhide.8 b/man/unhide.8
index 6918d9e..19f8c48 100644
--- a/man/unhide.8
+++ b/man/unhide.8
@@ -1,37 +1,72 @@
-.TH "UNHIDE" "8"
-.SH "NAME"
-unhide \(em forensic tool to find hidden processes
-.SH "SYNOPSIS"
-.PP
-\fBunhide\fR \fIproc\fR | \fIsys\fR | \fIbrute\fR
-.SH "DESCRIPTION"
-.PP
+.TH "UNHIDE" "8" "June 2010" "Administration commands"
+.SH "NAME"
+unhide \(em forensic tool to find hidden processes
+.SH "SYNOPSIS"
+.PP
+\fBunhide\fR [\fIOPTIONS\fR] \fITEST\fR
+.SH "DESCRIPTION"
+.PP
\fBunhide\fR is a forensic tool to find processes hidden by
rootkits, Linux kernel modules or by other techniques. It
detects hidden processes using three techniques:
-.PP
-The \fIproc\fR technique consists of comparing /proc with the
-output of /bin/ps.
-.PP
-The \fIsys\fR technique consists of comparing information
-gathered from /bin/ps with information gathered from system
-calls.
-.PP
-The \fIbrute\fR technique consists of bruteforcing the all
-process IDs.
-.SH "SEE ALSO"
-.PP
-unhide-tcp (8).
-.SH "AUTHOR"
-.PP
-This manual page was written by Francois Marier francois at debian.org for
-the \fBDebian\fP system (but may be used by others). Permission is
-granted to copy, distribute and/or modify this document under
-the terms of the GNU General Public License, Version 3 any
-later version published by the Free Software Foundation.
-
-.PP
-On Debian systems, the complete text of the GNU General Public
-License can be found in /usr/share/common-licenses/GPL.
-
-.\" created by instant / docbook-to-man, Thu 06 Dec 2007, 17:59
+.PP
+.SH "OPTIONS"
+.TP
+\fB\-h\fR
+display help
+.TP
+\fB\-v\fR
+be verbose, display warning message (default : don't display)
+.TP
+\fB\-m\fR
+do more checks. As of 2010-06-01 version, this option has only
+effect for the dirfunc test. Implies -v
+.PP
+.PP
+.SH "TESTS"
+.PP
+The \fIproc\fR technique consists of comparing /proc with the
+output of /bin/ps.
+.PP
+The \fIprocfs\fR technique consists of comparing information
+gathered from /bin/ps with information gathered by walking in the procfs.
+.PP
+The \fIsys\fR technique consists of comparing information
+gathered from /bin/ps with information gathered from system
+calls.
+.PP
+The \fIbrute\fR technique consists of bruteforcing the all
+process IDs. This technique is only available on Linux 2.6 kernels.
+.PP
+The \fIreverse\fR technique consists of verifying that all threads
+seen by ps are also seen in procfs and by system calls. It is intended to
+verify that a rootkit has not killed a security tool (IDS or other) and
+make the admin believe it's still running.
+This technique is only available on Linux 2.6 kernels.
+.PP
+The \fIquick\fR technique combines the proc, procfs and sys techniques in a
+quick way. It's about 20 times faster but may give false positives.
+This technique is only available on Linux 2.6 kernels.
+.SS "Exit status:"
+.TP
+0
+if OK,
+.TP
+1
+if problems.
+.PP
+Report ls bugs to yjesus at security-projects.com
+.SH "SEE ALSO"
+.PP
+unhide-tcp (8).
+.SH "AUTHOR"
+.PP
+This manual page was written by Francois Marier francois at debian.org and Patrick Gouin.
+Permission is granted to copy, distribute and/or modify this document under
+the terms of the GNU General Public License, Version 3 or any
+later version published by the Free Software Foundation.
+.SH LICENSE
+License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.
+.br
+This is free software: you are free to change and redistribute it.
+There is NO WARRANTY, to the extent permitted by law.
diff --git a/unhide-linux26.c b/unhide-linux26.c
index e280714..73bc30e 100644
--- a/unhide-linux26.c
+++ b/unhide-linux26.c
@@ -1,5 +1,20 @@
/* Unhide yjesus at security-projects.com */
+/*
+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/>.
+*/
+
// Needed for unistd.h to declare getpgid() and others
#define _XOPEN_SOURCE 500
@@ -22,11 +37,12 @@
#include <fcntl.h>
#include <pthread.h>
#include <sys/syscall.h>
+#include <ctype.h>
// we are looking only for real process not thread and only one by one
#define COMMAND "ps --no-header -p %i o pid"
-// we ara looking for session ID one by one
+// we are looking for session ID one by one
#define SESSION "ps --no-header -s %i o sess"
// We are looking for group ID one by one
// but ps can't select by pgid
@@ -34,16 +50,43 @@
// We are looking for all processes even threads
#define THREADS "ps --no-header -eL o lwp"
// for sysinfo scanning, fall back to old command, as --no-header seems to create
-// an extra process
+// an extra process/thread
#define SYS_COMMAND "ps -eL o lwp"
+// an extra process/thread
+#define REVERSE "ps --no-header -eL o lwp,cmd"
+
+// Define mask for the checks to do in checkps
+#define PS_PROC 0x00000001
+#define PS_THREAD 0x00000002
+#define PS_MORE 0x00000004
+
+// Define test number
+#define TST_NONE 0
+#define TST_PROC 1
+#define TST_DIR 2
+#define TST_SYS 3
+#define TST_BRUTE 4
+#define TST_REVERSE 5
+#define TST_QUICK 6
+
+// boolean value
+#define FALSE 0
+#define TRUE 1
// sysctl kernel.pid_max
-int maxpid= 32768;
+int maxpid = 32768;
// For Threads sync
int tid ;
+// our own PID
+pid_t mypid ;
+
+// options
+int verbose = FALSE;
+int morecheck = FALSE;
+
void *funcionThread (void *parametro) {
tid = (pid_t) syscall (SYS_gettid);
@@ -71,7 +114,7 @@ void get_max_pid(int* newmaxpid) {
}
-int checkps(int tmppid, int morechecks) {
+int checkps(int tmppid, int checks) {
int ok = 0;
char pids[30];
@@ -82,64 +125,77 @@ int checkps(int tmppid, int morechecks) {
FILE *fich_tmp ;
+// printf("in --> checkps\n"); // DEBUG
+
// The compare string is the same for all test
sprintf(compare,"%i\n",tmppid);
- sprintf(command,COMMAND,tmppid) ;
+ if (PS_PROC == (checks & PS_PROC)) {
+ sprintf(command,COMMAND,tmppid) ;
- fich_tmp=popen (command, "r") ;
- if (fich_tmp == NULL) {
- printf("Warning : popen failed while ps checking pid %d (memory, or something set errno: %s)\n", tmppid, strerror(errno));
- return(0);
- }
+ fich_tmp=popen (command, "r") ;
+ if (fich_tmp == NULL) {
+ if(TRUE == verbose) {
+ printf("Warning : popen failed while ps checking pid %d (memory, or something set errno: %s)\n", tmppid, strerror(errno));
+ }
+ return(0);
+ }
+
+// while (!feof(fich_tmp) && ok == 0) {
+ {
+ char* tmp_pids = pids;
+
+ fgets(pids, 30, fich_tmp);
+ pids[29] = 0;
- while (!feof(fich_tmp) && ok == 0) {
- char* tmp_pids = pids;
+// printf("pids = %s\n", pids); // DEBUG
+ while( *tmp_pids == ' ' && tmp_pids <= pids+29) {
+ tmp_pids++;
+ }
- fgets(pids, 30, fich_tmp);
- pids[29] = 0;
+ if (strncmp(tmp_pids, compare, 30) == 0) {ok = 1;}
- while( *tmp_pids == ' ' && tmp_pids <= pids+29) {
- tmp_pids++;
}
- if (strncmp(tmp_pids, compare, 30) == 0) {ok = 1;}
+ if (fich_tmp != NULL)
+ pclose(fich_tmp);
+ if (1 == ok) return(ok) ; // pid is found, no need to go further
}
- if (fich_tmp != NULL)
- pclose(fich_tmp);
-
- if (1 == ok) return(ok) ; // pid is found, no need to go further
+ if (PS_THREAD == (checks & PS_THREAD)) {
+ FILE *fich_thread ;
- FILE *fich_thread ;
+ fich_thread=popen (THREADS, "r") ;
+ if (fich_thread == NULL) {
+ if(TRUE == verbose) {
+ printf("Warning : popen failed while thread checking pid %d (memory, or something set errno: %s)\n", tmppid, strerror(errno));
+ }
+ return(0);
+ }
- fich_thread=popen (THREADS, "r") ;
- if (fich_thread == NULL) {
- printf("Warning : popen failed while thread checking pid %d (memory, or something set errno: %s)\n", tmppid, strerror(errno));
- return(0);
- }
+ while (!feof(fich_thread) && ok == 0) {
+ char* tmp_pids = pids;
- while (!feof(fich_thread) && ok == 0) {
- char* tmp_pids = pids;
+ fgets(pids, 30, fich_thread);
+ pids[29] = 0;
- fgets(pids, 30, fich_thread);
- pids[29] = 0;
+// printf(" threads = %s\n", pids); // DEBUG
+ while( *tmp_pids == ' ' && tmp_pids <= pids+29) {
+ tmp_pids++;
+ }
- while( *tmp_pids == ' ' && tmp_pids <= pids+29) {
- tmp_pids++;
- }
+ if (strncmp(tmp_pids, compare, 30) == 0) {ok = 1;}
- if (strncmp(tmp_pids, compare, 30) == 0) {ok = 1;}
+ }
+ if (fich_thread != NULL)
+ pclose(fich_thread);
+ if (1 == ok) return(ok) ; // thread is found, no need to go further
}
- if (fich_thread != NULL)
- pclose(fich_thread);
- if (1 == ok) return(ok) ; // thread is found, no need to go further
-
- if (morechecks == 1) {
+ if (PS_MORE == (checks & PS_MORE)) {
FILE *fich_session ;
@@ -174,7 +230,9 @@ int checkps(int tmppid, int morechecks) {
fich_pgid=popen (PGID, "r") ;
if (fich_pgid == NULL) {
- printf("Warning : popen failed while pgid checking pid %d (memory, or something set errno: %s)\n", tmppid, strerror(errno));
+ if(TRUE == verbose) {
+ printf("Warning : popen failed while pgid checking pid %d (memory, or something set errno: %s)\n", tmppid, strerror(errno));
+ }
return(0);
}
@@ -203,46 +261,68 @@ void printbadpid (int tmppid) {
int statuscmd ;
char cmd[100] ;
struct stat buffer;
+ FILE *cmdfile ;
+ char cmdcont[1000];
+ int cmdok = 0;
printf ("Found HIDDEN PID: %i\n", tmppid) ;
sprintf(cmd,"/proc/%i/cmdline",tmppid);
statuscmd = stat(cmd, &buffer);
+// statuscmd = 0 ; // DEBUG
if (statuscmd == 0) {
-
- FILE *cmdfile ;
- char cmdcont[1000];
-
cmdfile=fopen (cmd, "r") ;
if (cmdfile != NULL) {
while (!feof (cmdfile)) {
- fgets (cmdcont, 1000, cmdfile);
- printf ("Command: %s\n\n", cmdcont);
+ if (NULL != fgets (cmdcont, 1000, cmdfile)) {
+ cmdok = 1;
+ printf ("Command: %s\n\n", cmdcont);
+ }
}
fclose(cmdfile);
}
}
+ if (0 == cmdok) { // fallback : try to readlink the exe
+ ssize_t length ;
+
+ sprintf(cmd,"/proc/%i/exe",tmppid);
+ statuscmd = stat(cmd, &buffer);
+ printf("%s",cmd) ; //DEBUG
+ if (statuscmd == 0) {
+ length = readlink(cmd, cmdcont, 1000) ;
+// printf("\t%0d\n",(int)length) ; //DEBUG
+ if (-1 != length) {
+ cmdcont[length] = 0; // terminate the string
+ printf ("Exe: %s\n\n", cmdcont);
+ }
+ }
+ else {
+ printf(" ... maybe a transitory process\n");
+ }
+ }
}
-
void checkproc() {
int procpids ;
int statusprocbefore, statusprocafter;
struct stat buffer;
- printf ("[*]Searching for Hidden processes through /proc scanning\n\n") ;
+ printf ("[*]Searching for Hidden processes through /proc stat scanning\n\n") ;
for ( procpids = 1; procpids <= maxpid; procpids = procpids +1 ) {
char directory[100] ;
-
+ // avoid ourselves
+ if (procpids == mypid) {
+ continue;
+ }
sprintf(directory,"/proc/%d",procpids);
statusprocbefore = stat(directory, &buffer) ;
@@ -250,7 +330,7 @@ void checkproc() {
continue;
}
- if(checkps(procpids,0)) {
+ if(checkps(procpids,PS_PROC | PS_THREAD)) {
continue;
}
@@ -263,6 +343,194 @@ void checkproc() {
}
}
+void checkchdir() {
+
+ int procpids ;
+ int statusdir;
+ char curdir[PATH_MAX] ;
+// char scratch[PATH_MAX] ; // DEBUG
+// int count = 0; //DEBUG
+
+ printf ("[*]Searching for Hidden processes through /proc chdir scanning\n\n") ;
+ // get the path where Unhide is ran from.
+ getcwd(curdir, PATH_MAX);
+
+ for ( procpids = 1; procpids <= maxpid; procpids = procpids +1 ) {
+
+ char directory[100] ;
+
+ // avoid ourselves
+ if (procpids == mypid) {
+ continue;
+ }
+
+ sprintf(directory,"/proc/%d",procpids);
+
+ statusdir = chdir(directory) ;
+ // the directory doesn't exist continue with the next one
+ if (statusdir != 0) {
+ continue;
+ }
+ if (morecheck == TRUE) {
+ // find process group ID (the master thread) by reading the status file of the current dir
+ FILE *fich_tmp ;
+ int found_tgid = FALSE;
+ char line[128] ;
+ char* tmp_pids = line;
+ char* end_pid;
+ char new_directory[100] ;
+
+// printf("directory = '%s'\n", directory); // DEBUG
+// getcwd(scratch, PATH_MAX); // DEBUG
+// printf("CWD = '%s'\n", scratch); // DEBUG
+
+ fich_tmp=fopen("status", "r") ;
+ if (fich_tmp == NULL) {
+ if(TRUE == verbose) {
+ printf("Warning : can't open status file for process: %d)\n", procpids);
+ }
+ continue ; // next process
+ }
+ while ((FALSE == found_tgid) && (!feof (fich_tmp))) {
+
+ if (NULL != fgets (line, 128, fich_tmp)) {
+ line[127] = 0;
+ if (0 == strncmp (line, "Tgid:", 5)) {
+ found_tgid = TRUE;
+ }
+ }
+ }
+ fclose(fich_tmp);
+
+ if (TRUE == found_tgid) {
+ tmp_pids = line + 5;
+ while( ((*tmp_pids == ' ') || (*tmp_pids == '\t')) && (tmp_pids <= line+127)) {
+ tmp_pids++;
+ }
+// printf("tmp_pids2 = '%s'\n", tmp_pids); // DEBUG
+ end_pid = tmp_pids;
+ while( isdigit(*end_pid) && end_pid <= line+127) {
+ end_pid++;
+ }
+ *end_pid = 0; // remove \n
+// if the number of threads is < to about 40 % of the number of processes,
+// the next "optimising" test actually produce a slower executable.
+// if(procpids != atoi(tmp_pids))
+ { // if the thread isn't the master thread (process)
+// count++; // DEBUG
+ sprintf(new_directory,"/proc/%s/task/%d", tmp_pids, procpids) ;
+// printf("new_dir = %s\n", new_directory); // DEBUG
+ statusdir = chdir(new_directory) ;
+ if (statusdir != 0) {
+ // the thread is not listed in the master thread task directory
+ if(TRUE == verbose) {
+ printf("Warning : Thread %s said it's in group %d but isn't listed in %s\n", tmp_pids, procpids, new_directory);
+ }
+ }
+ }
+ }
+ else {
+ if(TRUE == verbose) {
+ printf("Warning : Can't find TGID in status file for process': %d)\n", procpids);
+ }
+ }
+ }
+
+ // unlock the proc directory so it can disappear if it's a transitory process
+ chdir(curdir);
+
+ if(checkps(procpids, PS_PROC | PS_THREAD)) {
+ continue;
+ }
+
+ // Avoid false positive on short life process/thread
+ statusdir = chdir(directory) ;
+ if (statusdir != 0) {
+ continue;
+ }
+
+ printbadpid(procpids);
+ }
+ // go back to our path
+ chdir(curdir);
+// printf("Passages = %d\n", count); // DEBUG
+}
+
+
+void checkreaddir() {
+
+ int procpids ;
+ DIR *procdir, *taskdir;
+ struct dirent *dir, *dirproc;
+
+ printf ("[*]Searching for Hidden thread through /proc/pid/task readdir scanning\n\n") ;
+
+ procdir = opendir("/proc");
+ if (NULL == procdir) {
+ if(TRUE == verbose) {
+ printf("Warning : Cannot open /proc directory !");
+ }
+ return ;
+ }
+ while ((dirproc = readdir(procdir))) {
+ // As of Linux kernel 2.6 :
+ // readdir directly in /proc only see process, not thread
+ // because procfs voluntary hides threads to readdir
+ char *directory ;
+ char task[100] ;
+
+ directory = dirproc->d_name;
+ if(!isdigit(*directory)) {
+ // not a process directory of /proc
+ continue;
+ }
+// sprintf(currentproc, "%d", directory);
+
+ sprintf(task, "/proc/%s/task", directory) ;
+// printf("task : %s", task) ; // DEBUG
+ taskdir = opendir(task);
+ if (NULL == taskdir) {
+ if(TRUE == verbose) {
+ printf("Warning : Cannot open %s directory !", task);
+ }
+ return ;
+ }
+
+ while ((dir = readdir(taskdir)))
+ {
+ char *tmp_d_name ;
+ tmp_d_name = dir->d_name;
+// printf(" thread : %s\n",tmp_d_name) ; // DEBUG
+ if (!strcmp(tmp_d_name, ".") || !strcmp(tmp_d_name, "..")) // skip parent and current dir
+ continue;
+ if(!isdigit(*tmp_d_name)) {
+ if(TRUE == verbose) {
+ printf("Warning : Not a a thread ID (%s) in %s\n", tmp_d_name, task) ;
+ }
+ continue;
+ }
+ else if (0 != strcmp(tmp_d_name, directory)) { // thread ID is not the process ID
+// printf("thread : %s\n",tmp_d_name) ; // DEBUG
+ procpids = atoi(tmp_d_name) ;
+ if(checkps(procpids,PS_THREAD)) {
+ continue;
+ }
+ printbadpid(atoi(tmp_d_name));
+ }
+ else {
+// printf("process : %s\n",tmp_d_name) ; // DEBUG
+ procpids = atoi(tmp_d_name) ;
+ if(checkps(procpids,PS_PROC)) {
+ continue;
+ }
+ printbadpid(atoi(tmp_d_name));
+ }
+ }
+ closedir(taskdir);
+ }
+ closedir(procdir) ;
+}
+
void checkgetpriority() {
int syspids ;
@@ -277,12 +545,17 @@ void checkgetpriority() {
errno= 0 ;
+ // avoid ourselves
+ if (syspids == mypid) {
+ continue;
+ }
+
ret = getpriority(which, syspids);
if ( errno != 0) {
continue;
}
- if(checkps(syspids,0)) {
+ if(checkps(syspids,PS_PROC | PS_THREAD)) {
continue;
}
@@ -309,12 +582,17 @@ void checkgetpgid() {
errno= 0 ;
+ // avoid ourselves
+ if (syspids == mypid) {
+ continue;
+ }
+
ret = getpgid(syspids);
if ( errno != 0 ) {
continue;
}
- if(checkps(syspids,0)) {
+ if(checkps(syspids,PS_PROC | PS_THREAD)) {
continue;
}
@@ -342,11 +620,16 @@ void checkgetsid() {
errno= 0 ;
+ // avoid ourselves
+ if (syspids == mypid) {
+ continue;
+ }
+
ret = getsid(syspids);
if ( errno != 0) {
continue;
}
- if(checkps(syspids,0)) {
+ if(checkps(syspids,PS_PROC | PS_THREAD)) {
continue;
}
errno=0;
@@ -374,11 +657,16 @@ void checksched_getaffinity() {
errno= 0 ;
+ // avoid ourselves
+ if (syspids == mypid) {
+ continue;
+ }
+
ret = sched_getaffinity(syspids, sizeof(cpu_set_t), &mask);
if (errno != 0) {
continue;
}
- if (checkps(syspids,0)) {
+ if (checkps(syspids,PS_PROC | PS_THREAD)) {
continue;
}
errno=0;
@@ -406,12 +694,17 @@ void checksched_getparam() {
errno= 0 ;
+ // avoid ourselves
+ if (syspids == mypid) {
+ continue;
+ }
+
ret = sched_getparam(syspids, ¶m);
if ( errno != 0) {
continue;
}
- if(checkps(syspids,0)) {
+ if(checkps(syspids,PS_PROC | PS_THREAD)) {
continue;
}
@@ -439,12 +732,17 @@ void checksched_getscheduler() {
errno= 0 ;
+ // avoid ourselves
+ if (syspids == mypid) {
+ continue;
+ }
+
ret = sched_getscheduler(syspids);
if ( errno != 0) {
continue;
}
- if(checkps(syspids,0)) {
+ if(checkps(syspids,PS_PROC | PS_THREAD)) {
continue;
}
@@ -472,12 +770,17 @@ void checksched_rr_get_interval() {
errno= 0 ;
+ // avoid ourselves
+ if (syspids == mypid) {
+ continue;
+ }
+
ret = sched_rr_get_interval(syspids, &tp);
if ( errno != 0) {
continue;
}
- if(checkps(syspids,0)) {
+ if(checkps(syspids,PS_PROC | PS_THREAD)) {
continue;
}
@@ -502,12 +805,18 @@ void checkkill() {
int ret;
errno= 0 ;
+
+ // avoid ourselves
+ if (syspids == mypid) {
+ continue;
+ }
+
ret = kill(syspids, 0);
if ( errno != 0) {
continue;
}
- if(checkps(syspids,0)) {
+ if(checkps(syspids,PS_PROC | PS_THREAD)) {
continue;
}
@@ -539,6 +848,11 @@ void checkallnoprocps() {
for ( syspids = 1; syspids <= maxpid; syspids++ ) {
+ // avoid ourselves
+ if (syspids == mypid) {
+ continue;
+ }
+
found=0;
found_killbefore=0;
found_killafter=0;
@@ -588,11 +902,265 @@ void checkallnoprocps() {
}
} /* else: unreliable */
else {
- printf("Warning : syscall comparison test skipped for PID %d", syspids);
+ if(TRUE == verbose) {
+ printf("Warning : syscall comparison test skipped for PID %d", syspids);
+ }
}
}
}
+void checkallquick() {
+
+ /* compare the various system calls against each other,
+ * without invoking 'ps' or looking at /proc */
+
+ int ret;
+ int syspids;
+ struct timespec tp;
+ struct sched_param param;
+ cpu_set_t mask;
+ int found=0;
+ int found_killbefore=0;
+ int found_killafter=0;
+ char directory[100];
+ struct stat buffer;
+ int statusproc, statusdir;
+ char curdir[PATH_MAX] ;
+
+ printf ("[*]Searching for Hidden processes through comparison of results of system calls, proc, dir and ps\n\n") ;
+
+ // get the path where Unhide is ran from.
+ getcwd(curdir, PATH_MAX);
+
+ for ( syspids = 1; syspids <= maxpid; syspids++ ) {
+
+ // avoid ourselves
+ if (syspids == mypid) {
+ continue;
+ }
+
+ // printf("syspid = %d\n", syspids); //DEBUG
+
+ found=0;
+ found_killbefore=0;
+ found_killafter=0;
+
+ errno=0;
+ ret = kill(syspids, 0);
+ if (errno == 0) found_killbefore=1;
+
+ errno= 0 ;
+ ret = getpriority(PRIO_PROCESS, syspids);
+ if (errno == 0) found++;
+
+ errno= 0 ;
+ ret = getpgid(syspids);
+ if (errno == 0) found++;
+
+ errno= 0 ;
+ ret = getsid(syspids);
+ if (errno == 0) found++;
+
+ errno= 0 ;
+ ret = sched_getaffinity(syspids, sizeof(cpu_set_t), &mask);
+ if (ret == 0) found++;
+
+ errno= 0 ;
+ ret = sched_getparam(syspids, ¶m);
+ if (errno == 0) found++;
+
+ errno= 0 ;
+ ret = sched_getscheduler(syspids);
+ if (errno == 0) found++;
+
+ errno=0;
+ ret = sched_rr_get_interval(syspids, &tp);
+ if (errno == 0) found++;
+
+ sprintf(directory,"/proc/%d",syspids);
+
+ statusproc = stat(directory, &buffer) ;
+ if (statusproc == 0) {
+ found++;
+ }
+
+ statusdir = chdir(directory) ;
+ if (statusdir == 0) {
+ found++;
+ chdir(curdir);
+ }
+
+ if ((0 != found) || (0 != found_killbefore)) {
+ if(checkps(syspids,PS_PROC | PS_THREAD)) {
+ found++;
+ }
+ }
+
+ errno=0;
+ ret = kill(syspids, 0);
+ if (errno == 0) found_killafter=1;
+
+
+ /* these should all agree, except if a process went or came in the middle */
+ if (found_killbefore == found_killafter) {
+ if ( ! ((found_killbefore == 0 && found == 0) ||
+ (found_killbefore == 1 && found == 10)) ) {
+ printbadpid(syspids);
+ }
+ } /* else: unreliable */
+ else {
+ if(TRUE == verbose) {
+ printf("Warning : syscall comparison test skipped for PID %d", syspids);
+ }
+ }
+ }
+}
+
+
+void checkallreverse() {
+
+ /* verify if all thread showed by ps are also seen by others */
+
+ int ret;
+ int syspids;
+ struct timespec tp;
+ struct sched_param param;
+ cpu_set_t mask;
+ int found=0;
+ int found_killbefore=0;
+ int found_killafter=0;
+ FILE *fich_tmp;
+ char command[50];
+ char read_line[1024];
+ char lwp[7];
+ int index;
+ char directory[100];
+ struct stat buffer;
+ int statusproc, statusdir;
+ char curdir[PATH_MAX] ;
+
+ printf ("[*]Searching for Hidden processes by verifying that all thread seen by ps are also seen by others\n\n") ;
+
+ sprintf(command,REVERSE) ;
+
+ // get the path where Unhide is ran from.
+ getcwd(curdir, PATH_MAX);
+
+ fich_tmp=popen (command, "r") ;
+ if (fich_tmp == NULL) {
+ if(TRUE == verbose) {
+ printf("Warning : popen failed while running ps (memory, or something set errno: %s)\n", strerror(errno));
+ }
+ return;
+ }
+
+ while (!feof(fich_tmp)) {
+ char* curline = read_line;
+
+ fgets(read_line, 1024, fich_tmp);
+ read_line[1023] = 0;
+
+// printf("read_line = %s\n", read_line); // DEBUG
+ while( *curline == ' ' && curline <= read_line+1023) {
+ curline++;
+ }
+
+ // get LWP
+ index=0;
+ while( isdigit(*curline) && curline <= read_line+1023) {
+ lwp[index++] = *curline;
+ curline++;
+ }
+ lwp[index] = 0; // terminate string
+
+ syspids = -1;
+ syspids = atol(lwp);
+ if (-1 == syspids) continue ; // something went wrong
+
+ // avoid ourselves
+ if (syspids == mypid) {
+ continue;
+ }
+
+ found=0;
+ found_killbefore=0;
+ found_killafter=0;
+
+ errno=0;
+ ret = kill(syspids, 0);
+ if (errno == 0) found_killbefore=1;
+
+ strcpy(directory,"/proc/");
+ strcat(directory,lwp);
+
+ statusproc = stat(directory, &buffer) ;
+ if (statusproc != 0) {
+ found++;
+ }
+
+ statusdir = chdir(directory) ;
+ if (statusdir != 0) {
+ found++;
+ }
+ else {
+ chdir(curdir);
+ }
+
+ errno= 0 ;
+ ret = getpriority(PRIO_PROCESS, syspids);
+ if (errno != 0) found++;
+
+ errno= 0 ;
+ ret = getpgid(syspids);
+ if (errno != 0) found++;
+
+ errno= 0 ;
+ ret = getsid(syspids);
+ if (errno != 0) found++;
+
+ errno= 0 ;
+ ret = sched_getaffinity(syspids, sizeof(cpu_set_t), &mask);
+ if (ret != 0) found++;
+
+ errno= 0 ;
+ ret = sched_getparam(syspids, ¶m);
+ if (errno != 0) found++;
+
+ errno= 0 ;
+ ret = sched_getscheduler(syspids);
+ if (errno != 0) found++;
+
+ errno=0;
+ ret = sched_rr_get_interval(syspids, &tp);
+ if (errno != 0) found++;
+
+ errno=0;
+ ret = kill(syspids, 0);
+ if (errno == 0) found_killafter=1;
+
+
+ /* these should all agree, except if a process went or came in the middle */
+ if (found_killbefore == found_killafter) {
+ if (0 != found) {
+ printf ("Found HIDDEN PID: %i\n", syspids) ;
+ printf ("\tCommand = %s\n", curline) ;
+ //printf("\tfound = %d\n", found); // DEBUG
+ }
+ } /* else: unreliable */
+ else {
+ if(TRUE == verbose) {
+ printf("Warning : reverse test skipped for PID %d", syspids);
+ }
+ }
+ }
+
+ if (fich_tmp != NULL)
+ pclose(fich_tmp);
+
+}
+
+
+
void checksysinfo() {
struct sysinfo info;
@@ -611,14 +1179,17 @@ void checksysinfo() {
fich_proceso=popen (SYS_COMMAND, "r") ;
if (fich_proceso == NULL) {
- printf("popen failed while checking sysinfo (memory, or something set errno: %s)\n", strerror(errno));
+ if(TRUE == verbose) {
+ printf("Warning : popen failed while checking sysinfo (memory, or something set errno: %s)\n", strerror(errno));
+ }
return;
}
buffer[499] = '\0';
while (!feof(fich_proceso)) {
- fscanf( fich_proceso, "%499s", &buffer[0] );
+// fscanf( fich_proceso, "%499s", &buffer[0] );
+ fgets(buffer, 499, fich_proceso);
contador++;
}
@@ -641,7 +1212,9 @@ void checksysinfo() {
}
}
else {
- printf("Warning : sysinfo test skipped due to intermittent activity");
+ if(TRUE == verbose) {
+ printf("Warning : sysinfo test skipped due to intermittent activity");
+ }
}
}
@@ -670,7 +1243,7 @@ void brute() {
}
- for (i=0; i < maxpid; i++) {
+ for (i=300; i < maxpid; i++) {
int vpid;
int status;
@@ -695,7 +1268,7 @@ void brute() {
if (allpids[y] != 0) {
- if(!checkps(allpids[y],1) ) {
+ if(!checkps(allpids[y],PS_PROC | PS_THREAD | PS_MORE) ) {
printbadpid(allpids[y]);
@@ -720,7 +1293,7 @@ void brute() {
}
- for (i=0; i < maxpid ; i++) {
+ for (i=300; i < maxpid ; i++) {
void *status;
errno= 0 ;
@@ -755,7 +1328,7 @@ void brute() {
if (allpids[y] != 0) {
- if(!checkps(allpids[y],1) ) {
+ if(!checkps(allpids[y],PS_PROC | PS_THREAD | PS_MORE) ) {
printbadpid(allpids[y]);
@@ -764,44 +1337,83 @@ void brute() {
}
-
-
}
+void usage(char * command) {
+ printf("Usage: %s [options] proc | procfs | sys | brute | reverse | quick\n\n", command);
+ printf("Option :\n");
+ printf(" -v verbose\n");
+ printf(" -h display this help\n");
+ printf(" -m more checks (available only with procfs command)\n");
+}
int main (int argc, char *argv[]) {
- printf ("Unhide 20100201\n") ;
+int i, test = TST_NONE;
+
+ printf ("Unhide 20100819\n") ;
printf ("http://www.security-projects.com/?Unhide\n\n\n") ;
get_max_pid(&maxpid);
- if(argc != 2) {
-
- printf("usage: %s proc | sys | brute\n\n", argv[0]);
+ if(argc < 2) {
+ usage(argv[0]);
exit (1);
}
+ for (i=1 ; i<argc ; i++) {
+ if (strcmp(argv[i], "-v") == 0) {verbose = TRUE;}
+ else if (strcmp(argv[i], "-h") == 0) {usage(argv[0]); exit(0);}
+ else if (strcmp(argv[i], "-m") == 0) {morecheck = TRUE; verbose = TRUE;}
+ else if (strcmp(argv[i], "proc") == 0) {test= TST_PROC;}
+ else if (strcmp(argv[i], "procfs") == 0) {test= TST_DIR;}
+ else if (strcmp(argv[i], "sys") == 0) {test= TST_SYS;}
+ else if (strcmp(argv[i], "brute") == 0) {test= TST_BRUTE;}
+ else if (strcmp(argv[i], "reverse") == 0) {test= TST_REVERSE;}
+ else if (strcmp(argv[i], "quick") == 0) {test= TST_QUICK;}
+ else { printf("Unknown argument\n") ; usage(argv[0]); exit(0);}
+ }
+
setpriority(PRIO_PROCESS,0,-20); /* reduce risk from intermittent processes - may fail, dont care */
- if (strcmp(argv[1], "proc") == 0) {checkproc();}
+ mypid = getpid();
+
+ switch (test) {
+ case TST_PROC :
+ checkproc();
+ break;
+ case TST_DIR :
+ checkreaddir();
+ checkchdir();
+ break;
+ case TST_SYS :
+ checkkill();
+ checkallnoprocps();
+ checkgetpriority();
+ checkgetpgid() ;
+ checkgetsid();
+ checksched_getaffinity();
+ checksched_getparam();
+ checksched_getscheduler();
+ checksched_rr_get_interval();
+ checksysinfo();
+ break;
+ case TST_BRUTE :
+ brute();
+ break;
+ case TST_REVERSE :
+ checkallreverse();
+ break;
+ case TST_QUICK :
+ checkallquick();
+ checksysinfo();
+ break;
+ default :
+ printf("Unknown test\n");
+ }
+
- else if (strcmp(argv[1], "sys") == 0) {
- checkkill();
- checkallnoprocps();
- checkgetpriority();
- checkgetpgid() ;
- checkgetsid();
- checksched_getaffinity();
- checksched_getparam();
- checksched_getscheduler();
- checksched_rr_get_interval();
- checksysinfo();
- }
- else if(strcmp(argv[1], "brute") == 0) {
- brute();
- }
return 0;
}
--
debian-forensics/unhide
More information about the forensics-changes
mailing list