c language implementation of SSH defense for host system security

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

    

Tags: C

Posted on Mon, 22 Nov 2021 22:46:29 -0500 by Brink Kale