Debian系统SSH防止暴力破解

@Ta 06-20 17:18发布,06-21 14:30修改 1345点击

测试系统:
Debian GNU/Linux 11 (bullseye)
Debian GNU/Linux 9 (stretch)

只要错误登录ssh次数超过10次就会被DROP IP
支持-d参数后台运行,后台运行时60秒检测一次,
支持把消息发送到钉钉机器人告警,修改#define IS_DIND 1或者#define IS_DIND 0

编译:
gcc -g -Wall a.c -o rhost -lcurl

一次运行
./rhost

后台运行
./rhost -d

为什么要搞这玩意?
当时腾讯已经发消息了,警告有非法IP在尝试暴力破解,我没管。没想到真的给破了。
5c8e046cb97468035240e82cb12d108.png

效果:
1655791074389.png
1655791149004.png

源码:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <time.h>
#include <signal.h>
#include <sys/wait.h>
#include <curl/curl.h>


#define BUFFER 1024
#define LONG_BUFFER 1024*100
#define TOP_IP 10
#define AWK " | awk -v num=%d '{a[$1]+=1;} END {for(i in a){if (a[i] >= num) {print i;}}}' "
#define GE_10 "grep -E \"^$(LC_ALL=\"C\" date \"+%h\").$(LC_ALL=\"C\" date \"+%d\")\" /var/log/auth.log | grep failure | grep rhost"
#define LE_10 "grep -E \"^$(LC_ALL=\"C\" date \"+%h\")..$(LC_ALL=\"C\" date | awk '{print $3}')\" /var/log/auth.log | grep failure | grep rhost"
#define IPTABLES "iptables -I INPUT -s %s -j DROP"
#define IPTABLES_CHECK "iptables -C INPUT -s %s -j DROP"
#define TIME 60


#define IS_DIND 1
#define PHONE "155"
#define DING_CURL "https://oapi.dingtalk.com/robot/send?access_token=7f069"


int strReplaceAll(char *str, char *sub, char *replace)
{

    if (NULL == str || NULL == sub || NULL == replace) {
        printf("mystrcat param error\n");
        return 1;
    }

    char *p = NULL;
    char *t = NULL;
    char *q = NULL;
    char *dst = NULL;
    char *src = NULL;

    int len = strlen(str);
    int len1 = strlen(sub);
    int len2 = strlen(replace);

    p = str;
    while ('\0' != *p) {
        t = str + len;
        q = strstr(str, sub);
        if (NULL == q) {        /*没有子串了,那么直接返回吧 */
            break;
        }

        src = q + len1;         /*源头, 原有sub后的一个字符 */
        dst = q + len2;         /*目的,放完replace后的一个字符 */
        memcpy(dst, src, t - src); /*原有字符串后移,放出空间 */
        memcpy(q, replace, len2); /*将replace字符拷贝进来 */
        len = len + len2 - len1;

        p = q + len2;           /* p 下一轮replace后的一个字符 */
    }

    str[len] = '\0';            /*通过'\0'表示结尾 */
    
    return 0;
}

int dingding_warning(char *ip)
{
    CURL *curl;
    CURLcode res;

    curl_global_init(CURL_GLOBAL_ALL);
    curl = curl_easy_init();
    if (curl == NULL) {
        return 1;
    }

    char jsonObj[1024] = "{  \
            \"msgtype\": \"text\",  \
            \"text\": { \
                \"content\": \"Alert @PHONE IP, 封禁!Warning!\" \
            },  \
            \"at\": {   \
                \"atMobiles\": [\"PHONE\"], \
                \"isAtAll\": false  \
            }   \
        }";

    strReplaceAll(jsonObj, "IP", ip);
    strReplaceAll(jsonObj, "PHONE", PHONE);
    printf("%s\n", jsonObj);

    struct curl_slist *headers = NULL;
    headers = curl_slist_append(headers, "Accept: application/json");
    headers = curl_slist_append(headers, "Content-Type: application/json");
    headers = curl_slist_append(headers, "charset: utf-8");

    curl_easy_setopt(curl, CURLOPT_URL, DING_CURL);
    curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0);
    curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0);
    curl_easy_setopt(curl, CURLOPT_POST, 1);
    curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
    curl_easy_setopt(curl, CURLOPT_POSTFIELDS, jsonObj);
    curl_easy_setopt(curl, CURLOPT_USERAGENT, "libcurl/0.1");

    res = curl_easy_perform(curl);

    curl_easy_cleanup(curl);
    curl_global_cleanup();

    return res;
}

int rule()
{
    FILE *fp, *fc;
    char p[2], splice_command[LONG_BUFFER], command[LONG_BUFFER], *temp, buffer[BUFFER], awk[BUFFER], iptables[BUFFER+(sizeof(IPTABLES))], iptables_check[BUFFER+(sizeof(IPTABLES))];

    time_t timep;
    struct tm *tp;
    time(&timep);
    tp = localtime(&timep);
    memset(splice_command, 0, LONG_BUFFER);
    memset(command, 0, LONG_BUFFER);
    memset(buffer, 0, BUFFER);
    memset(awk, 0, BUFFER);
    memset(iptables, 0, BUFFER);
    fp = NULL;
    fc = NULL;
    
    if (tp->tm_mday >= 10)
    {
        if ((fp = popen(GE_10, "r")) == NULL)
        {
            return 1;
        }
    }
    else
    {
        if ((fp = popen(LE_10, "r")) == NULL) {
            return 1;
        }
    }
    
    while (fgets(buffer, BUFFER, fp) != NULL) 
    {
        temp = strstr(buffer, "rhost");
        sscanf(temp, "rhost=%s", temp);
        if (atoi(strncpy(p, temp, 1)) > 0)
        {
            strcat(splice_command, temp);
            strcat(splice_command, "\n");
        }
    }
    //printf("%s", splice_command);                   // 测试没问题
    
    // 拼接命令
    sprintf(awk, AWK, TOP_IP);
    strcpy(command, "echo \"");
    strcat(command, splice_command);
    strcat(command, "\"");
    strcat(command, awk);
    //printf("%s\n", command);    // 测试没问题
    
    
    if ((fp = popen(command, "r")) == NULL)         // 执行命令
    {
        perror("popen");
        return 1;
    }
    
    while (fgets(buffer, BUFFER, fp) != NULL)       // 执行命令后, 为空时就不会
    {
        buffer[strlen(buffer) - 1] = '\0';          // 去除回车
        sprintf(iptables, IPTABLES, buffer);
        sprintf(iptables_check, IPTABLES_CHECK, buffer);
        printf("%s\n", iptables);
        
        if (0 != system(iptables_check))            // 判断是否存在规则, 不存在时再添加规则
        {
            if (IS_DIND == 1)                       // 钉钉告警
            {
                dingding_warning(buffer);
            }
            
            if ((fc = popen(iptables, "r")) == NULL)
            {
                perror("popen");
                return 1;
            }
        }

    }

    if (fp != NULL)
        pclose(fp);
    
    if (fc != NULL)
        pclose(fc);
    
    return 0;
}

static void sig_child(int signo)
{
    pid_t pid;
    int stat;
    // 处理僵尸进程
    while ((pid = waitpid(-1, &stat, WNOHANG)) > 0)
        ;
    return;
}


int main(int argc, char *argv[], char **env)
{
    signal(SIGCHLD, sig_child); // 创建捕捉子进程退出信号
    
    if (argv[1] != NULL && 0 == strcmp(argv[1], "-d"))
    {
        if (daemon(1, 1)) {         // 守护进程
            perror("daemon");
            return -1;
        }
        
        while (1)
        {
            rule();
            
            sleep(TIME);
        }
    }
    else
    {
        rule();
    }
    
    return 0;
}

回复列表(6)
添加新回复
回复需要登录