Fragile server
I believe many coder s like to rent some virtual machines to build sites, store resources or practice, and most people should choose to run naked after renting them.
I happen to have a virtual machine in my hand. I log in occasionally for recreation. One day, by coincidence, the login system checked the login authentication log file / var/log/secure, and something frightening happened:
I found a lot of "authentication failure... Rhost =..." in the file. It turns out that our virtual machine has been in danger without knowing it. Every moment, some illegal people are scanning and blasting our host. Thank you for the extremely complex login password inspired that day, To avoid the fate of becoming a broiler.
The security services of the cloud platform side need to be charged. For those who are often poor, I can only manually roll up the code for defense. Although it is simple, it is better than nothing. Here, local tyrants can consciously go out... Show their muscles and mount various services!
Some ideas of SSH attack defense
1. First, monitor the / var/log/secure file. From the secure file, we can find the history of authorization authentication failure, so as to obtain the IP address information of the remote attacker. Here, inotify can be used to detect the modification of the file.
2. Then, we need to build a hash linked list and a string hash function. The hash function can choose the commonly used time33 algorithm. When the attacker's IP address information is analyzed, the corresponding IP information is stored in the hash linked list and counted.
3. Create a task thread to clean up the hash linked list. On the one hand, it can clean up the cache nodes that have timed out. On the other hand, it can find out the illegal persons whose attack times in a specific cycle reach the threshold.
4. After finding the illegal person, we can build the alarm information and insert it into the syslog log file for reference (/ var/log/message).
5. You can use the iptables instruction to block ip addresses. Of course, you can also send a friendly reminder email with the help of the mail command. In the following code, there is no attempt to send mail. Interested friends can join and try.
SSH attack defense source code
The following is the smelly and long c code, which is a little rough.
#include <stdio.h> #include <string.h> #include <stdint.h> #include <stdlib.h> #include <stdarg.h> #include <pthread.h> #include <unistd.h> #include <errno.h> #include <regex.h> #include <syslog.h> #include <sys/inotify.h> #include <sys/time.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/select.h> #define SSH_BUF_SIZE 1024 #define SSH_MONITOR_FILE "/var/log/secure" #define SSH_MONITOR_HASH_SIZE (1 << 14) #define SSH_MONITOR_HASH_MASK (SSH_MONITOR_HASH_SIZE - 1) typedef struct ssh_monitor_s { int period, frequency, utime, stopflag; pthread_t tid; void (* init) (void); void (* start) (void); void (* destroy) (void); int (* hash) (const char *); void (* push) (const char *ipaddr); void (* log) (const char *msg, ...); }ssh_monitor; extern struct ssh_monitor_s monitor; typedef struct ssh_monitor_node_s { char rip[16]; uint32_t autherr_ftime; uint32_t error_count; struct ssh_monitor_node_s *next; } ssh_monitor_node; static uint32_t g_sshfile_offset; static regex_t g_ssh_features; //illegal ip address hash table static ssh_monitor_node * h_ssh_table[SSH_MONITOR_HASH_SIZE]; static pthread_rwlock_t h_ssh_lock[SSH_MONITOR_HASH_SIZE]; //timer33 hash algorithm static int ssh_monitor_iphash(const char *ipaddr) { int i, hash; hash = 0; for (i = 0; i < strlen(ipaddr); i++) hash = hash * 33 + ipaddr[i]; return (hash & SSH_MONITOR_HASH_MASK); } //Insert remote illegal ip address into hash linked list static void ssh_monitor_ippush(const char *ipaddr) { int h; time_t now; ssh_monitor_node *item, *node, *prev; now = time(NULL); h = monitor.hash(ipaddr); pthread_rwlock_wrlock(&h_ssh_lock[h]); item = h_ssh_table[h]; if (!item) { node = (ssh_monitor_node *)malloc(sizeof(ssh_monitor_node)); memset(node, 0x0, sizeof(ssh_monitor_node)); strncpy(node->rip, ipaddr, sizeof(node->rip)); node->autherr_ftime = now; node->error_count = 1; h_ssh_table[h] = node; } else { do { if ( !strncmp(item->rip, ipaddr, strlen(ipaddr)) ) break; prev = item; item = item->next; } while (item); if (!item) { node = (ssh_monitor_node *)malloc(sizeof(ssh_monitor_node)); memset(node, 0x0, sizeof(ssh_monitor_node)); strncpy(node->rip, ipaddr, sizeof(node->rip)); node->autherr_ftime = now; node->error_count = 1; prev->next = node; } else item->error_count++; } pthread_rwlock_unlock(&h_ssh_lock[h]); } static void ssh_monitor_destroy(void) { regfree(&g_ssh_features); } static void ssh_init_fileoffset(void) { struct stat _stat; if ( !lstat(SSH_MONITOR_FILE, &_stat) ) g_sshfile_offset = _stat.st_size; } //Failed to match authorization authentication information static void ssh_get_secure_message(void) { int rz, soff, eoff; FILE *fp = NULL; char buffer[SSH_BUF_SIZE]; char ipaddr[16]; regmatch_t pmatch[8]; fp = fopen(SSH_MONITOR_FILE, "r"); if (fp) { fseek(fp, g_sshfile_offset, SEEK_SET); while ( fgets(buffer, sizeof(buffer), fp) ) { rz = regexec(&g_ssh_features, buffer, 8, pmatch, 0); soff = pmatch[1].rm_so; eoff = pmatch[1].rm_eo; if ( !rz && (-1 != soff) && (-1 != eoff) ) { strncpy(ipaddr, buffer + soff, eoff - soff); //The attacker's ip address was found monitor.push(ipaddr); } g_sshfile_offset += strlen(buffer); memset(buffer, 0x0, SSH_BUF_SIZE); memset(ipaddr, 0x0, sizeof(ipaddr)); } fclose(fp); } } //ssh attack information monitoring static void ssh_monitor_start(void) { int wfd, iwfd, rz, offset, nfds; char buffer[SSH_BUF_SIZE]; struct inotify_event *ev = NULL; wfd = inotify_init(); iwfd = inotify_add_watch(wfd, SSH_MONITOR_FILE, IN_MODIFY|IN_CREATE|IN_DELETE_SELF); nfds = wfd + 1; ssh_init_fileoffset(); while (!monitor.stopflag) { fd_set rfds; struct timeval tv; int retval; FD_ZERO(&rfds); FD_SET(wfd, &rfds); tv.tv_sec = 5; tv.tv_usec = 0; retval = select(nfds, &rfds, NULL, NULL, &tv); if ( retval <= 0 ) { usleep(100000); continue; } offset = 0; while ( (rz = read(wfd, &buffer, sizeof(buffer))) < 0 ) { if ( EINTR == errno ) usleep(100000); break; } while (rz > 0 && offset < rz) { ev = (struct inotify_event *)(buffer + offset); //When secure is modified, the authorization authentication error information is matched and extracted if (IN_MODIFY & ev->mask) ssh_get_secure_message(); offset += sizeof(struct inotify_event); offset += ev->len; } memset(buffer, 0x0, SSH_BUF_SIZE); } } //Cache processing thread static void * ssh_monitor_expire_handler(void *arg) { int i; time_t now; int utime = monitor.utime; ssh_monitor_node *item, *node, *prev; char cmd[SSH_BUF_SIZE]; while (!monitor.stopflag) { now = time(NULL); for (i = 0; i < SSH_MONITOR_HASH_SIZE; i++) { pthread_rwlock_wrlock(&h_ssh_lock[i]); item = h_ssh_table[i]; do { prev = item; if (item) { if (now - item->autherr_ftime >= monitor.period) { //node expire prev->next = item->next; free(item); if (h_ssh_table[i] == item) h_ssh_table[i] = NULL; break; } else if (item->error_count >= monitor.frequency) { //Disable attackers using iptables memset(cmd, 0x0, sizeof(cmd)); sprintf(cmd, "iptables -I INPUT -s %s -j DROP", item->rip); system(cmd); //Write syslog log file monitor.log("Find Attacker From Remote IP:%s", item->rip); //deny, delete node prev->next = item->next; free(item); if (h_ssh_table[i] == item) h_ssh_table[i] = NULL; break; } item = item->next; } } while (item); pthread_rwlock_unlock(&h_ssh_lock[i]); } sleep(utime); } return NULL; } //Initialization file static void ssh_monitor_init() { int i; pthread_t tid; g_sshfile_offset = 0; //Regular matching string regcomp(&g_ssh_features, "authentication failure.*?rhost=([0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+)", REG_EXTENDED); for ( i = 0; i < SSH_MONITOR_HASH_SIZE; i++ ) { h_ssh_table[i] = NULL; pthread_rwlock_init( &h_ssh_lock[i], NULL ); } //Create cache processing task thread pthread_create(&tid, NULL, ssh_monitor_expire_handler, NULL); monitor.tid = tid; } //Write syslog file static void ssh_monitor_syslog(const char *msg, ...) { va_list valist; char buffer[SSH_BUF_SIZE] = {0}; va_start(valist, msg); vsprintf(buffer, msg, valist); va_end(valist); openlog(NULL, LOG_PID, LOG_SYSLOG); syslog(LOG_PID|LOG_SYSLOG, buffer); closelog(); } ssh_monitor monitor = { 1800, 10, 30, 0, .tid = 0, .init = ssh_monitor_init, .start = ssh_monitor_start, .destroy = ssh_monitor_destroy, .hash = ssh_monitor_iphash, .push = ssh_monitor_ippush, .log = ssh_monitor_syslog, }; void usage(void) { printf("ssheye [optinos]\n\ \t-d: run in backend\n\ \t-h: ask for help\n\ \t-p: period number, the unit is second, default 1800(half an hour)\n\ \t-f: frequency number,default value 10\n\ \t-u: update second,default value 30\n"); } int main(int argc, char **argv) { int opt, backend = 0, period = 1800, frequency = 10, utime = 30; while ( -1 != (opt = getopt(argc, argv, "dhp:f:u:")) ) { switch (opt) { case 'h': usage(); _exit(0); case 'd': backend = 1; break; case 'p': period = strtoul(optarg, NULL, 10); break; case 'f': frequency = strtoul(optarg, NULL, 10); break; case 'u': utime = strtoul(optarg, NULL, 10); break; deafult: break; } } //run in backend if ( backend ) daemon(0, 0); if ( period < 0 || frequency < 0 || utime < 0) { fprintf(stderr, "argument error\n"); _exit(-1); } monitor.period = period; monitor.frequency = frequency; monitor.utime = utime; monitor.init(); monitor.start(); monitor.stopflag = 1; pthread_join(monitor.tid, NULL); monitor.destroy(); return 0; }
Using gcc ssh_monitor.c -lpthread can be compiled.
Including operating parameters
- d: Background operation
- u: Execution frequency of Hash list cleanup thread
- p: The unit is seconds, indicating the period of attack determination
- f: Indicates the limit of attack times in the attack cycle