void test_ports(int proto, int *_errors, int *_total) { int i; for(i = 0; i<= 65535; i++) { (*_total)++; if(conn_port(proto, i)) { /* Checking if we can find it using netstat, if not, * check again to see if the port is still being used. */ if(run_netstat(proto, i)) { continue; #ifdef OSSECHIDS sleep(2); #endif } /* If we are being run by the ossec hids, sleep here (no rush) */ #ifdef OSSECHIDS sleep(2); #endif if(!run_netstat(proto, i) && conn_port(proto, i)) { char op_msg[OS_SIZE_1024 +1]; (*_errors)++; snprintf(op_msg, OS_SIZE_1024, "Port '%d'(%s) hidden. " "Kernel-level rootkit or trojaned " "version of netstat.", i, (proto == IPPROTO_UDP)? "udp" : "tcp"); notify_rk(ALERT_ROOTKIT_FOUND, op_msg); } } if((*_errors) > 20) { char op_msg[OS_SIZE_1024 +1]; snprintf(op_msg, OS_SIZE_1024, "Excessive number of '%s' ports " "hidden. It maybe a false-positive or " "something really bad is going on.", (proto == IPPROTO_UDP)? "udp" : "tcp" ); notify_rk(ALERT_SYSTEM_CRIT, op_msg); return; } } }
/* check_rc_ports: v0.1 * Check all ports */ void check_rc_ports() { int _errors = 0; int _total = 0; int i = 0; while(i<=65535) { total_ports_tcp[i] = 0; total_ports_udp[i] = 0; i++; } /* Trsting TCP ports */ test_ports(IPPROTO_TCP, &_errors, &_total); /* Testing UDP ports */ test_ports(IPPROTO_UDP, &_errors, &_total); if(_errors == 0) { char op_msg[OS_SIZE_1024 +1]; snprintf(op_msg,OS_SIZE_1024,"No kernel-level rootkit hiding any port." "\n Netstat is acting correctly." " Analyzed %d ports.", _total); notify_rk(ALERT_OK, op_msg); } return; }
int read_dev_file(char *file_name) { struct stat statbuf; if(lstat(file_name, &statbuf) < 0) { return(-1); } if(S_ISDIR(statbuf.st_mode)) { #ifdef DEBUG verbose("%s: Reading dir: %s\n",ARGV0, file_name); #endif return(read_dev_dir(file_name)); } else if(S_ISREG(statbuf.st_mode)) { char op_msg[OS_SIZE_1024 +1]; snprintf(op_msg, OS_SIZE_1024, "File '%s' present on /dev." " Possible hidden file.", file_name); notify_rk(ALERT_SYSTEM_CRIT, op_msg); _dev_errors++; } return(0); }
/* check_rc_sys: v0.1 * Scan the whole filesystem looking for possible issues */ void check_rc_pids() { int _total = 0; int _errors = 0; char ps[OS_SIZE_1024 +1]; char proc_0[] = "/proc"; char proc_1[] = "/proc/1"; pid_t max_pid = MAX_PID; noproc = 1; /* Checking where ps is */ memset(ps, '\0', OS_SIZE_1024 +1); strncpy(ps, "/bin/ps", OS_SIZE_1024); if(!is_file(ps)) { strncpy(ps, "/usr/bin/ps", OS_SIZE_1024); if(!is_file(ps)) ps[0] = '\0'; } /* Proc is mounted */ if(is_file(proc_0) && is_file(proc_1)) { noproc = 0; } loop_all_pids(ps, max_pid, &_errors, &_total); if(_errors == 0) { char op_msg[OS_SIZE_1024 +1]; snprintf(op_msg, OS_SIZE_1024, "No hidden process by Kernel-level " "rootkits.\n %s is not trojaned. " "Analyzed %d processes.", ps, _total); notify_rk(ALERT_OK, op_msg); } return; }
void check_open_ports() { #ifndef OSSECHIDS memset(open_ports_str, '\0', OS_SIZE_1024 + 1); open_ports_size = OS_SIZE_1024 - 1; _ports_open = 0; snprintf(open_ports_str, OS_SIZE_1024, "The following ports are open:"); open_ports_size -= strlen(open_ports_str) + 1; /* Testing All ports */ try_to_access_ports(); open_ports_str[strlen(open_ports_str) - 1] = '\0'; notify_rk(ALERT_OK, open_ports_str); #endif return; }
void check_rc_dev(const char *basedir) { char file_path[OS_SIZE_1024 + 1]; _dev_total = 0, _dev_errors = 0; debug1("%s: DEBUG: Starting on check_rc_dev", ARGV0); snprintf(file_path, OS_SIZE_1024, "%s/dev", basedir); read_dev_dir(file_path); if (_dev_errors == 0) { char op_msg[OS_SIZE_1024 + 1]; snprintf(op_msg, OS_SIZE_1024, "No problem detected on the /dev " "directory. Analyzed %d files", _dev_total); notify_rk(ALERT_OK, op_msg); } return; }
/* check_rc_if: v0.1 * Check all interfaces for promiscuous mode */ void check_rc_if() { int _fd, _errors = 0, _total = 0; struct ifreq tmp_str[16]; struct ifconf _if; struct ifreq *_ir; struct ifreq *_ifend; struct ifreq _ifr; _fd = socket(AF_INET, SOCK_DGRAM, 0); if(_fd < 0) { merror("%s: Error checking interfaces (socket)", ARGV0); return; } memset(tmp_str, 0, sizeof(struct ifreq)*16); _if.ifc_len = sizeof(tmp_str); _if.ifc_buf = (caddr_t)(tmp_str); if (ioctl(_fd, SIOCGIFCONF, &_if) < 0) { close(_fd); merror("%s: Error checking interfaces (ioctl)", ARGV0); return; } _ifend = (struct ifreq*) (void *) ((char*)tmp_str + _if.ifc_len); _ir = tmp_str; /* Looping on all interfaces */ for (; _ir < _ifend; _ir++) { strncpy(_ifr.ifr_name, _ir->ifr_name, sizeof(_ifr.ifr_name)); /* Getting information from each interface */ if (ioctl(_fd, SIOCGIFFLAGS, (char*)&_ifr) == -1) { continue; } _total++; if ((_ifr.ifr_flags & IFF_PROMISC) ) { char op_msg[OS_SIZE_1024 +1]; if(run_ifconfig(_ifr.ifr_name)) { snprintf(op_msg, OS_SIZE_1024,"Interface '%s' in promiscuous" " mode.", _ifr.ifr_name); notify_rk(ALERT_SYSTEM_CRIT, op_msg); } else { snprintf(op_msg, OS_SIZE_1024,"Interface '%s' in promiscuous" " mode, but ifconfig is not showing it" "(probably trojaned).", _ifr.ifr_name); notify_rk(ALERT_ROOTKIT_FOUND, op_msg); } _errors++; } } close(_fd); if(_errors == 0) { char op_msg[OS_SIZE_1024 +1]; snprintf(op_msg, OS_SIZE_1024, "No problem detected on ifconfig/ifs." " Analyzed %d interfaces.", _total); notify_rk(ALERT_OK, op_msg); } return; }
/* Check if file has NTFS ADS */ int os_check_ads(const char *full_path) { HANDLE file_h; WIN32_STREAM_ID sid; void *context = NULL; char stream_name[MAX_PATH + 1]; char final_name[MAX_PATH + 1]; DWORD dwRead, shs, dw1, dw2; /* Open file */ file_h = CreateFile(full_path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_POSIX_SEMANTICS, NULL); if (file_h == INVALID_HANDLE_VALUE) { return 0; } /* Zero memory */ ZeroMemory(&sid, sizeof(WIN32_STREAM_ID)); /* Get stream header size -- should be 20 bytes */ shs = (LPBYTE)&sid.cStreamName - (LPBYTE)&sid + sid.dwStreamNameSize; while (1) { if (BackupRead(file_h, (LPBYTE) &sid, shs, &dwRead, FALSE, FALSE, &context) == 0) { break; } if (dwRead == 0) { break; } stream_name[0] = '\0'; stream_name[MAX_PATH] = '\0'; if (BackupRead(file_h, (LPBYTE)stream_name, sid.dwStreamNameSize, &dwRead, FALSE, FALSE, &context)) { if (dwRead != 0) { DWORD i = 0; int max_path_size = 0; char *tmp_pt; char op_msg[OS_SIZE_1024 + 1]; snprintf(final_name, MAX_PATH, "%s", full_path); max_path_size = strlen(final_name); /* Copy from wide char to char */ while ((i < dwRead) && (max_path_size < MAX_PATH)) { if (stream_name[i] != 0) { final_name[max_path_size] = stream_name[i]; max_path_size++; final_name[max_path_size] = '\0'; } i++; } tmp_pt = strrchr(final_name, ':'); if (tmp_pt) { *tmp_pt = '\0'; } snprintf(op_msg, OS_SIZE_1024, "NTFS Alternate data stream " "found: '%s'. Possible hidden" " content.", final_name); notify_rk(ALERT_ROOTKIT_FOUND, op_msg); } } /* Get next */ if (!BackupSeek(file_h, sid.Size.LowPart, sid.Size.HighPart, &dw1, &dw2, &context)) { break; } } CloseHandle(file_h); return (0); }
/* check_rc_sys: v0.1 * Scan the whole filesystem looking for possible issues */ void check_rc_sys(char *basedir) { char file_path[OS_SIZE_1024 +1]; debug1("%s: DEBUG: Starting on check_rc_sys", ARGV0); _sys_errors = 0; _sys_total = 0; did = 0; /* device id */ snprintf(file_path, OS_SIZE_1024, "%s", basedir); /* Opening output files */ if(rootcheck.notify != QUEUE) { _wx = fopen("rootcheck-rw-rw-rw-.txt", "w"); _ww = fopen("rootcheck-rwxrwxrwx.txt", "w"); _suid=fopen("rootcheck-suid-files.txt", "w"); } else { _wx = NULL; _ww = NULL; _suid = NULL; } /* Scan the whole file system -- may be slow */ if(rootcheck.scanall) { #ifndef WIN32 snprintf(file_path, 3, "%s", "/"); #endif read_sys_dir(file_path, rootcheck.readall); } /* Scan only specific directories */ else { int _i = 0; #ifndef WIN32 char *(dirs_to_scan[]) = {"/bin", "/sbin", "/usr/bin", "/usr/sbin", "/dev", "/lib", "/etc", "/root", "/var/log", "/var/mail", "/var/lib", "/var/www", "/usr/lib", "/usr/include", "/tmp", "/boot", "/usr/local", "/var/tmp", "/sys", NULL}; #else char *(dirs_to_scan[]) = {"C:\\WINDOWS", "C:\\Program Files", NULL}; #endif for(_i = 0; _i <= 24; _i++) { if(dirs_to_scan[_i] == NULL) break; #ifndef WIN32 snprintf(file_path, OS_SIZE_1024, "%s%s", basedir, dirs_to_scan[_i]); read_sys_dir(file_path, rootcheck.readall); #else read_sys_dir(dirs_to_scan[_i], rootcheck.readall); #endif } } if(_sys_errors == 0) { char op_msg[OS_SIZE_1024 +1]; snprintf(op_msg, OS_SIZE_1024, "No problem found on the system." " Analyzed %d files.", _sys_total); notify_rk(ALERT_OK, op_msg); } else if(_wx && _ww && _suid) { char op_msg[OS_SIZE_1024 +1]; snprintf(op_msg, OS_SIZE_1024, "Check the following files for more " "information:\n%s%s%s", (ftell(_wx) == 0)?"": " rootcheck-rw-rw-rw-.txt (list of world writable files)\n", (ftell(_ww) == 0)?"": " rootcheck-rwxrwxrwx.txt (list of world writtable/executable files)\n", (ftell(_suid) == 0)?"": " rootcheck-suid-files.txt (list of suid files)"); notify_rk(ALERT_SYSTEM_ERROR, op_msg); } if(_wx) { if(ftell(_wx) == 0) unlink("rootcheck-rw-rw-rw-.txt"); fclose(_wx); } if(_ww) { if(ftell(_ww) == 0) unlink("rootcheck-rwxrwxrwx.txt"); fclose(_ww); } if(_suid) { if(ftell(_suid) == 0) unlink("rootcheck-suid-files.txt"); fclose(_suid); } return; }
int read_sys_file(char *file_name, int do_read) { struct stat statbuf; _sys_total++; #ifdef WIN32 /* Check for NTFS ADS on Windows */ os_check_ads(file_name); #endif if(lstat(file_name, &statbuf) < 0) { #ifndef WIN32 char op_msg[OS_SIZE_1024 +1]; snprintf(op_msg, OS_SIZE_1024, "Anomaly detected in file '%s'. " "Hidden from stats, but showing up on readdir. " "Possible kernel level rootkit.", file_name); notify_rk(ALERT_ROOTKIT_FOUND, op_msg); _sys_errors++; #endif return(-1); } /* If directory, read the directory */ else if(S_ISDIR(statbuf.st_mode)) { /* Making Darwin happy. for some reason, * when I read /dev/fd, it goes forever on * /dev/fd5, /dev/fd6, etc.. weird */ if(strstr(file_name, "/dev/fd") != NULL) return(0); /* Ignoring /proc directory (it has the size 0). */ if(statbuf.st_size == 0) return(0); return(read_sys_dir(file_name, do_read)); } /* Check if the size from stats is the same as when we * read the file */ if(S_ISREG(statbuf.st_mode) && do_read) { char buf[OS_SIZE_1024]; int fd; int nr; unsigned long int total = 0; fd = open(file_name, O_RDONLY, 0); /* It may not necessarily open */ if(fd >= 0) { while ((nr = read(fd, buf, sizeof(buf))) > 0) { total += nr; } close(fd); if(strcmp(file_name, "/dev/bus/usb/.usbfs/devices") == 0) { /* Ignore .usbfs/devices. */ } else if(total != statbuf.st_size) { struct stat statbuf2; if((lstat(file_name, &statbuf2) == 0) && (total != statbuf2.st_size) && (statbuf.st_size == statbuf2.st_size)) { char op_msg[OS_SIZE_1024 +1]; snprintf(op_msg, OS_SIZE_1024, "Anomaly detected in file " "'%s'. File size doesn't match what we found. " "Possible kernel level rootkit.", file_name); notify_rk(ALERT_ROOTKIT_FOUND, op_msg); _sys_errors++; } } } } /* If has OTHER write and exec permission, alert */ #ifndef WIN32 if(((statbuf.st_mode & S_IWOTH) == S_IWOTH) && (S_ISREG(statbuf.st_mode))) { if((statbuf.st_mode & S_IXUSR) == S_IXUSR) { if(_wx) fprintf(_wx, "%s\n",file_name); _sys_errors++; } else { if(_ww) fprintf(_ww, "%s\n", file_name); } if(statbuf.st_uid == 0) { char op_msg[OS_SIZE_1024 +1]; #ifdef OSSECHIDS snprintf(op_msg, OS_SIZE_1024, "File '%s' is owned by root " "and has written permissions to anyone.", file_name); #else snprintf(op_msg, OS_SIZE_1024, "File '%s' is: \n" " - owned by root,\n" " - has written permissions to anyone.", file_name); #endif notify_rk(ALERT_SYSTEM_CRIT, op_msg); } _sys_errors++; } else if((statbuf.st_mode & S_ISUID) == S_ISUID) { if(_suid) fprintf(_suid,"%s\n", file_name); } #endif return(0); }
/* read_dir v0.1 * */ int read_sys_dir(char *dir_name, int do_read) { int i = 0; unsigned int entry_count = 0; int did_changed = 0; DIR *dp; struct dirent *entry; struct stat statbuf; #ifndef WIN32 char *(dirs_to_doread[]) = { "/bin", "/sbin", "/usr/bin", "/usr/sbin", "/dev", "/etc", "/boot", NULL }; #endif if((dir_name == NULL)||(strlen(dir_name) > PATH_MAX)) { merror("%s: Invalid directory given.",ARGV0); return(-1); } /* Ignoring user-supplied list. */ if(rootcheck.ignore) { while(rootcheck.ignore[i]) { if(strcmp(dir_name, rootcheck.ignore[i]) == 0) { return(1); } i++; } i = 0; } /* Getting the number of nodes. The total number on opendir * must be the same */ if(lstat(dir_name, &statbuf) < 0) { return(-1); } /* Currently device id */ if(did != statbuf.st_dev) { if(did != 0) did_changed = 1; did = statbuf.st_dev; } if(!S_ISDIR(statbuf.st_mode)) { return(-1); } #ifndef WIN32 /* Check if the do_read is valid for this directory */ while(dirs_to_doread[i]) { if(strcmp(dir_name, dirs_to_doread[i]) == 0) { do_read = 1; break; } i++; } #else do_read = 0; #endif /* Opening the directory given */ dp = opendir(dir_name); if(!dp) { if((strcmp(dir_name, "") == 0)&& (dp = opendir("/"))) { /* ok */ } else { return(-1); } } /* Reading every entry in the directory */ while((entry = readdir(dp)) != NULL) { char f_name[PATH_MAX +2]; struct stat statbuf_local; /* Just ignore . and .. */ if((strcmp(entry->d_name,".") == 0) || (strcmp(entry->d_name,"..") == 0)) { entry_count++; continue; } /* Creating new file + path string */ if(strcmp(dir_name, "/") == 0) { snprintf(f_name, PATH_MAX +1, "/%s", entry->d_name); } else { snprintf(f_name, PATH_MAX +1, "%s/%s",dir_name, entry->d_name); } /* Checking if file is a directory */ if(lstat(f_name, &statbuf_local) == 0) { /* On all the systems, except darwin, the * link count is only increased on directories. */ #ifndef Darwin if(S_ISDIR(statbuf_local.st_mode)) #else if(S_ISDIR(statbuf_local.st_mode) || S_ISREG(statbuf_local.st_mode) || S_ISLNK(statbuf_local.st_mode)) #endif { entry_count++; } } /* Checking every file against the rootkit database */ for(i = 0; i<= rk_sys_count; i++) { if(!rk_sys_file[i]) break; if(strcmp(rk_sys_file[i], entry->d_name) == 0) { char op_msg[OS_SIZE_1024 +1]; _sys_errors++; snprintf(op_msg, OS_SIZE_1024, "Rootkit '%s' detected " "by the presence of file '%s/%s'.", rk_sys_name[i], dir_name, rk_sys_file[i]); notify_rk(ALERT_ROOTKIT_FOUND, op_msg); } } /* Ignoring /proc */ if((strcmp(f_name, "/proc") == 0) || (strcmp(f_name, "/sys") == 0)) continue; read_sys_file(f_name, do_read); } /* Entry count for directory different than the actual * link count from stats. */ if((entry_count != statbuf.st_nlink) && ((did_changed == 0) || ((entry_count + 1) != statbuf.st_nlink))) { #ifndef WIN32 struct stat statbuf2; char op_msg[OS_SIZE_1024 +1]; if((lstat(dir_name, &statbuf2) == 0) && (statbuf2.st_nlink != entry_count)) { snprintf(op_msg, OS_SIZE_1024, "Files hidden inside directory " "'%s'. Link count does not match number of files " "(%d,%d).", dir_name, entry_count, (int)statbuf.st_nlink); /* Solaris /boot is terrible :) */ #ifdef SOLARIS if(strncmp(dir_name, "/boot", strlen("/boot")) != 0) { notify_rk(ALERT_ROOTKIT_FOUND, op_msg); _sys_errors++; } #elif Darwin if(strncmp(dir_name, "/dev", strlen("/dev")) != 0) { notify_rk(ALERT_ROOTKIT_FOUND, op_msg); _sys_errors++; } #else notify_rk(ALERT_ROOTKIT_FOUND, op_msg); _sys_errors++; #endif } #endif } closedir(dp); return(0); }
/* check_rc_files: * Read the file pointer specified (rootkit_files) * and check if the configured file is there */ void check_rc_files(char *basedir, FILE *fp) { char buf[OS_SIZE_1024 +1]; char file_path[OS_SIZE_1024 +1]; char *file; char *name; char *link; int _errors = 0; int _total = 0; debug1("%s: DEBUG: Starting on check_rc_files", ARGV0); while(fgets(buf, OS_SIZE_1024, fp) != NULL) { char *nbuf; /* Removing end of line */ nbuf = strchr(buf, '\n'); if(nbuf) { *nbuf = '\0'; } /* Assigning buf to be used */ nbuf = buf; /* Excluding commented lines or blanked ones */ while(*nbuf != '\0') { if(*nbuf == ' ' || *nbuf == '\t') { nbuf++; continue; } else if(*nbuf == '#') goto newline; else break; } if(*nbuf == '\0') goto newline; /* File now may be valid */ file = nbuf; name = nbuf; /* Getting the file and the rootkit name */ while(*nbuf != '\0') { if(*nbuf == ' ' || *nbuf == '\t') { /* Setting the limit for the file */ *nbuf = '\0'; nbuf++; break; } else { nbuf++; } } if(*nbuf == '\0') goto newline; /* Some ugly code to remove spaces and \t */ while(*nbuf != '\0') { if(*nbuf == '!') { nbuf++; if(*nbuf == ' ' || *nbuf == '\t') { nbuf++; name = nbuf; break; } } else if(*nbuf == ' ' || *nbuf == '\t') { nbuf++; continue; } else { goto newline; } } /* Getting the link (if present) */ link = strchr(nbuf, ':'); if(link) { *link = '\0'; link++; if(*link == ':') { link++; } } /* Cleaning any space of \t at the end */ nbuf = strchr(nbuf, ' '); if(nbuf) { *nbuf = '\0'; } nbuf = strchr(nbuf, '\t'); if(nbuf) { *nbuf = '\0'; } _total++; /* Checking if it is a file to search everywhere */ if(*file == '*') { if(rk_sys_count >= MAX_RK_SYS) { merror(MAX_RK_MSG, ARGV0, MAX_RK_SYS); } else { /* Removing * / from the file */ file++; if(*file == '/') file++; /* Memory assignment */ rk_sys_file[rk_sys_count] = strdup(file); rk_sys_name[rk_sys_count] = strdup(name); if(!rk_sys_name[rk_sys_count] || !rk_sys_file[rk_sys_count] ) { merror(MEM_ERROR, ARGV0); if(rk_sys_file[rk_sys_count]) free(rk_sys_file[rk_sys_count]); if(rk_sys_name[rk_sys_count]) free(rk_sys_name[rk_sys_count]); rk_sys_file[rk_sys_count] = NULL; rk_sys_name[rk_sys_count] = NULL; } rk_sys_count++; /* Always assigning the last as NULL */ rk_sys_file[rk_sys_count] = NULL; rk_sys_name[rk_sys_count] = NULL; } continue; } snprintf(file_path, OS_SIZE_1024, "%s/%s",basedir, file); /* Checking if file exists */ if(is_file(file_path)) { char op_msg[OS_SIZE_1024 +1]; _errors = 1; snprintf(op_msg, OS_SIZE_1024, "Rootkit '%s' detected " "by the presence of file '%s'.",name, file_path); notify_rk(ALERT_ROOTKIT_FOUND, op_msg); } newline: continue; } if(_errors == 0) { char op_msg[OS_SIZE_1024 +1]; snprintf(op_msg,OS_SIZE_1024,"No presence of public rootkits detected." " Analyzed %d files.", _total); notify_rk(ALERT_OK, op_msg); } }
/* check_rc_trojans: * Read the file pointer specified (rootkit_trojans) * and check if the any trojan entry is on the configured files */ void check_rc_trojans(char *basedir, FILE *fp) { int i = 0, _errors = 0, _total = 0; char buf[OS_SIZE_1024 +1]; char file_path[OS_SIZE_1024 +1]; char *file; char *string_to_look; #ifndef WIN32 char *(all_paths[]) = {"bin","sbin","usr/bin","usr/sbin", NULL}; #else char *(all_paths[]) = {"C:\\Windows\\", "D:\\Windows\\", NULL}; #endif debug1("%s: DEBUG: Starting on check_rc_trojans", ARGV0); while(fgets(buf, OS_SIZE_1024, fp) != NULL) { char *nbuf; char *message = NULL; i = 0; /* Removing end of line */ nbuf = strchr(buf, '\n'); if(nbuf) { *nbuf = '\0'; } /* Normalizing line */ nbuf = normalize_string(buf); if(*nbuf == '\0' || *nbuf == '#') { continue; } /* File now may be valid */ file = nbuf; string_to_look = strchr(file, '!'); if(!string_to_look) { continue; } *string_to_look = '\0'; string_to_look++; message = strchr(string_to_look, '!'); if(!message) { continue; } *message = '\0'; message++; string_to_look = normalize_string(string_to_look); file = normalize_string(file); message = normalize_string(message); if(*file == '\0' || *string_to_look == '\0') { continue; } _total++; /* Trying with all possible paths */ while(all_paths[i] != NULL) { if(*file != '/') { snprintf(file_path, OS_SIZE_1024, "%s/%s/%s",basedir, all_paths[i], file); } else { strncpy(file_path, file, OS_SIZE_1024); file_path[OS_SIZE_1024 -1] = '\0'; } /* Checking if entry is found */ if(is_file(file_path) && os_string(file_path, string_to_look)) { char op_msg[OS_SIZE_1024 +1]; _errors = 1; snprintf(op_msg, OS_SIZE_1024, "Trojaned version of file " "'%s' detected. Signature used: '%s' (%s).", file_path, string_to_look, *message == '\0'? "Generic":message); notify_rk(ALERT_ROOTKIT_FOUND, op_msg); } if(*file == '/') { break; } i++; } continue; } if(_errors == 0) { char op_msg[OS_SIZE_1024 +1]; snprintf(op_msg,OS_SIZE_1024, "No binaries with any trojan detected. " "Analyzed %d files.", _total); notify_rk(ALERT_OK, op_msg); } }
/* run_rk_check: v0.1 * Execute the rootkit checks */ void run_rk_check() { time_t time1; time_t time2; FILE *fp; OSList *plist; #ifndef WIN32 /* Hard coding basedir */ size_t i; char basedir[] = "/"; /* Removing the last / from basedir */ i = strlen(basedir); if(i > 0) { if(basedir[i-1] == '/') { basedir[i-1] = '\0'; } } #else /* Basedir for Windows */ char basedir[] = "C:\\"; #endif /* Setting basedir */ if(rootcheck.basedir == NULL) { rootcheck.basedir = basedir; } time1 = time(0); /*** Initial message ***/ if(rootcheck.notify != QUEUE) { printf("\n"); printf("** Starting Rootcheck v0.9 by Daniel B. Cid **\n"); printf("** http://www.ossec.net/en/about.html#dev-team **\n"); printf("** http://www.ossec.net/rootcheck/ **\n\n"); printf("Be patient, it may take a few minutes to complete...\n"); printf("\n"); } /* Cleaning the global variables */ rk_sys_count = 0; rk_sys_file[rk_sys_count] = NULL; rk_sys_name[rk_sys_count] = NULL; /* Sending scan start message */ notify_rk(ALERT_POLICY_VIOLATION, "Starting rootcheck scan."); if(rootcheck.notify == QUEUE) { merror("%s: INFO: Starting rootcheck scan.", ARGV0); } /*** First check, look for rootkits ***/ /* Open rootkit_files and pass the pointer to check_rc_files */ if (rootcheck.checks.rc_files) { if(!rootcheck.rootkit_files) { #ifndef WIN32 merror("%s: No rootcheck_files file configured.", ARGV0); #endif } else { fp = fopen(rootcheck.rootkit_files, "r"); if(!fp) { merror("%s: No rootcheck_files file: '%s'",ARGV0, rootcheck.rootkit_files); } else { check_rc_files(rootcheck.basedir, fp); fclose(fp); } } } /*** Second check. look for trojan entries in common binaries ***/ if (rootcheck.checks.rc_trojans) { if(!rootcheck.rootkit_trojans) { #ifndef WIN32 merror("%s: No rootcheck_trojans file configured.", ARGV0); #endif } else { fp = fopen(rootcheck.rootkit_trojans, "r"); if(!fp) { merror("%s: No rootcheck_trojans file: '%s'",ARGV0, rootcheck.rootkit_trojans); } else { #ifndef HPUX check_rc_trojans(rootcheck.basedir, fp); #endif fclose(fp); } } } #ifdef WIN32 /*** Getting process list ***/ plist = os_get_process_list(); /*** Windows audit check ***/ if (rootcheck.checks.rc_winaudit) { if(!rootcheck.winaudit) { merror("%s: No winaudit file configured.", ARGV0); } else { fp = fopen(rootcheck.winaudit, "r"); if(!fp) { merror("%s: No winaudit file: '%s'",ARGV0, rootcheck.winaudit); } else { check_rc_winaudit(fp, plist); fclose(fp); } } } /* Windows malware */ if (rootcheck.checks.rc_winmalware) { if(!rootcheck.winmalware) { merror("%s: No winmalware file configured.", ARGV0); } else { fp = fopen(rootcheck.winmalware, "r"); if(!fp) { merror("%s: No winmalware file: '%s'",ARGV0, rootcheck.winmalware); } else { check_rc_winmalware(fp, plist); fclose(fp); } } } /* Windows Apps */ if (rootcheck.checks.rc_winapps) { if(!rootcheck.winapps) { merror("%s: No winapps file configured.", ARGV0); } else { fp = fopen(rootcheck.winapps, "r"); if(!fp) { merror("%s: No winapps file: '%s'",ARGV0, rootcheck.winapps); } else { check_rc_winapps(fp, plist); fclose(fp); } } } /* Freeing process list */ del_plist((void *)plist); /** Checks for other non Windows. **/ #else /*** Unix audit check ***/ if (rootcheck.checks.rc_unixaudit) { if(rootcheck.unixaudit) { /* Getting process list. */ plist = os_get_process_list(); i = 0; while(rootcheck.unixaudit[i]) { fp = fopen(rootcheck.unixaudit[i], "r"); if(!fp) { merror("%s: No unixaudit file: '%s'",ARGV0, rootcheck.unixaudit[i]); } else { /* Running unix audit. */ check_rc_unixaudit(fp, plist); fclose(fp); } i++; } /* Freeing list */ del_plist(plist); } } #endif /*** Third check, looking for files on the /dev ***/ if (rootcheck.checks.rc_dev) { debug1("%s: DEBUG: Going into check_rc_dev", ARGV0); check_rc_dev(rootcheck.basedir); } /*** Fourth check, scan the whole system looking for additional issues */ if (rootcheck.checks.rc_sys) { debug1("%s: DEBUG: Going into check_rc_sys", ARGV0); check_rc_sys(rootcheck.basedir); } /*** Process checking ***/ if (rootcheck.checks.rc_pids) { debug1("%s: DEBUG: Going into check_rc_pids", ARGV0); check_rc_pids(); } /*** Check all the ports ***/ if (rootcheck.checks.rc_ports) { debug1("%s: DEBUG: Going into check_rc_ports", ARGV0); check_rc_ports(); /*** Check open ports ***/ debug1("%s: DEBUG: Going into check_open_ports", ARGV0); check_open_ports(); } /*** Check interfaces ***/ if (rootcheck.checks.rc_if) { debug1("%s: DEBUG: Going into check_rc_if", ARGV0); check_rc_if(); } debug1("%s: DEBUG: Completed with all checks.", ARGV0); /* Cleaning the global memory */ { int li; for(li = 0; li <= rk_sys_count; li++) { if(!rk_sys_file[li] || !rk_sys_name[li]) break; free(rk_sys_file[li]); free(rk_sys_name[li]); } } /*** Final message ***/ time2 = time(0); if(rootcheck.notify != QUEUE) { printf("\n"); printf("- Scan completed in %d seconds.\n\n", (int)(time2 - time1)); } else { sleep(5); } /* Sending scan ending message */ notify_rk(ALERT_POLICY_VIOLATION, "Ending rootcheck scan."); if(rootcheck.notify == QUEUE) { merror("%s: INFO: Ending rootcheck scan.", ARGV0); } debug1("%s: DEBUG: Leaving run_rk_check",ARGV0); return; }
/** void loop_all_pids(char *ps, pid_t max_pid, int *_errors, int *_total) * Check all the available PIDs for hidden stuff. */ void loop_all_pids(char *ps, pid_t max_pid, int *_errors, int *_total) { int _kill0 = 0; int _kill1 = 0; int _gsid0 = 0; int _gsid1 = 0; int _gpid0 = 0; int _gpid1 = 0; int _ps0 = -1; int _proc_stat = 0; int _proc_read = 0; int _proc_chdir = 0; pid_t i = 1; pid_t my_pid; char command[OS_SIZE_1024 +1]; my_pid = getpid(); for(;;i++) { if((i <= 0)||(i > max_pid)) break; (*_total)++; _kill0 = 0; _kill1 = 0; _gsid0 = 0; _gsid1 = 0; _gpid0 = 0; _gpid1 = 0; _ps0 = -1; _proc_stat = 0; _proc_read = 0; _proc_chdir = 0; /* kill test */ if(!((kill(i, 0) == -1)&&(errno == ESRCH))) { _kill0 = 1; } /* getsid to test */ if(!((getsid(i) == -1)&&(errno == ESRCH))) { _gsid0 = 1; } /* getpgid test */ if(!((getpgid(i) == -1)&&(errno == ESRCH))) { _gpid0 = 1; } /* proc stat */ _proc_stat = proc_stat(i); /* proc readdir */ _proc_read = proc_read(i); /* proc chdir */ _proc_chdir = proc_chdir(i); /* IF PID does not exist, keep going */ if(!_kill0 && !_gsid0 && !_gpid0 && !_proc_stat && !_proc_read && !_proc_chdir) { continue; } /* We do not need to look at our own pid */ else if(i == my_pid) { continue; } /* Checking the number of errors */ if((*_errors) > 15) { char op_msg[OS_SIZE_1024 +1]; snprintf(op_msg,OS_SIZE_1024,"Excessive number of hidden processes" ". It maybe a false-positive or " "something really bad is going on."); notify_rk(ALERT_SYSTEM_CRIT, op_msg); return; } /* checking if process appears on ps */ if(*ps) { snprintf(command, OS_SIZE_1024, "%s -p %d > /dev/null 2>&1", ps, (int)i); /* Found PID on ps */ _ps0 = 0; if(system(command) == 0) _ps0 = 1; } /* If we are being run by the ossec hids, sleep here (no rush) */ #ifdef OSSECHIDS sleep(2); #endif /* Everyone returned ok */ if(_ps0 && _kill0 && _gsid0 && _gpid0 && _proc_stat && _proc_read) { continue; } /* If our kill or getsid system call, got the * PID , but ps didn't, we need to find if it was a problem * with a PID being deleted (not used anymore) */ { if(!((getsid(i) == -1)&&(errno == ESRCH))) { _gsid1 = 1; } if(!((kill(i, 0) == -1)&&(errno == ESRCH))) { _kill1 = 1; } if(!((getpgid(i) == -1)&&(errno == ESRCH))) { _gpid1 = 1; } _proc_stat = proc_stat(i); _proc_read = proc_read(i); _proc_chdir = proc_chdir(i); /* If it matches, process was terminated */ if(!_gsid1 &&!_kill1 &&!_gpid1 &&!_proc_stat && !_proc_read &&!_proc_chdir) { continue; } } #ifdef AIX /* Ignoring AIX wait and sched programs. */ if((_gsid0 == _gsid1) && (_kill0 == _kill1) && (_gpid0 == _gpid1) && (_ps0 == 1) && (_gsid0 == 1) && (_kill0 == 0)) { /* The wait and sched programs do not respond to kill 0. * So, if everything else finds it, including ps, getpid, getsid, * but not * kill, we can safely ignore on AIX. * A malicious program would specially try to hide from ps.. */ continue; } #endif if((_gsid0 == _gsid1)&& (_kill0 == _kill1)&& (_gsid0 != _kill0)) { /* If kill found, but getsid and getpgid didnt', it may * be a defunct process -- ignore. */ if(!((_kill0 == 1)&&(_gsid0 == 0)&&(_gpid0 == 0)&&(_gsid1 == 0))) { char op_msg[OS_SIZE_1024 +1]; snprintf(op_msg, OS_SIZE_1024, "Process '%d' hidden from " "kill (%d) or getsid (%d). Possible kernel-level" " rootkit.", (int)i, _kill0, _gsid0); notify_rk(ALERT_ROOTKIT_FOUND, op_msg); (*_errors)++; } } else if((_kill1 != _gsid1)|| (_gpid1 != _kill1)|| (_gpid1 != _gsid1)) { /* See defunct process comment above. */ if(!((_kill1 == 1)&&(_gsid1 == 0)&&(_gpid0 == 0)&&(_gsid1 == 0))) { char op_msg[OS_SIZE_1024 +1]; snprintf(op_msg, OS_SIZE_1024, "Process '%d' hidden from " "kill (%d), getsid (%d) or getpgid. Possible " "kernel-level rootkit.", (int)i, _kill1, _gsid1); notify_rk(ALERT_ROOTKIT_FOUND, op_msg); (*_errors)++; } } else if((_proc_read != _proc_stat)|| (_proc_read != _proc_chdir)|| (_proc_stat != _kill1)) { /* checking if the pid is a thread (not showing on proc */ if(!noproc && !check_rc_readproc((int)i)) { char op_msg[OS_SIZE_1024 +1]; snprintf(op_msg, OS_SIZE_1024, "Process '%d' hidden from " "/proc. Possible kernel level rootkit.", (int)i); notify_rk(ALERT_ROOTKIT_FOUND, op_msg); (*_errors)++; } } else if(_gsid1 && _kill1 && !_ps0) { /* checking if the pid is a thread (not showing on ps */ if(!check_rc_readproc((int)i)) { char op_msg[OS_SIZE_1024 +1]; snprintf(op_msg, OS_SIZE_1024, "Process '%d' hidden from " "ps. Possible trojaned version installed.", (int)i); notify_rk(ALERT_ROOTKIT_FOUND, op_msg); (*_errors)++; } } } }