主机系统安全之SSH防御的c语言实现

脆弱的服务器

    相信好多coder们都喜欢租用一些云主机来进行建站、存储资源或者实践,而大多数人租用了之后应该都会选择系统裸奔。

    我手里正好也有一台云主机,偶尔登录消遣一下。某天机缘巧合,登录系统查看了一下登录认证日志文件/var/log/secure,令人惊恐的事情出现了:

    我在文件中发现了大量的“authentication failure......rhost=......”,原来我们的云主机一直处于危险之中而不自知,每时每刻都会有一些非法的人在对我们的主机进行扫描、爆破等不可言传的行为,要感谢那天灵感迸发而出的异常复杂的登录口令,才避免遭遇沦为肉鸡的命运。

    云平台方的安全服务是需要收费的,对于灰常贫穷的我只能手动撸码防御了,虽简易,聊胜于无嘛。到这里,土豪们可以自觉出去了......秀出肌肉,各种服务裱起来!

SSH攻击防御的一些思路

1、首先呢,就是监控一下/var/log/secure文件。从secure文件中,我们可以找到授权认证失败的历史,从而获得远端攻击者的IP地址信息,在这里可以使用inotify来检测文件的修改。

2、然后呢,我们需要构建一个哈希链表和一个字符串哈希函数。哈希函数可以选择常用的time33算法。当分析出来攻击者IP地址信息时,将对应IP信息存入哈希链表,并计数。

3、创建一个任务线程,做哈希链表的清理工作。一方面是清理超时的缓存节点,另一方面可以找出特定周期内的攻击次数达到阈值的非法者。

4、找到非法者之后,我们可以构建告警信息插入到syslog日志文件,供查阅(/var/log/message)。

5、可以使用iptables指令进行ip地址的封禁。当然了,还可以借助邮件命令发送一封友好的提醒邮件。再下面的代码中,发送邮件的功能没有尝试,有兴趣的朋友可以加入试试。

SSH攻击防御源码

下面就是又臭又长的c代码了,行文略显粗糙。

#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 哈希算法 
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);
}

//将远端非法ip地址插入哈希链表
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; 
}

//匹配授权认证失败信息
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);
                //找到了攻击者ip地址
                monitor.push(ipaddr);
            }
            g_sshfile_offset += strlen(buffer);
            memset(buffer, 0x0, SSH_BUF_SIZE);
            memset(ipaddr, 0x0, sizeof(ipaddr));
        }
        fclose(fp);
    }
}

//ssh攻击信息监测
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); 
            //当secure修改时,进行授权认证错误信息的匹配和信息提取
            if (IN_MODIFY & ev->mask)
                ssh_get_secure_message();
            offset += sizeof(struct inotify_event);
            offset += ev->len;
        }
        memset(buffer, 0x0, SSH_BUF_SIZE);
    }
}

//缓存处理线程
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) {
                        //使用iptables禁用攻击者
                        memset(cmd, 0x0, sizeof(cmd));
                        sprintf(cmd, "iptables -I INPUT -s %s -j DROP", item->rip);
                        system(cmd);
                        //写入syslog日志文件
                        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;
}

//初始化文件
static void ssh_monitor_init()
{
    int i;
    pthread_t tid;
    g_sshfile_offset = 0;
    //正则匹配串
    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 );
    }
    //创建缓存处理任务线程
    pthread_create(&tid, NULL, ssh_monitor_expire_handler, NULL);
    monitor.tid = tid;
}

//写入syslog文件
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;
}

使用gcc ssh_monitor.c -lpthread编译即可。

其中运行参数

    -d:后台运行

    -u:哈希链表清理线程的执行频率

    -p:单位是秒,表示攻击判定的周期

    -f:表示攻击周期内的攻击次数限定

    

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值