unhide-20130526/0000755000076400007640000000000012150411332012616 5ustar pgouinpgouinunhide-20130526/sanity-tcp.sh0000755000076400007640000000420512150411332015251 0ustar pgouinpgouin#!/bin/sh # sanity.sh -- a growing testsuite for unhide-tcp. # # Copyright (C) 2010 Patrick Gouin. # # 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 . # # Original Author: Patrick Gouin # BSD portability: Nikos Ntarmos if [ "x`/usr/bin/env uname`" == "xLinux" ]; then ONFREEBSD=0 CHECKER=ss else ONFREEBSD=1 CHECKER=netstat fi # remove pre-existing local ss rm -f ./$CHECKER #test 0 # Don't call CHECKER : let all ports appear hidden cat <./$CHECKER #!/bin/sh false EOF chmod 754 ./$CHECKER PATH=.:$PATH ./unhide-tcp -fl # PATH=.:$PATH ./unhide-tcp #PATH=.:$PATH ./unhide-tcp-double_check # remove pre-existing local $CHECKER rm -f ./$CHECKER #test 1 # Call $CHECKER : let cups port appears hidden cat <./$CHECKER #!/bin/sh set -e # echo "Le 1er paramètre est : \$1" >&2 # echo "Le 2ème paramètre est : \$2" >&2 # echo "Le 3ème paramètre est : \$3" >&2 # echo "Le 4ème paramètre est : \$4" >&2 if [ $ONFREEBSD -eq 1 ] then /usr/bin/netstat \$@ | grep -v 631 exit elif [ "\$4" != ":631" ] then # appelle le véritable ss /sbin/ss \$@ else echo "Le 4ème paramètre est : \$4" >&2 fi EOF chmod 754 ./$CHECKER PATH=.:$PATH ./unhide-tcp -fl # PATH=.:$PATH ./unhide-tcp-double_check -fl # remove pre-existing local CHECKER #rm -f ./$CHECKER unhide-20130526/unhide-tcp-simple-check.c0000644000076400007640000003114512150411332017370 0ustar pgouinpgouin/* http://www.unhide-forensics.info */ /* 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 . */ #include #include #include #include #include #include #include #include #include #include #include #include #include "unhide-output.h" #include "unhide-tcp.h" // header const char header[] = "Unhide-tcp 20121229\n" "Copyright © 2012 Yago Jesus & Patrick Gouin\n" "License GPLv3+ : GNU GPL version 3 or later\n" "http://www.unhide-forensics.info\n"; // options int verbose = 0; int use_fuser = 0; int use_lsof = 0; int use_ss = 1; // use ss by default int use_quick = 0; char checker[10] = "ss" ; // Temporary string for output char scratch[1000]; // Temporary string for output char used_options[1000] = ""; // For logging to file int logtofile = 0; FILE *unlog; // Global hidden port counter, used only to set the exit code of the program int hidden_found; /* thx aramosf@unsec.net for the nice regexp! */ // Linux char tcpcommand1[]= "netstat -tan | sed -e '/[\\.:][0-9]/!d' -e 's/.*[\\.:]\\([0-9]*\\) .*[\\.:].*/\\1/'" ; char udpcommand1[]= "netstat -uan | sed -e '/[\\.:][0-9]/!d' -e 's/.*[\\.:]\\([0-9]*\\) .*[\\.:].*/\\1/'" ; // Alternative commands, needs iproute2 char tcpcommand2[]= "ss -tan sport = :%d | sed -e '/[\\.:][0-9]/!d' -e 's/.*[\\.:]\\([0-9]*\\) .*[\\.:].*/\\1/'" ; char udpcommand2[]= "ss -uan sport = :%d | sed -e '/[\\.:][0-9]/!d' -e 's/.*[\\.:]\\([0-9]*\\) .*[\\.:].*/\\1/'" ; // fuser commands char fuserTCPcommand[]= "fuser -v -n tcp %d 2>&1" ; char fuserUDPcommand[]= "fuser -v -n udp %d 2>&1" ; // lsof commands char lsofTCPcommand[]= "lsof +c 0 -iTCP:%d" ; char lsofUDPcommand[]= "lsof +c 0 -iUDP:%d" ; // OpenBSD // char tcpcommand[]= "netstat -an -p tcp | sed -e '/[\\.:][0-9]/!d' -e 's/.*[\\.:]\\([0-9]*\\) .*[\\.:].*/\\1/'" ; // char udpcommand[]= "netstat -an -p udp| sed -e '/[\\.:][0-9]/!d' -e 's/.*[\\.:]\\([0-9]*\\) .*[\\.:].*/\\1/'" ; // Solaris // char tcpcommand[]= "netstat -an -P tcp | sed -e '/[\\.:][0-9]/!d' -e 's/.*[\\.:]\\([0-9]*\\) .*[\\.:].*/\\1/'" ; // char udpcommand[]= "netstat -an -P udp| sed -e '/[\\.:][0-9]/!d' -e 's/.*[\\.:]\\([0-9]*\\) .*[\\.:].*/\\1/'" ; /* * Run a command to get more information about a port. */ static void print_info(const char *prog_name, const char *command_fmt, int port) { char buffer[1000]; FILE* fp; sprintf(buffer, command_fmt, port); fp = popen(buffer, "r") ; if (NULL == fp) { warnln(verbose, unlog, "Couldn't run command: %s", buffer) ; return ; } msgln(unlog, 1, "%s reports :", prog_name) ; while (NULL != fgets(buffer, 1000, fp)) { msgln(unlog, 1, buffer) ; } pclose(fp); } /* Print a port, optionally querying info about it via lsof or fuser. */ void print_port(enum Proto proto, int port) { msgln(unlog, 0, "\nFound Hidden port that not appears in %s: %i", checker, port) ; if (1 == use_fuser) { if (TCP == proto) { print_info("fuser", fuserTCPcommand, port); } else { print_info("fuser", fuserUDPcommand, port); } } if (1 == use_lsof) { if (TCP == proto) { print_info("lsof", lsofTCPcommand, port); } else { print_info("lsof", lsofUDPcommand, port); } } } /* * Check if port is seen by netstat. * * If not, report it and optionnally run lsof and/or fuser * to show more info. */ void checkoneport(int port, char command[], enum Proto proto) { int ok = 0; char ports[30]; char compare[100]; FILE *fich_tmp ; if (NULL != (fich_tmp=popen (command, "r"))) { sprintf(compare,"%i\n",port); while ((NULL != fgets(ports, 30, fich_tmp)) && ok == 0) { if (strcmp(ports, compare) == 0) {ok = 1;} } pclose(fich_tmp); } else { die(unlog, "Couldn't execute command : %s while checking port %d", command, port) ; } // test // ok = 0 ; if ( ok == 0 ) { hidden_found++; print_port(proto, port) ; } } /* * Check all TCP ports one by one. */ static void print_hidden_TCP_ports_1_by_1(enum Proto proto) { int i ; char tcpcommand[512] ; hidden_found = 0 ; for (i =1; i <= 65535; i++) { int socket_desc; struct sockaddr_in address; if ( -1 != (socket_desc=socket(AF_INET,SOCK_STREAM,0))) { address.sin_family = AF_INET; address.sin_addr.s_addr = INADDR_ANY; address.sin_port = htons(i); errno= 0 ; if ( -1 != bind(socket_desc,(struct sockaddr *)&address,sizeof(address))) { listen(socket_desc,1); if ( EADDRINUSE == errno ) // port is listened by another process { close(socket_desc); if (1 == use_ss) { sprintf(tcpcommand, tcpcommand2, i) ; } else { strncpy(tcpcommand, tcpcommand1, 512) ; } checkoneport(i, tcpcommand, TCP); } else { close(socket_desc); } } else { if (EADDRINUSE == errno) //port is in use by another process { close(socket_desc); if (1 == use_ss) { sprintf(tcpcommand, tcpcommand2, i) ; } else { strncpy(tcpcommand, tcpcommand1, 512) ; } checkoneport(i, tcpcommand, TCP); } else { close(socket_desc); warnln(verbose, unlog, "can't bind to socket while checking port %d", i) ; } } } else { warnln(verbose, unlog, "can't create socket while checking port %d/tcp", i) ; } } } /* * Check all UDP ports one by one. */ static void print_hidden_UDP_ports_1_by_1(enum Proto proto) { int u ; char udpcommand[512] ; hidden_found = 0 ; for (u = 1; u <= 65535; u++) { int socket_desc; struct sockaddr_in address; if ( -1 != (socket_desc=socket(AF_INET,SOCK_DGRAM,0))) { address.sin_family = AF_INET; address.sin_addr.s_addr = INADDR_ANY; address.sin_port = htons(u); errno= 0 ; if ( 0 != bind(socket_desc,(struct sockaddr *)&address,sizeof(address))) { if ( EADDRINUSE == errno ) //port is in use by another process { close(socket_desc); if (1 == use_ss) { sprintf(udpcommand, udpcommand2, u) ; } else { strncpy(udpcommand, udpcommand1, 512) ; } checkoneport(u, udpcommand, UDP); } else // other error { close(socket_desc); warnln(verbose, unlog, "can't bind to socket while checking port %d", u) ; } } else // port is available { close(socket_desc); } } else { warnln(verbose, unlog, "can't create socket while checking port %d/udp", u) ; } } } /* * Print usage. */ void usage(char * command) { printf("Usage: %s [options] \n\n", command); printf("Options :\n"); printf(" -V Show version and exit\n"); printf(" -v verbose\n"); printf(" -h display this help\n"); printf(" -f show fuser output for hidden ports\n"); printf(" -l show lsof output for hidden ports\n"); printf(" -o log result into unhide-tcp.log file\n"); printf(" -s use very quick version for server with lot of opened ports\n"); printf(" -n use netstat instead of ss\n"); } /* * Parse command line arguments (exiting if requested by any option). */ void parse_args(int argc, char **argv) { int c = 0; static struct option long_options[] = { /* These options set a flag. */ {"verbose", no_argument, &verbose, 1}, {"brief", no_argument, &verbose, 0}, {"fuser", no_argument, &use_fuser, 1}, {"lsof", no_argument, &use_lsof, 1}, {"log", no_argument, &logtofile, 1}, {"netstat", no_argument, &use_ss, 0}, {"server", no_argument, &use_quick, 1}, /* These options don't set a flag. We distinguish them by their indices. */ {"help", no_argument, 0, 'h'}, {"version", no_argument, 0, 'V'}, {0, 0, 0, 0} }; for(;;) // until there's no more option { /* getopt_long stores the option index here. */ int option_index = 0; c = getopt_long (argc, argv, "Vvhflosn", long_options, &option_index); /* Detect the end of the options. */ if (c == -1) break; switch(c) { case 0 : // flag long options if (long_options[option_index].flag != 0) //if this option set a flag { break; // nothing to do } printf ("option %s", long_options[option_index].name); if (optarg) // if there's an argument { printf (" with arg %s", optarg); } printf ("\n"); break ; case 'f' : use_fuser = 1 ; break ; case 'h' : usage(argv[0]) ; exit (0) ; break ; case 'l' : use_lsof = 1 ; break; case 'o' : logtofile = 1 ; break ; case 's' : use_quick = 1 ; break ; case 'n' : use_ss = 0 ; break ; case 'v' : verbose++ ; ; break ; case 'V' : exit (0) ; break ; case '?' : // invalid option exit (2) ; break ; default : // something very nasty happened exit(-1) ; break ; } } // generate options string for logging strncpy(used_options, "Used options: ", 1000); if (verbose) strncat(used_options, "verbose ", 1000-1-strlen(used_options)); if (use_lsof) strncat(used_options, "use_lsof ", 1000-1-strlen(used_options)); if (use_fuser) strncat(used_options, "use_fuser ", 1000-1-strlen(used_options)); if (!use_ss) strncat(used_options, "use_netscape ", 1000-1-strlen(used_options)); if (use_quick) strncat(used_options, "use_quick ", 1000-1-strlen(used_options)); if (logtofile) strncat(used_options, "logtofile ", 1000-1-strlen(used_options)); } /* * Look for TCP and UDP ports that are hidden to netstat. * * Returns 0 if none is found, 1 if there is some internal error, 4 if TCP * hidden ports were found, 8 if UDP hidden ports were found or 12 (4 & 8) if * both were found. */ int main(int argc, char **argv) { int ret_code = 0; printf(header) ; if(getuid() != 0){ die(unlog, "You must be root to run %s !", argv[0]) ; } parse_args(argc, argv) ; if (1 == logtofile) { unlog = init_log(logtofile, header, "unhide-tcp") ; } msgln(unlog, 0, used_options) ; if (1 == use_ss) { strncpy(checker, "ss", 10); } else { strncpy(checker, "netstat", 10); } setpriority(PRIO_PROCESS,0,-20); /* reduce risk of race condition - may fail, dont care */ msgln(unlog, 0, "[*]Starting TCP checking") ; if (1 == use_quick) { print_hidden_ports(TCP); } else { print_hidden_TCP_ports_1_by_1(TCP) ; } if (hidden_found) { ret_code += 4; } msgln(unlog, 0, "[*]Starting UDP checking") ; if (1 == use_quick) { print_hidden_ports(UDP); } else { print_hidden_UDP_ports_1_by_1(UDP) ; } if (hidden_found) { ret_code += 8; } if (logtofile == 1) { close_log(unlog, "unhide-tcp") ; } return(ret_code); } unhide-20130526/unhide-tcp.c0000644000076400007640000003556612150411332015041 0ustar pgouinpgouin/* http://www.unhide-forensics.info */ /* 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 . */ #include #include #include #include #include #include #include #include #include #include #include #include #include "unhide-output.h" #include "unhide-tcp.h" // header const char header[] = "Unhide-tcp 20130526\n" "Copyright © 2013 Yago Jesus & Patrick Gouin\n" "License GPLv3+ : GNU GPL version 3 or later\n" "http://www.unhide-forensics.info\n"; // options int verbose = 0; int use_fuser = 0; int use_lsof = 0; #ifdef __linux__ int use_ss = 1; // on Linux use ss by default #else int use_ss = 0; // else don't use ss by default #endif int use_quick = 0; char checker[10] = "ss" ; // Temporary string for output char scratch[1000]; // Temporary string for output char used_options[1000] = ""; // For logging to file int logtofile = 0; FILE *unlog; // Global hidden port counter, used only to set the exit code of the program int hidden_found; /* thx aramosf@unsec.net for the nice regexp! */ // Default commands for Linux, needs iproute2 char tcpcommand2[]= "ss -tan sport = :%d | sed -e '/[\\.:][0-9]/!d' -e 's/.*[\\.:]\\([0-9]*\\) .*[\\.:].*/\\1/'" ; char udpcommand2[]= "ss -uan sport = :%d | sed -e '/[\\.:][0-9]/!d' -e 's/.*[\\.:]\\([0-9]*\\) .*[\\.:].*/\\1/'" ; // fuser commands // for FreeBSD, use sockstat as fuser equivalent. #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) // FreeBSD char fuserTCPcommand[]= "sockstat -46 -p %d -P tcp" ; char fuserUDPcommand[]= "sockstat -46 -p %d -P udp" ; #else char fuserTCPcommand[]= "fuser -v -n tcp %d 2>&1" ; char fuserUDPcommand[]= "fuser -v -n udp %d 2>&1" ; #endif // lsof commands char lsofTCPcommand[]= "lsof +c 0 -iTCP:%d" ; char lsofUDPcommand[]= "lsof +c 0 -iUDP:%d" ; #ifdef __OpenBSD__ // OpenBSD char tcpcommand1[]= "netstat -an -p tcp | sed -e '/[\\.:][0-9]/!d' -e 's/.*[\\.:]\\([0-9]*\\) .*[\\.:].*/\\1/'" ; char udpcommand1[]= "netstat -an -p udp| sed -e '/[\\.:][0-9]/!d' -e 's/.*[\\.:]\\([0-9]*\\) .*[\\.:].*/\\1/'" ; #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) // FreeBSD char tcpcommand1[]= "netstat -an -p tcp | sed -e '/[\\.:][0-9]/!d' -e 's/.*[\\.:]\\([0-9]*\\) .*[\\.:].*/\\1/'" ; char udpcommand1[]= "netstat -an -p udp| sed -e '/[\\.:][0-9]/!d' -e 's/.*[\\.:]\\([0-9]*\\) .*[\\.:].*/\\1/'" ; #elif (defined(sun) || defined(__sun)) && (defined(__SVR4) || defined(__svr4__)) // Solaris char tcpcommand1[]= "netstat -an -P tcp | sed -e '/[\\.:][0-9]/!d' -e 's/.*[\\.:]\\([0-9]*\\) .*[\\.:].*/\\1/'" ; char udpcommand1[]= "netstat -an -P udp| sed -e '/[\\.:][0-9]/!d' -e 's/.*[\\.:]\\([0-9]*\\) .*[\\.:].*/\\1/'" ; #else // Linux / default char tcpcommand1[]= "netstat -tan | sed -e '/[\\.:][0-9]/!d' -e 's/.*[\\.:]\\([0-9]*\\) .*[\\.:].*/\\1/'" ; char udpcommand1[]= "netstat -uan | sed -e '/[\\.:][0-9]/!d' -e 's/.*[\\.:]\\([0-9]*\\) .*[\\.:].*/\\1/'" ; #endif /* * Run a command to get more information about a port. */ static void print_info(const char *prog_name, const char *command_fmt, int port) { char buffer[1000]; FILE* fp; sprintf(buffer, command_fmt, port); fp = popen(buffer, "r") ; if (NULL == fp) { warnln(verbose, unlog, "Couldn't run command: %s", buffer) ; return ; } msgln(unlog, 1, "%s reports :", prog_name) ; while (NULL != fgets(buffer, 1000, fp)) { msgln(unlog, 1, buffer) ; } pclose(fp); } /* Print a port, optionally querying info about it via lsof or fuser. */ void print_port(enum Proto proto, int port) { msgln(unlog, 0, "\nFound Hidden port that not appears in %s: %i", checker, port) ; if (1 == use_fuser) { if (TCP == proto) { #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) print_info("sockstat", fuserTCPcommand, port); #else print_info("fuser", fuserTCPcommand, port); #endif } else { #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) print_info("sockstat", fuserUDPcommand, port); #else print_info("fuser", fuserUDPcommand, port); #endif } } if (1 == use_lsof) { if (TCP == proto) { print_info("lsof", lsofTCPcommand, port); } else { print_info("lsof", lsofUDPcommand, port); } } } /* * Check if port is seen by netstat. * * If not, report it and optionnally run lsof and/or fuser * to show more info. */ int checkoneport(int port, char command[], enum Proto proto) { int ok = 0; char ports[30]; char compare[100]; FILE *fich_tmp ; if (NULL != (fich_tmp=popen (command, "r"))) { sprintf(compare,"%i\n",port); while ((NULL != fgets(ports, 30, fich_tmp)) && ok == 0) { if (strcmp(ports, compare) == 0) {ok = 1;} } pclose(fich_tmp); } else { die(unlog, "Couldn't execute command : %s while checking port %d", command, port) ; } return(ok) ; } /* * Check all TCP ports one by one. */ static void print_hidden_TCP_ports_1_by_1(enum Proto proto) { int i ; char tcpcommand[512] ; hidden_found = 0 ; for (i =1; i <= 65535; i++) { int socket_desc; struct sockaddr_in address; if ( -1 != (socket_desc=socket(AF_INET,SOCK_STREAM,0))) { address.sin_family = AF_INET; address.sin_addr.s_addr = INADDR_ANY; address.sin_port = htons(i); errno= 0 ; if ( -1 != bind(socket_desc,(struct sockaddr *)&address,sizeof(address))) { listen(socket_desc,1); if ( EADDRINUSE == errno ) // port is listened by another process { if (1 == use_ss) { sprintf(tcpcommand, tcpcommand2, i) ; } else { strncpy(tcpcommand, tcpcommand1, 512) ; } if (0 == checkoneport(i, tcpcommand, TCP)) { // test again listen(socket_desc,1); if ( EADDRINUSE == errno ) // port is still listened by another process { hidden_found++; print_port(proto, i) ; } } close(socket_desc); } else { close(socket_desc); } } else { if (EADDRINUSE == errno) //port is in use by another process { if (1 == use_ss) { sprintf(tcpcommand, tcpcommand2, i) ; } else { strncpy(tcpcommand, tcpcommand1, 512) ; } if (0 == checkoneport(i, tcpcommand, TCP)) { // test again if ( -1 == bind(socket_desc,(struct sockaddr *)&address,sizeof(address))) { if ( EADDRINUSE == errno ) // port is still used by another process { hidden_found++; print_port(proto, i) ; } else { warnln(verbose, unlog, "can't bind to socket while checking port %d", i) ; } close(socket_desc); } } else { close(socket_desc); } } } } else { warnln(verbose, unlog, "can't create socket while checking port %d/tcp", i) ; } } } /* * Check all UDP ports one by one. */ static void print_hidden_UDP_ports_1_by_1(enum Proto proto) { int u ; char udpcommand[512] ; hidden_found = 0 ; for (u = 1; u <= 65535; u++) { int socket_desc; struct sockaddr_in address; if ( -1 != (socket_desc=socket(AF_INET,SOCK_DGRAM,0))) { address.sin_family = AF_INET; address.sin_addr.s_addr = INADDR_ANY; address.sin_port = htons(u); errno= 0 ; if ( 0 != bind(socket_desc,(struct sockaddr *)&address,sizeof(address))) { if ( EADDRINUSE == errno ) //port is in use by another process { if (1 == use_ss) { sprintf(udpcommand, udpcommand2, u) ; } else { strncpy(udpcommand, udpcommand1, 512) ; } ; if (0 == checkoneport(u, udpcommand, UDP)) { // test again if ( 0 != bind(socket_desc,(struct sockaddr *)&address,sizeof(address))) // port is still in use by another process { if ( EADDRINUSE == errno ) //port is in use by another process { hidden_found++; print_port(proto, u) ; } } } close(socket_desc); } else // other error { close(socket_desc); warnln(verbose, unlog, "can't bind to socket while checking port %d", u) ; } } else // port is available { close(socket_desc); } } else { warnln(verbose, unlog, "can't create socket while checking port %d/udp", u) ; } } } /* * Print usage. */ void usage(char * command) { printf("Usage: %s [options] \n\n", command); printf("Options :\n"); printf(" -V Show version and exit\n"); printf(" -v verbose\n"); printf(" -h display this help\n"); printf(" -f show fuser output for hidden ports\n"); printf(" -l show lsof output for hidden ports\n"); printf(" -o log result into unhide-tcp.log file\n"); printf(" -s use very quick version for server with lot of opened ports\n"); printf(" -n use netstat instead of ss\n"); } /* * Parse command line arguments (exiting if requested by any option). */ void parse_args(int argc, char **argv) { int c = 0; static struct option long_options[] = { /* These options set a flag. */ {"verbose", no_argument, &verbose, 1}, {"brief", no_argument, &verbose, 0}, {"fuser", no_argument, &use_fuser, 1}, {"lsof", no_argument, &use_lsof, 1}, {"log", no_argument, &logtofile, 1}, {"netstat", no_argument, &use_ss, 0}, {"server", no_argument, &use_quick, 1}, /* These options don't set a flag. We distinguish them by their indices. */ {"help", no_argument, 0, 'h'}, {"version", no_argument, 0, 'V'}, {0, 0, 0, 0} }; for(;;) // until there's no more option { /* getopt_long stores the option index here. */ int option_index = 0; c = getopt_long (argc, argv, "Vvhflosn", long_options, &option_index); /* Detect the end of the options. */ if (c == -1) break; switch(c) { case 0 : // flag long options if (long_options[option_index].flag != 0) //if this option set a flag { break; // nothing to do } printf ("option %s", long_options[option_index].name); if (optarg) // if there's an argument { printf (" with arg %s", optarg); } printf ("\n"); break ; case 'f' : use_fuser = 1 ; break ; case 'h' : usage(argv[0]) ; exit (0) ; break ; case 'l' : use_lsof = 1 ; break; case 'o' : logtofile = 1 ; break ; case 's' : use_quick = 1 ; break ; case 'n' : use_ss = 0 ; break ; case 'v' : verbose++ ; ; break ; case 'V' : exit (0) ; break ; case '?' : // invalid option exit (2) ; break ; default : // something very nasty happened exit(-1) ; break ; } } // generate options string for logging strncpy(used_options, "Used options: ", 1000); if (verbose) strncat(used_options, "verbose ", 1000-1-strlen(used_options)); if (use_lsof) strncat(used_options, "use_lsof ", 1000-1-strlen(used_options)); if (use_fuser) strncat(used_options, "use_fuser ", 1000-1-strlen(used_options)); if (!use_ss) strncat(used_options, "use_netscape ", 1000-1-strlen(used_options)); if (use_quick) strncat(used_options, "use_quick ", 1000-1-strlen(used_options)); if (logtofile) strncat(used_options, "logtofile ", 1000-1-strlen(used_options)); } /* * Look for TCP and UDP ports that are hidden to netstat. * * Returns 0 if none is found, 1 if there is some internal error, 4 if TCP * hidden ports were found, 8 if UDP hidden ports were found or 12 (4 & 8) if * both were found. */ int main(int argc, char **argv) { int ret_code = 0; printf(header) ; if(getuid() != 0){ die(unlog, "You must be root to run %s !", argv[0]) ; } parse_args(argc, argv) ; if (1 == logtofile) { unlog = init_log(logtofile, header, "unhide-tcp") ; } msgln(unlog, 0, used_options) ; if (1 == use_ss) { strncpy(checker, "ss", 10); } else { strncpy(checker, "netstat", 10); } setpriority(PRIO_PROCESS,0,-20); /* reduce risk of race condition - may fail, dont care */ msgln(unlog, 0, "[*]Starting TCP checking") ; if (1 == use_quick) { print_hidden_ports(TCP); } else { print_hidden_TCP_ports_1_by_1(TCP) ; } if (hidden_found) { ret_code += 4; } msgln(unlog, 0, "[*]Starting UDP checking") ; if (1 == use_quick) { print_hidden_ports(UDP); } else { print_hidden_UDP_ports_1_by_1(UDP) ; } if (hidden_found) { ret_code += 8; } if (logtofile == 1) { close_log(unlog, "unhide-tcp") ; } return(ret_code); } unhide-20130526/unhide-output.h0000644000076400007640000000275012150411332015605 0ustar pgouinpgouin/* http://www.unhide-forensics.info */ /* 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 . */ #include /* * Globals */ /* * unhide-output */ // Print a message to a file stream (and log the message if necessary). extern void vfmsg(FILE * unlog, FILE* fp, const char* fmt, va_list ap) ; // Print a message to a stdout (and log the message if necessary), appending \n. extern void msgln(FILE * unlog, int indent, const char* fmt, ...) ; // Print a warning message to a stderr (and log the message if necessary), extern void warnln(int verbose, FILE * unlog, const char* fmt, ...) ; // Print an error to stderr and exit with code 1. extern void die(FILE * unlog, const char* fmt, ...) ; // Initialize and write a header to the log file. extern FILE *init_log(int logtofile, const char *header, const char *basename) ; // Write a footer and close a log file. extern void close_log(FILE *fh, const char *basename) ; unhide-20130526/unhide-output.c0000644000076400007640000001032412150411332015574 0ustar pgouinpgouin/* http://www.unhide-forensics.info */ /* 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 . */ #include #include #include #include #ifdef __linux__ #include #else #include #endif #include #include #include "unhide-output.h" /* * Print a message to a file stream (and log the message if necessary). */ void vfmsg(FILE * unlog, FILE* fp, const char* fmt, va_list ap) { char buf[BUFSIZ]; vsnprintf(buf, BUFSIZ, fmt, ap); fputs(buf, fp); if (NULL != unlog) fputs(buf, unlog); } /* * Print a message to a stdout (and log the message if necessary), appending \n. */ void msgln(FILE * unlog, int indent, const char* fmt, ...) { char buf[BUFSIZ]; va_list ap; if(1 == indent) { strncpy(buf, "\t", BUFSIZ); strncat(buf, fmt, BUFSIZ-strlen(buf)); } else { strncpy(buf, fmt, BUFSIZ); } strncat(buf, "\n", BUFSIZ-1-strlen(buf)); va_start(ap, fmt); vfmsg(unlog, stdout, buf, ap); va_end(ap); } /* * Print a warning message to a stderr (and log the message if necessary), * appending \n, only if in verbose mode. * * If errno is not 0, then information about the last error is printed too. */ void warnln(int verbose, FILE * unlog, const char* fmt, ...) { char buf[BUFSIZ]; va_list ap; int e = errno; /* save it in case some other function fails */ if (!verbose) { return; } strncpy(buf, "Warning : ", BUFSIZ); strncat(buf, fmt, BUFSIZ-1-strlen(buf)); if (e != 0) { strncat(buf, " [", BUFSIZ-1-strlen(buf)); strncat(buf, strerror(e), BUFSIZ-1-strlen(buf)); strncat(buf, "]", BUFSIZ-1-strlen(buf)); } strncat(buf, "\n", BUFSIZ-1-strlen(buf)); va_start(ap, fmt); vfmsg(unlog, stderr, buf, ap); va_end(ap); } /* * Print an error to stderr and exit with code 1. * * If errno is not 0, then information about the last error is printed too. */ void die(FILE * unlog, const char* fmt, ...) { va_list ap; char buf[BUFSIZ]; int e = errno; /* save it in case some other function fails */ strncpy(buf, "Error : ", BUFSIZ); strncat(buf, fmt, BUFSIZ-1-strlen(buf)); if (e != 0) { strncat(buf, " [", BUFSIZ-1-strlen(buf)); strncat(buf, strerror(e), BUFSIZ-1-strlen(buf)); strncat(buf, "]", BUFSIZ-1-strlen(buf)); } strncat(buf, "\n", BUFSIZ-1-strlen(buf)); va_start(ap, fmt); vfmsg(unlog, stderr, buf, ap); va_end(ap); exit(1); } /* * Initialize and write a header to the log file. */ FILE *init_log(int logtofile, const char *header, const char *basename) { FILE *fh ; char filename[PATH_MAX] ; time_t scantime; struct tm *tmPtr; char cad[80]; if (0 == logtofile) { return(NULL); } scantime = time(NULL); tmPtr = localtime(&scantime); sprintf(filename, "%s_%4d-%02d-%02d.%s", basename, tmPtr->tm_year+1900, tmPtr->tm_mon + 1, tmPtr->tm_mday, "log" ); fh = fopen(filename, "w"); if (NULL == fh) { logtofile = 0; // inhibit write to log file warnln(1, NULL, "Unable to open log file (%s)!", filename) ; return(NULL) ; } fputs(header, fh); strftime( cad, 80, "%H:%M:%S, %F", tmPtr ); fprintf(fh, "\n%s scan starting at: %s\n", basename, cad) ; return(fh); } /* Write a footer and close the log file. */ void close_log(FILE *fh, const char *basename) { if (NULL == fh) { return ; } time_t scantime; char cad[80]; struct tm *tmPtr; scantime = time(NULL); tmPtr = localtime(&scantime); strftime( cad, 80, "%H:%M:%S, %F", tmPtr ); fprintf(fh, "%s scan ending at: %s\n", basename, cad ); fclose(fh); } unhide-20130526/make_tarball.sh0000750000076400007640000000123512150411332015567 0ustar pgouinpgouin#! /bin/sh TAR_DATE=`date +%Y%m%d` echo $TAR_DATE TAR_FILE="unhide-$TAR_DATE" echo $TAR_FILE if [ -e "../$TAR_FILE" ]; then echo "../$TAR_FILE already exists, do you want to delete it and continue [yN] ?" read DEL_DIR if [ $DEL_DIR == "Y" -o $DEL_DIR == "y" ]; then if [ -d "../$TAR_FILE" ]; then echo "\rm -rf ../$TAR_FILE" else echo "\rm -f ../$TAR_FILE" fi else exit 1 fi else echo "../$TAR_FILE n'existe pas" fi mkdir -p ../$TAR_FILE/man/es ../$TAR_FILE/man/fr for FILE in `cat tar_list.txt`; do cp $FILE ../$TAR_FILE/$FILE done tar -czvf $TAR_FILE.tgz ../$TAR_FILE mv $TAR_FILE.tgz ../$TAR_FILE unhide-20130526/unhide-linux.h0000644000076400007640000000767412150411332015416 0ustar pgouinpgouin/* http://sourceforge.net/projects/unhide/ */ /* 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 . */ // External commands // ================= // we are looking only for real process not thread and only one by one #define COMMAND "ps --no-header -p %i o pid" // 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 #define PGID "ps --no-header -eL o pgid" // 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/thread // #define SYS_COMMAND "ps -eL o lwp" #define SYS_COMMAND "ps --no-header -eL o lwp" // an extra process/thread #define REVERSE "ps --no-header -eL o lwp,cmd" // Masks for the checks to do in checkps // ===================================== #define PS_PROC 0x00000001 #define PS_THREAD 0x00000002 #define PS_MORE 0x00000004 // Test numbers // ============ // note that checkps can't be call alone. enum test_num { // Individual test TST_NONE = 0, TST_VERSION, TST_PROC, TST_CHDIR, TST_OPENDIR, TST_READDIR, TST_GETPRIO, TST_GETPGID, TST_GETSID, TST_GETAFF, TST_GETPARM, TST_GETSCHED, TST_RR_INT, TST_KILL, TST_NOPROCPS, TST_BRUTE, TST_REVERSE, TST_QUICKONLY, TST_SYS_INFO, TST_SYS_INFO2, TST_SYS_INFO3, // meta test TST_DIR, TST_SYS, TST_QUICK, TST_PROCALL, // MAX number, should be the last of enum. MAX_TESTNUM }; // boolean values // ============== #define FALSE 0 #define TRUE 1 // Structure of the table of tests // =============================== struct tab_test_t { int todo; void (*func)(void); } ; // Default sysctl kernel.pid_max extern int maxpid ; // Threads id for sync extern int tid ; // our own PID extern pid_t mypid ; // options extern int verbose ; extern int morecheck ; extern int RTsys ; extern int brutesimplecheck ; // Found hidden proccess flag extern int found_HP ; // Temporary string for output extern char used_options[1000]; // For logging to file extern int logtofile; extern FILE *unlog; // Temporary string for output extern char scratch[1000]; extern struct tab_test_t tab_test[MAX_TESTNUM]; // prototypes // ========== // unhide-linux-bruteforce.c extern void *funcionThread (void *parametro) ; extern void brute(void) ; // unhide-linux.c extern void get_max_pid(int* newmaxpid) ; extern int checkps(int tmppid, int checks) ; extern void printbadpid (int tmppid) ; extern void usage(char * command) ; extern void parse_args(int argc, char **argv) ; // unhide-linux-procfs.c extern void checkproc(void) ; extern void checkchdir(void) ; extern void checkopendir(void) ; extern void checkreaddir(void) ; // unhide-linux-syscall.c extern void checkgetpriority(void) ; extern void checkgetpgid(void) ; extern void checkgetsid(void) ; extern void checksched_getaffinity(void) ; extern void checksched_getparam(void) ; extern void checksched_getscheduler(void) ; extern void checksched_rr_get_interval(void) ; extern void checkkill(void) ; extern void checkallnoprocps(void) ; extern void checksysinfo(void) ; extern void checksysinfo2(void) ; extern void checksysinfo3(void) ; extern void checksysinfo4(void) ; // unhide-linux-compound.c extern void checkallquick(void) ; extern void checkallreverse(void) ; unhide-20130526/NEWS0000644000076400007640000001363012150411332013320 0ustar pgouinpgouinChanges since 20121229 : ********************** BUG FIXES - include in unhide-output.h, some old gcc/glibc need it. SUPPORT FOR PORTING - On non Linux OS, ss is not used by default by unhide-tcp. This way, FreeBSD guys should be able to package without patching unhide source :) - On FreeBSD, use sockstat instead of fuser. MISCELLANOUS - The unhide files in the tarball are again contained in a directory (unhide-YYYYMMDD) - The name of the tarball uses again a '-' not a '_'. - Help packagers: in unhide-posix.c, unhide-output.c, unhide-tcp.c, OS specific command are put between #ifdef instead of beeing commented. - Correct banner of unhide-posix. - Update manpages. - Add build/use require list in readme files Changes since 20110113 : ********************** IMPORTANT - unhide-linux26.c was renamed to unhide-linux.c - unhide.c was renamed to unhide-posix.c - The log file of unhide-linux is renamed 'unhide-linux_AAAA-MM-DD.log' - The log file of unhide-tcp is named 'unhide-tcp_AAAA-MM-DD.log' - By default, unhide-tcp now use /sbin/ss from iproute2 package, to use netstat as before '-n' option must be given on command line. - Display is more verbose and multi-lines for hidden processes (unhide-linux). - If asked to (-l and/or -f), display is more verbose and multi-lines for hidden ports (unhide-tcp). - sysinfo test is no more called as part of compound quick and sys tests as it may give false positives. It could still be run using the checksysinfo, checksysinfo2 or checksysinfo3 command line parameter. NEW FEATURES - Major enhancement of unhide-tcp : * Add capability to output a log file (unhide-tcp_AAA-MM-DD.log) * Add capability to output more information (via lsof and/or fuser) on hidden port if available * Add verbose mode (disabled by default) to display warning * Add a new method (via option '-s') very fast on system with huge number of opened ports * Make a double check of port access to avoid false positive (previous single check version is available as unhide-tcp-simple-check.c if needed). - Add a quick port in C language of unhide.rb (unhide_rb.c) and guess what ... it's 40 times faster than original ruby unhide.rb unhide_rb doesn't take any option. - Add "-d" option for doing a double check in brute test, this reduce false positives. - Add "-o" option as synonym of "-f". - For found hidden processes, display the user and the working directory as extracted from the process environment. Note that it doesn't work well for kernel processes/threads nor for deamons. - For found hidden processes, display cmdline, exe link and internal command name. MISCELLANOUS - Add french and spanish man page for unhide-tcp - Update english manpage of unhide-tcp to reflect changes - Minor corrections in french manpage of unhide - Display copyright and license information in start banners. - Make message from sysinfo tests more clear. - Add a NEWS file :) - Update README.txt, LISEZ-MOI.txt and LEEME.txt to clarify difference between unhide-posix and unhide-linux. - Remove sysinfo test from quick and sys compound tests as it may give false positive. sysinfo test still can be used via the checksysinfo[2|3] command line parameters. BUG FIXES - Suppress pedantic compilation warnings (glibc >=2.3, gcc >=4.6). - Correct the number of processes displayed for /proc counting in sysinfo test. Changes since 20100819 : ********************** NEW FEATURES - Add spanish man page - Add additional check to checkopendir when -m is specified. - Add a option (-f) to create a log file. - Add checkopendir test (also called by procfs and procall compound test) - Also do opendir() test in reverse and quick tests. - Add alternate sysinfo test (via -r option or checksysinfo2 test name) - Make the output of hidden process on one line to facilitate parsing - Display wchan if there is no cmdline and no exe link (sleeping kernel threads) - Add -V version to show version and exit. - The -v option can now be given more than once on command line : management of several verbosity level. - Now several tests can be simultaneously entered on the command line. - Add all elementary tests to the command line test list - Add procall compound test command line args. - Check for our own spawn ps process in reverse test to avoid false positive. - Enhanced fake process detection in reverse test. BUG FIXES - Correct warning message in additional check of checkchdir. - Close log file only if it is open. - Correct the value returned by unhide - Add the misssing new lines in most of the warnings (thanks to gordy for the report). - Check the return of fgets in checkallreverse(), check of feof seems not to be very reliable for a pipe, we sometime got the last line 2 times (thanks to gordy for the report). - Correct an initialized fd use, that gcc don't report when -O2 isn't given on command line DEVELOPER ISSUES - Minor readability when generating program info for display - Factorize (f)printf to stdout & log. - Add a preliminary testsuite for unhide (sanity.sh) - Use printbadpid() in checkallnoprocps() as in other tests. - Also check it in checksysinfo & checksysinfo2 - Simplify and clarify test checksysinfo() - Redo args parsing : Manage multiple args on command line and several verbosity levels. - Add a tests table to allow new command line parsing. - Correct a copy/past "typo", in checkps - Minor optimizations of printf & sprintf calls. MISCELLANOUS - Add a NEWS file - Add GPL disclaimer to source files - Add french LISEZ-MOI.txt file - Add reference to new unhide site in version string - Add a warning about the generic version of unhide in README.txt (thanks to gordy for the report) - Modify man page to add the -V option, correct typos and clarify quick test. - Add -O2 option to compiling command line in README.txt - Add a TODO file unhide-20130526/tar_list.txt0000644000076400007640000000073112150411332015201 0ustar pgouinpgouinchangelog COPYING LEEME.txt LISEZ-MOI.TXT make_tarball.sh man/es/unhide.8 man/es/unhide-tcp.8 man/fr/unhide.8 man/fr/unhide-tcp.8 man/unhide.8 man/unhide-tcp.8 NEWS README.txt sanity.sh sanity-tcp.sh tar_list.txt TODO unhide-linux-bruteforce.c unhide-linux.c unhide-linux-compound.c unhide-linux.h unhide-linux-procfs.c unhide-linux-syscall.c unhide-output.c unhide-output.h unhide-posix.c unhide_rb.c unhide-tcp.c unhide-tcp-simple-check.c unhide-tcp-fast.c unhide-tcp.h unhide-20130526/sanity.sh0000755000076400007640000000476012150411332014473 0ustar pgouinpgouin#! /bin/sh # sanity.sh -- a growing testsuite for unhide. # # Copyright (C) 2010 Patrick Gouin. # # 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 . # # Original Author: Patrick Gouin # remove pre-existing local ps rm -f ./ps #test 0 # Call ps, but add a faked process. cat <./ps #! /bin/bash /bin/ps "\$@" echo 65535 my_false_proc EOF chmod 754 ./ps PATH=.:$PATH ./unhide-linux -v checksysinfo checksysinfo2 # remove pre-existing local ps rm -f ./ps # test2 # Don't call ps : let all processes appear hidden cat <./ps #! /bin/bash false EOF chmod 754 ./ps PATH=.:$PATH ./unhide-linux procall # remove pre-existing local ps rm -f ./ps # test 1 # Call ps, but hide the last line of output cat <