unhide-20130526/ 0000755 0000764 0000764 00000000000 12150411332 012616 5 ustar pgouin pgouin unhide-20130526/sanity-tcp.sh 0000755 0000764 0000764 00000004205 12150411332 015251 0 ustar pgouin pgouin #!/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.c 0000644 0000764 0000764 00000031145 12150411332 017370 0 ustar pgouin pgouin /*
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.c 0000644 0000764 0000764 00000035566 12150411332 015041 0 ustar pgouin pgouin /*
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.h 0000644 0000764 0000764 00000002750 12150411332 015605 0 ustar pgouin pgouin /*
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.c 0000644 0000764 0000764 00000010324 12150411332 015574 0 ustar pgouin pgouin /*
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.sh 0000750 0000764 0000764 00000001235 12150411332 015567 0 ustar pgouin pgouin #! /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.h 0000644 0000764 0000764 00000007674 12150411332 015416 0 ustar pgouin pgouin /*
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/NEWS 0000644 0000764 0000764 00000013630 12150411332 013320 0 ustar pgouin pgouin Changes 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.txt 0000644 0000764 0000764 00000000731 12150411332 015201 0 ustar pgouin pgouin changelog
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.sh 0000755 0000764 0000764 00000004760 12150411332 014473 0 ustar pgouin pgouin #! /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 <./ps
#! /bin/bash
/bin/ps "\$@" | head -n-1
EOF
chmod 754 ./ps
PATH=.:$PATH ./unhide-linux sys
# 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 3
# 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 reverse
unhide-20130526/unhide-linux-bruteforce.c 0000644 0000764 0000764 00000011420 12150411332 017527 0 ustar pgouin pgouin /*
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 .
*/
// Needed for unistd.h to declare getpgid() and others
#define _XOPEN_SOURCE 500
// Needed for sched.h to declare sched_getaffinity()
#define _GNU_SOURCE
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "unhide-output.h"
#include "unhide-linux.h"
/*
* Minimalist thread function for brute test.
* Set tid with the pid of the created thread.
*/
void *funcionThread (void *parametro)
{
tid = (pid_t) syscall (SYS_gettid);
return(&tid) ;
};
/*
* Brute force the pid space via vfork and
* pthread_create/pthread_join. All pid which
* can't be obtained are check against ps output
*/
void brute(void)
{
int i=0;
int allpids[maxpid] ;
int allpids2[maxpid] ;
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++)
{
allpids[x] = 0 ;
allpids2[x] = 0 ;
}
for(z=301; z < maxpid; z++)
{
allpids[z] = z ;
allpids2[z] = z ;
}
for (i=301; i < maxpid; i++)
{
int vpid;
int status;
errno= 0 ;
if ((vpid = vfork()) == 0)
{
_exit(0);
}
if (0 == errno)
{
allpids[vpid] = 0;
waitpid(vpid, &status, 0);
}
}
if(FALSE == brutesimplecheck) // Do the scan a second time
{
// printf("DOING double check ...\n") ;
for (i=301; i < maxpid; i++)
{
int vpid;
int status;
errno= 0 ;
if ((vpid = vfork()) == 0)
{
_exit(0);
}
if (0 == errno)
{
allpids2[vpid] = 0;
waitpid(vpid, &status, 0);
}
}
}
/* processes that quit at this point in time create false positives */
for(y=0; y < maxpid; y++)
{
if ((allpids[y] != 0) && ((TRUE == brutesimplecheck) || (allpids2[y] != 0)))
{
// printf("Check PID : %d\n", y);
if(!checkps(allpids[y],PS_PROC | PS_THREAD | PS_MORE) )
{
printbadpid(allpids[y]);
}
}
}
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++)
{
allpids[x] = 0 ;
allpids2[x] = 0 ;
}
for(z=301; z < maxpid; z++)
{
allpids[z] = z ;
allpids2[z] = z ;
}
for (i=301; i < maxpid ; i++)
{
void *status;
errno= 0 ;
pthread_t idHilo;
int error;
error = pthread_create (&idHilo, NULL, funcionThread, NULL);
if (error != 0)
{
die(unlog, "Error: Cannot create thread ! Exiting.");
}
error = pthread_join(idHilo, &status);
if (error != 0)
{
die(unlog, "Error : Cannot join thread ! Exiting.");
}
allpids[tid] = 0;
}
if(FALSE == brutesimplecheck) // Do the scan a second time
{
// printf("DOING double check ...\n") ;
for (i=301; i < maxpid ; i++) {
void *status;
errno= 0 ;
pthread_t idHilo;
int error;
error = pthread_create (&idHilo, NULL, funcionThread, NULL);
if (error != 0)
{
die(unlog, "Error: Cannot create thread ! Exiting.");
}
error = pthread_join(idHilo, &status);
if (error != 0)
{
die(unlog, "Error : Cannot join thread ! Exiting.");
}
allpids2[tid] = 0;
}
}
/* processes that quit at this point in time create false positives */
for(y=0; y < maxpid; y++)
{
if ((allpids[y] != 0) && ((TRUE == brutesimplecheck) || (allpids2[y] != 0)))
{
if(!checkps(allpids[y],PS_PROC | PS_THREAD | PS_MORE) )
{
printbadpid(allpids[y]);
}
}
}
}
unhide-20130526/LISEZ-MOI.TXT 0000644 0000764 0000764 00000015275 12150411332 014541 0 ustar pgouin pgouin **-Unhide-**
http://www.unhide-forensics.info
Unhide est un outil d'investigation dont le rôle est de détecter les processus et les
flux TCP/UDP cachés par les rootkits / LKM ou par d''autres techniques de masquage.
Le paquet comprend quatre utilitaires : unhide-posix, unhide-linux, unhide_rb et unhide-tcp.
// unhide (unhide-posix, unhide-linux)
// -----------------------------------
Détection de processus cachés. Il met en œuvre six techniques principales
1 - Comparaison de /proc avec la sortie de /bin/ps.
2 - Comparaison des informations recueillies par le parcours de l'arborescence du
système de fichiers procfs avec les informations issues de /bin/ps .
Cette technique n'est disponible qu'avec la version unhide-linux.
3 - Comparaison des informations collectées depuis des appels système avec les
informations issues de /bin/ps(syscall scanning).
4 - Scan complet de l'espace des ID de processus par force brute (PIDs bruteforcing).
Cette technique n'est disponible qu'avec la version unhide-linux.
5 - Comparaison de la sortie de /bin/ps avec /proc, le parcours de procfs et les
appels systèmes.
Recherche inverse afin vérifiez que tous les processus affichés par /bin/ps
existent réellement.
Cette technique n'est disponible qu'avec la version unhide-linux.
6 - Comparaison rapide des informations recueillies dans /proc, par le parcours
de procfs et par lesappels systèmes avec la sortie de /bin/ps.
cette technique est environ 20 fois plus rapide que les 3 premières réunies
mais peut éventuellement donner davantage de faux positifs.
Cette technique n'est disponible qu'avec la version unhide-linux.
// Unhide_rb
// ---------
C'est un portage en langage C de l'utilitaire unhide_rb.
Comme l'original, il est grossièrement équivalent à "unhide-linux quick reverse" :
- il effectue trois tests de moins (kill, opendir and chdir),
- il lance /bin/ps seulement un fois au démarrage et une fois pour la double vérification,
- ses tests sont moins précis (P.ex. : test de la valeur de retour au lieu de errno),
- les processus sont uniquement identifiés par le lien sur leur exécutable (unhide-linux utilise
aussi la copie de la ligne de commande et le nom des "processus noyau dormant"),
- il y a peu de protection contre les erreurs (échec de fopen ou popen par exemple),
- il ne sait pas générer un fichier journal.
Il est très rapide, environ 80 fois plus que "unhide-linux quick reverse"
// unhide-TCP
// ----------
Sert à identifier les ports TCP ou UDP qui sont en écoute mais qui ne sont pas
visibles par la commande /sbin/ss (ou /bin/netstat).
Deux techniques sont employées :
- Celle de la force brute (passage en revue de tous les ports TCP/UDP possibles)
et comparaison avec la sortie de SS/netstat.
- Test de tous les ports TCP/UDP non listés par netstat.
// Fichiers
// --------
unhide-linux.c -- Recherche des processus cachés, pour les systèmes Linux >= 2.6
unhide-linux.h -- Header pour unhide-linux
unhide-tcp.c -- Recherche des ports TCP/UDP cachés (ss ou netstat)
unhide-tcp-fast.c -- Recherche des ports TCP/UDP cachés (recherche rapide)
unhide-tcp.h -- Header pour unhide-tcp
unhide_rb.c -- Portage en C de unhide.rb (une version très allégée de unhide-linux en ruby)
unhide-posix.c -- Recherche des processus cachés, pour les systèmes Unix génériques (*BSD,
Solaris, Linux 2.2 / 2.4)
Il ne met en œuvre que les techniques 1 et 3. Besoin de plus de tests
Avertissement: Cette version est quelque peu obsolète, et peut générer
des faux positifs. Utilisez unhide-linux.c si c'est possible'.
unhide-output.c -- Routines de sortie utilisés par les autres modules de unhide
unhide-output.h -- Header de unhide-output
changelog -- liste des évolutions apportées à unhide
COPYING -- Fichier de Licence, GNU GPL V3
LEEME.txt -- Version espagnole de ce fichier
LISEZ-MOI.TXT -- Ce fichier
NEWS -- Notes de version
README.txt -- Version anglaise de ce fichier
sanity.sh -- Fichier de test de unhide-linux
TODO -- Liste des évolutions envisagées (des volontaires ?)
man/unhide.8 -- man page en anglais de unhide
man/unhide-tcp.8 -- man page en anglais de unhide-tcp
man/es/unhide.8 -- man page en espagnol de unhide
man/es/unhide-tcp.8 -- man page en espagnol de unhide-tcp
man/fr/unhide.8 -- man page en français de unhide
man/fr/unhide-tcp.8 -- man page en français de unhide-tcp
// Compilation
// -----------
Prérequis de build
glibc-devel
glibc-static-devel
Prérequis d'utilisation
- 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
Si vous utilisez un noyau Linux >= 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
ln -s unhide unhide-linux
Sinon (Linux < 2.6, *BSD, Solaris, etc.)
gcc --static unhide-posix.c -o unhide-posix
ln -s unhide unhide-posix
// Utilisation
// -----------
Vous DEVEZ être root pour utiliser unhide
Exemples:
# ./unhide-linux -vo quick reverse
# ./unhide-linux -vom procall sys
# ./unhide_rb
# ./unhide-tcp -flov
# ./unhide-tcp -flovs
// Licence
GPL V.3 (http://www.gnu.org/licenses/gpl-3.0.html)
// Remerciement
// ------------
A. Ramos (aramosf@unsec.net) pour certaines expressions rationnelles
unspawn (unspawn@rootshell.be) support CentOS
Martin Bowers (Martin.Bowers@freescale.com) soutien CentOS
Lorenzo Martinez (lorenzo@lorenzomartinez.homeip.net) pour ses idées d'amélioration et le betatesting
François Marier (francois@debian.org) Auteur des pages de manuel et le support Debian
Johan Walles (johan.walles@gmail.com) Identification et correction d'un bug très désagréable de concurrence critique (race condition)
Jan Iven (jan.iven@cern.ch) En raison de ses grandes améliorations, de nouveaux tests et de corrections de bugs
P. Gouin (patrick-g@users.sourceforge.net) En raison de son travail incroyable correction des bugs et d'amélioration des performances
François Boisson pour l'idée de la double vérification dans le test "brute".
Leandro Lucarella (leandro.lucarella@sociomantic.com) pour la méthode rapide de balayage et son travail de factorisation de unhide-tcp
Nikos Ntarmos (ntarmos@ceid.upatras.gr) pour son aide inestimable pour le portage de unhide-tcp sur FreeBSD.
unhide-20130526/unhide-linux-procfs.c 0000644 0000764 0000764 00000030607 12150411332 016673 0 ustar pgouin pgouin /*
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 .
*/
// Needed for unistd.h to declare getpgid() and others
#define _XOPEN_SOURCE 500
// Needed for sched.h to declare sched_getaffinity()
#define _GNU_SOURCE
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "unhide-output.h"
#include "unhide-linux.h"
/*
* Check all the PID stat() see in /proc.
*/
void checkproc(void)
{
int procpids ;
int statusprocbefore, statusprocafter;
struct stat buffer;
char directory[100] ;
msgln(unlog, 0, "[*]Searching for Hidden processes through /proc stat scanning\n") ;
sprintf(directory,"/proc/");
for ( procpids = 1; procpids <= maxpid; procpids = procpids +1 )
{
// avoid ourselves
if (procpids == mypid)
{
continue;
}
sprintf(&directory[6],"%d",procpids);
statusprocbefore = stat(directory, &buffer) ;
if (statusprocbefore != 0)
{
continue;
}
if(checkps(procpids,PS_PROC | PS_THREAD))
{
continue;
}
statusprocafter = stat(directory, &buffer) ;
if (statusprocafter != 0)
{
continue;
}
printbadpid(procpids);
}
}
/*
* Check all the pid that chdir() see in /proc.
*/
void checkchdir(void)
{
int procpids ;
int statusdir, backtodir;
char curdir[PATH_MAX], *pathpt ;
char directory[100] ;
// char scratch[PATH_MAX] ; // DEBUG
// int count = 0; //DEBUG
msgln(unlog, 0, "[*]Searching for Hidden processes through /proc chdir scanning\n") ;
// get the path where Unhide is ran from.
if (NULL == (pathpt = getcwd(curdir, PATH_MAX)))
{
warnln(verbose, unlog, "Can't get current directory, test aborted") ;
return;
}
sprintf(directory,"/proc/");
for ( procpids = 1; procpids <= maxpid; procpids = procpids +1 )
{
// avoid ourselves
if (procpids == mypid)
{
continue;
}
sprintf(&directory[6],"%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
// we are in the /proc/pid directory
fich_tmp=fopen("status", "r") ;
if (NULL == fich_tmp)
{
warnln(verbose, unlog, "can't open status file for process: %d", procpids) ;
continue ; // next process
}
while ((FALSE == found_tgid) && (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
errno = 0 ;
warnln(1, unlog, "Thread %d said it's in group %s but isn't listed in %s", procpids, tmp_pids, new_directory) ;
}
}
}
else
{
errno = 0 ;
warnln(1, unlog, "Can't find TGID in status file for process: %d", procpids) ;
}
}
// unlock the proc directory so it can disappear if it's a transitory process
if (-1 == (backtodir = chdir(curdir)))
{
warnln(verbose, unlog, "Can't go back to unhide directory, test aborted") ;
return;
}
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
if (-1 == (backtodir = chdir(curdir)))
{
warnln(verbose, unlog, "Can't go back to unhide directory, test aborted") ;
return;
}
// printf("Passages = %d\n", count); // DEBUG
}
/*
* Check all the pid that opendir() see in /proc.
*/
void checkopendir(void)
{
int procpids ;
DIR *statusdir;
// char curdir[PATH_MAX] ;
char directory[100] ;
// char scratch[PATH_MAX] ; // DEBUG
// int count = 0; //DEBUG
msgln(unlog, 0, "[*]Searching for Hidden processes through /proc opendir scanning\n") ;
sprintf(directory,"/proc/");
for ( procpids = 1; procpids <= maxpid; procpids = procpids +1 )
{
// avoid ourselves
if (procpids == mypid)
{
continue;
}
sprintf(&directory[6],"%d",procpids);
statusdir = opendir(directory) ;
// the directory doesn't exist continue with the next one
if (statusdir == NULL)
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] ;
DIR* statdir;
// printf("directory = '%s'\n", directory); // DEBUG
// getcwd(scratch, PATH_MAX); // DEBUG
// printf("CWD = '%s'\n", scratch); // DEBUG
snprintf(line, 128, "%s/status", directory);
// printf("STATUS_FILE : %s\n", line);
fich_tmp=fopen(line, "r") ;
if (NULL == fich_tmp)
{
msgln(unlog, 0, "Can't open status file for process: %d", procpids) ;
continue ; // next process
}
while ((FALSE == found_tgid) && (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
// errno = 0;
statdir = opendir(new_directory) ;
if (NULL == statdir)
{
// the thread is not listed in the master thread task directory
// printf("opendir failed : %s)\n", strerror(errno)) ;
errno = 0 ;
warnln(1, unlog, "Thread %d said it's in group %s but isn't listed in %s", procpids, tmp_pids, new_directory) ;
}
else
{
closedir(statdir);
}
}
}
else
{
errno = 0 ;
warnln(1, unlog, "Can't find TGID in status file for process: %d", procpids) ;
}
}
// unlock the proc directory so it can disappear if it's a transitory process
closedir(statusdir);
if(checkps(procpids, PS_PROC | PS_THREAD)) {
continue;
}
// Avoid false positive on short life process/thread
statusdir = opendir(directory) ;
if (statusdir == NULL) {
continue;
}
// unlock dir & free descriptor
closedir(statusdir);
printbadpid(procpids);
}
// printf("Passages = %d\n", count); // DEBUG
}
/*
* Check all the pid that readdir() see in all /proc/pid/task.
*/
void checkreaddir(void)
{
int procpids ;
DIR *procdir, *taskdir;
struct dirent *dir, *dirproc;
char task[100] ;
msgln(unlog, 0, "[*]Searching for Hidden thread through /proc/pid/task readdir scanning\n") ;
procdir = opendir("/proc");
if (NULL == procdir)
{
warnln(verbose, unlog, "Cannot open /proc directory ! Exiting test.") ;
return ;
}
sprintf(task, "/proc/") ;
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 ;
directory = dirproc->d_name;
if(!isdigit(*directory))
{
// not a process directory of /proc
continue;
}
// sprintf(currentproc, "%d", directory);
sprintf(&task[6], "%s/task", directory) ;
// printf("task : %s", task) ; // DEBUG
taskdir = opendir(task);
if (NULL == taskdir)
{
warnln(verbose, unlog, "Cannot open %s directory ! ! Skipping process %s.", task, directory) ;
continue ;
}
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))
{
errno = 0 ;
warnln(verbose, unlog, "Not a thread ID (%s) in %s.", 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) ;
}
unhide-20130526/unhide-tcp-fast.c 0000644 0000764 0000764 00000012360 12150411332 015757 0 ustar pgouin pgouin /*
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 "unhide-output.h"
#include "unhide-tcp.h"
/*
* These are simplified hash set to store the ports that:
*
* - are visible to netstat
* - are found to be hidden by us
* - we want to check
*
* A value of 0 means the port is NOT in the set, and a value != 0 means
* otherwise.
*/
static char netstat_ports[65536];
static char hidden_ports[65536];
static char check_ports[65536];
/* Fill netstat_ports with the ports netstat see as used for protocol proto. */
static void get_netstat_ports(enum Proto proto)
{
FILE *fp;
int port;
if (TCP == proto)
{
fp=popen (tcpcommand1, "r");
}
else
{
fp=popen (udpcommand1, "r");
}
if (fp == NULL)
{
die(unlog, "popen failed to open netstat to get the ports list");
}
memset(netstat_ports, 0, sizeof(netstat_ports));
errno = 0;
while (!feof(fp))
{
if (fscanf(fp, "%i\n", &port) == EOF && errno != 0)
{
die(unlog, "fscanf failed to parse int");
}
netstat_ports[port] = 1;
}
pclose(fp);
}
/*
* Check a list of ports against what netstat report as used ports.
*
* All ports that are not reported as used by netstat are opened, binded and
* put in listen state (for the TCP proto). If any of that operations fail with
* an EADDRINUSE, it's reported as a port hidden to netstat.
*/
static void check(enum Proto proto)
{
int i;
int protocol;
if (proto == TCP)
protocol = SOCK_STREAM;
else if (proto == UDP)
protocol = SOCK_DGRAM;
else
abort();
memset(hidden_ports, 0, sizeof(hidden_ports));
hidden_found = 0;
get_netstat_ports(proto);
for (i = 0; i < 65536; i++)
{
int fd;
int reuseaddr;
struct sockaddr_in addr;
/*
* skip if is not a port to check or is already visible to
* netstat
*/
if (!check_ports[i] || netstat_ports[i])
{
continue;
}
fd = socket(AF_INET, protocol, 0);
if (fd == -1)
{
die(unlog, "socket creation failed");
}
reuseaddr = 1;
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuseaddr,
sizeof(reuseaddr)) != 0)
{
die(unlog, "setsockopt can't set SO_REUSEADDR");
}
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY;
addr.sin_port = htons(i);
/*
* if we can't bind or listen because the address is used, the
* port is asumed to be used and added to the hidden_ports list
* because we only check for ports not visible by netstat.
* If we can bind them, we remove them from the check_ports
* list so we don't try to check them again if a new pass is
* performed in the future.
*/
errno = 0;
if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) != 0 ||
(proto == TCP && listen(fd, 1) != 0))
{
if (errno == EADDRINUSE)
{
hidden_ports[i] = 1;
hidden_found++;
}
else
{
warnln(verbose, unlog, "bind failed, maybe you are not root?");
check_ports[i] = 0;
}
}
else
{
check_ports[i] = 0;
}
close(fd);
}
}
/*
* Print ports not visible to netstat but that are being used.
*
* The check for hidden ports is retried to minimize false positives, see
* comments inside the function for details.
*/
void print_hidden_ports(enum Proto proto)
{
/* reset the list of ports to check (we start wanting to check all of
* them) and the list of hidden ports (none is hidden until we prove
* otherwise)
*/
memset(check_ports, 1, sizeof(check_ports));
memset(hidden_ports, 0, sizeof(hidden_ports));
/*
* Double-check to minimize false positives.
*
* For very short lived connections we have a race condition between
* getting the output from netstat and trying to open the port
* ourselves. To minize this problem we check again the ports reported
* as hidden. If in the next run of netstat those ports are not present
* anymore, is fairly safe to asume they were false positives.
*/
check(proto);
if (hidden_found)
{
memcpy(check_ports, hidden_ports, sizeof(hidden_ports));
check(proto);
}
if (hidden_found)
{
int i;
for (i = 0; i < 65536; i++)
{
if (hidden_ports[i])
{
print_port(proto, i);
}
}
}
}
unhide-20130526/unhide_rb.c 0000644 0000764 0000764 00000036163 12150411332 014732 0 ustar pgouin pgouin /*
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 .
*/
// Needed for unistd.h to declare getpgid() and others
#define _XOPEN_SOURCE 500
// Needed for sched.h to declare sched_getaffinity()
#define _GNU_SOURCE
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define UNHIDE_RB "ps axhHo lwp,cmd"
// boolean value
#define FALSE 0
#define TRUE 1
#define UNKNOWN -1
// sysctl kernel.pid_max
int maxpid = 32768;
// Temporary string for output
char scratch[1000] ;
char cmdcont[1000] ;
unsigned int proc_parent_pids[65536] ;
char *proc_tasks[65536];
char *ps_pids[65536];
char *messages_pids[65536];
char message[1000] ;
char description[1000] ;
int ps_count = 0 ;
const char *pid_detectors[] =
{
"ps", "/proc ", "/proc_tasks ", "/proc_parent", "getsid()", "getpgid()",
"getpriority()", "sched_getparam()",
"sched_getaffinity()", "sched_getscheduler()", "sched_rr_get_interval()"
} ;
enum n_detector
{
N_PS,
N_PROC,
N_PROC_TASK,
N_PROC_PARENT,
N_GETSID,
N_GET_PGID,
N_GETPRIORITY,
N_SCHED_GETPARAM,
N_SCHED_GETAFFINITY,
N_SCHED_GETSCHEDULER,
N_SCHED_RR_GET_INTERVAL
} ;
void setup(int phase)
{
// setup part of unhide.rb
// -----------------------
DIR *procdir, *taskdir;
struct dirent *dirtask, *dirproc;
char mypath[512] = "/proc/" ;
sprintf(mypath, "/proc/") ;
procdir = opendir(mypath);
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 ;
if (dirproc->d_type != DT_DIR)
{
// not a directory or not a process directory
continue ;
}
directory = dirproc->d_name;
if(!isdigit(*directory))
{
// not a process directory of /proc
continue;
}
else
{
FILE* fich_tmp ;
char line[128] ;
int tmp_pid ;
// proc_parent_pids
sprintf(&mypath[6],"%s/status",directory);
// printf("%s\n",mypath);
if(NULL != (fich_tmp=fopen(mypath, "r")))
{
while (!feof (fich_tmp))
{
if (NULL != fgets (line, 128, fich_tmp))
{
line[127] = 0;
if (0 == strncmp (line, "ppid:", 5))
{
tmp_pid = strtol(line+5, NULL, 10);
proc_parent_pids[tmp_pid] = tmp_pid ;
}
}
}
fclose(fich_tmp);
}
// proc_tasks
sprintf(&mypath[6],"%s/task/", directory) ;
taskdir = opendir(mypath);
while ((dirtask = readdir(taskdir)))
{
char *directory ;
directory = dirtask->d_name;
if(!isdigit(*directory))
{
// not a process directory of /proc
continue;
}
else
{
// FILE* fich_tmp ;
// char line[128] ;
// int tmp_pid ;
size_t length ;
char myexe[512] ;
sprintf(myexe,"%s%s/exe",mypath,directory);
// printf("%s\n",myexe);
length = readlink(myexe, cmdcont, 1000) ;
if (-1 != length)
{
if (2 == phase)
{
cmdcont[length] = 0; // terminate the string
proc_tasks[strtol(directory, NULL, 10)] = malloc(length+1) ;
strcpy(proc_tasks[strtol(directory, NULL, 10)],cmdcont) ;
// printf("proc_tasks[%d] = %s\n",strtol(directory, NULL, 10), proc_tasks[strtol(directory, NULL, 10)]) ;
}
else
{
proc_tasks[strtol(directory, NULL, 10)] = (char *)1L ;
}
}
}
}
closedir(taskdir) ;
}
mypath[6] = 0 ;
}
closedir(procdir);
// ps_pids
FILE *fich_tmp ;
char myline[1000] ;
int mypid ;
// char ch_pid[30] ;
fich_tmp=popen (UNHIDE_RB, "r") ;
while (!feof(fich_tmp))
{
if (NULL != fgets(myline, 1000, fich_tmp))
{
myline[999] = 0;
if (myline[0] != '\n')
{
mypid = strtol(myline, NULL,10) ;
// printf("line+5 = --%s--\n", myline+5); // DEBUG
if (NULL == strstr(myline+5,UNHIDE_RB))
{
ps_count++ ;
if (2 == phase)
{
char *my_end ;
/*
int my_len ;
my_len = strlen(myline);
my_end=myline ;
if (1 == mypid)
while (my_end < myline+my_len)
{
printf(" %2x",*my_end);
my_end++;
}
*/
my_end = myline + strlen(myline) -1 ;
// printf("line = --%s--\n", myline); // DEBUG
while (' ' == *my_end || '\n' == *my_end || '\r' == *my_end || '\t' == *my_end)
{
*my_end = 0 ;
my_end-- ;
}
// printf("line = --%s--\n", myline); // DEBUG
ps_pids[mypid] = malloc(strlen(myline+5)+1) ;
// printf("ps_pids[%d] = %p for %d bytes\n", mypid,ps_pids[mypid],strlen(myline+5)+1);
strcpy(ps_pids[mypid],myline+5) ;
// printf("pid = %d\t", mypid); // DEBUG
// printf("cmd = --%s--\n",ps_pids[mypid]);
}
else
{
ps_pids[mypid] = (char *)1L ;
}
}
}
}
}
if (fich_tmp != NULL)
pclose(fich_tmp);
// end of unhide_rb setup
}
int get_suspicious_pids(int pid_num)
{
int pid_min, pid_max, my_pid ;
int pid_exists[N_SCHED_RR_GET_INTERVAL+1];
char mypath[50] ;
struct stat buffer;
// FILE *cmdfile ;
char proc_exe[512] ;
int statuscmd, length;
struct sched_param param;
cpu_set_t mask;
struct timespec tp;
int found_p = FALSE ;
if (pid_num == -1)
{
char path[]= "/proc/sys/kernel/pid_max";
pid_t tmppid = 0;
FILE* fd= fopen(path,"r");
if(!fd)
{
snprintf(scratch, 1000, "[*] Error: cannot get current maximum PID: %s\n", strerror(errno));
fputs(scratch, stdout);
}
else if((fscanf(fd, "%d", &tmppid) != 1) || tmppid < 1)
{
snprintf(scratch, 1000, "[*] cannot get current maximum PID: Error parsing %s format\n", path);
fputs(scratch, stdout);
} else
{
maxpid = tmppid;
}
fclose(fd) ;
pid_min = 1 ;
pid_max = maxpid ;
}
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) ;
// ps
if (ps_pids[my_pid] != NULL)
pid_exists[N_PS] = TRUE ;
else
pid_exists[N_PS] = FALSE ;
// proc
sprintf(mypath,"/proc/%d",my_pid);
statuscmd = stat(mypath, &buffer) ;
if ((statuscmd == 0) && S_ISDIR(buffer.st_mode))
{
pid_exists[N_PROC] = TRUE ;
strcat(mypath,"/exe") ;
length = readlink(mypath, cmdcont, 1000) ;
if (-1 != length)
{
cmdcont[length] = 0; // terminate the string
// printf("cmdcont(proc_exe) = %s\n", cmdcont) ; //DEBUG
strcpy(proc_exe,cmdcont) ;
}
else
{
strcpy(proc_exe,"unknown exe") ;
}
}
else
{
pid_exists[N_PROC] = FALSE ;
}
// proc/#/task
if (proc_tasks[my_pid] != NULL)
pid_exists[N_PROC_TASK] = TRUE ;
else
pid_exists[N_PROC_TASK] = FALSE ;
// proc_parent
if (proc_parent_pids[my_pid] != 0)
pid_exists[N_PROC_PARENT] = TRUE ;
else
pid_exists[N_PROC_PARENT] = UNKNOWN ;
// getsid()
if (-1 != getsid(my_pid))
{
pid_exists[N_GETSID] = TRUE ;
}
else
{
pid_exists[N_GETSID] = FALSE ;
}
// getpgid()
if (-1 != getpgid(my_pid))
{
pid_exists[N_GET_PGID] = TRUE ;
}
else
{
pid_exists[N_GET_PGID] = FALSE ;
}
// getpriority()
if (-1 != getpriority(PRIO_PROCESS, my_pid))
{
pid_exists[N_GETPRIORITY] = TRUE ;
}
else
{
pid_exists[N_GETPRIORITY] = FALSE ;
}
// sched_getparam()
if (-1 != sched_getparam(my_pid, ¶m))
{
pid_exists[N_SCHED_GETPARAM] = TRUE ;
}
else
{
pid_exists[N_SCHED_GETPARAM] = FALSE ;
}
// sched_getaffinity()
if (-1 != sched_getaffinity(my_pid, sizeof(cpu_set_t), &mask))
{
pid_exists[N_SCHED_GETAFFINITY] = TRUE ;
}
else
{
pid_exists[N_SCHED_GETAFFINITY] = FALSE ;
}
// sched_getscheduler()
if (-1 != sched_getscheduler(my_pid))
{
pid_exists[N_SCHED_GETSCHEDULER] = TRUE ;
}
else
{
pid_exists[N_SCHED_GETSCHEDULER] = FALSE ;
}
// sched_rr_get_interval()
if (-1 != sched_rr_get_interval(my_pid, &tp))
{
pid_exists[N_SCHED_RR_GET_INTERVAL] = TRUE ;
}
else
{
pid_exists[N_SCHED_RR_GET_INTERVAL] = FALSE ;
}
int suspicious = FALSE ;
int existence_consensus = UNKNOWN ;
int index ;
for (index = 0; index <= N_SCHED_RR_GET_INTERVAL; index++)
{
if (UNKNOWN == existence_consensus)
{
existence_consensus = pid_exists[index] ;
}
if (UNKNOWN == pid_exists[index])
continue ;
if (FALSE == existence_consensus)
{
if (TRUE == pid_exists[index])
suspicious = TRUE ;
break ;
}
else
{
if (FALSE == pid_exists[index])
suspicious = TRUE ;
break ;
}
}
// if (1 == my_pid) suspicious = TRUE ; //DEBUG
if(TRUE == suspicious)
{
found_p = TRUE ;
sprintf(message, "Suspicious PID %5d:", my_pid) ;
int index ;
for (index = 0; index <= N_SCHED_RR_GET_INTERVAL; index++)
{
if (UNKNOWN == pid_exists[index])
continue;
description[0] = 0 ;
if (-1 != pid_num)
{
if(N_PS == index)
{
if (NULL != ps_pids[my_pid])
strcpy(description, ps_pids[my_pid]) ;
}
else if (N_PROC_TASK == index)
{
if (NULL != proc_tasks[my_pid])
{
// printf("proc_tasks[%d] = %s", my_pid, proc_tasks[my_pid]) ; // DEBBUG
strcpy(description, proc_tasks[my_pid]) ;
}
}
else if (N_PROC == index)
{
// printf("proc_exe = %s\n", proc_exe) ; // DEBBUG
strcpy(description, proc_exe) ;
}
}
sprintf(scratch, "\n %s %s%s", (pid_exists[index] ? "Seen by" : "Not seen by"), pid_detectors[index], description) ;
strcat(message, scratch) ;
}
// puts(message) ; //DEBUG
if (-1 == pid_num)
{
messages_pids[my_pid] = malloc(strlen(message)+1) ;
strcpy(messages_pids[my_pid], message) ;
}
}
}
return(found_p);
}
int main (int argc, char *argv[])
{
/*
time_t scantime1, scantime2;
char cad[80];
struct tm *tmPtr;
double duree ;
*/
int i ;
int found_something = FALSE ;
int phase1_ko = FALSE ;
strncpy(scratch,"Unhide_rb 20130526\n", 1000) ;
strncat(scratch, "Copyright © 2013 Yago Jesus & Patrick Gouin\n", 1000);
strncat(scratch, "License GPLv3+ : GNU GPL version 3 or later\n", 1000);
strncat(scratch, "http://www.unhide-forensics.info\n\n", 1000);
strncat(scratch, "NOTE : This version of unhide_rb is for systems using Linux >= 2.6 \n\n", 1000);
fputs(scratch, stdout);
// printf(header) ;
if(getuid() != 0){
printf("You must be root to run %s !\n", argv[0]) ;
}
/*
scantime1 = time(NULL);
tmPtr = localtime(&scantime1);
strftime( cad, 80, "%H:%M.%S, %F", tmPtr );
printf("Unhide_rb scan starting at: %s\n", cad );
*/
puts ("Scanning for hidden processes...") ;
// initializing memory pointers
for (i = 0 ; i < maxpid; i++)
{
ps_pids[i] = NULL ;
proc_tasks[i] = NULL ;
messages_pids[i] = NULL ;
}
setup(1);
struct sysinfo info;
sysinfo(&info);
if (ps_count != info.procs)
{
puts("ps and sysinfo() process count mismatch:\n") ;
printf(" ps: %d processes\n", ps_count) ;
printf(" sysinfo(): %d processes\n", info.procs) ;
}
phase1_ko = get_suspicious_pids(-1) ;
// re-initializing memory pointers (were used as boolean in setup() phase 1)
for (i = 0 ; i < maxpid; i++)
{
ps_pids[i] = NULL ;
proc_tasks[i] = NULL ;
}
if (TRUE == phase1_ko)
{
setup(2);
for (i=1; i