测试系统:
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在尝试暴力破解,我没管。没想到真的给破了。
效果:
源码:
#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;
}
这个
C
,像极了胶水语言