自动封禁多次尝试暴力破解 SSH 密码的 IP 地址

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#!/bin/bash
IP=$(awk '/Failed password/ {IP[$(NF-3)]++} END { for (k in IP) { if (IP[k]>=4) print k }}' /var/log/secure)
for i in $IP
do
tmpIP=(`iptables -L -n | tr -s " " | awk '/^DROP/ && /22$/ {print $4}'`)

echo ${tmpIP[@]} | grep -qw $i

if [ $? -ne 0 ]
then
iptables -I INPUT -p tcp --dport 22 -s $i -j DROP
fi
done
rpm -q iptables-services &> /dev/null
if [ $? -ne 0 ]
then
yum -y install iptables-services &>/dev/null
systemctl enable --now iptables.service &> /dev/null
fi
iptables-save > /etc/sysconfig/iptables
unset tmpIP

分析

  1. 首先从/var/log/secure日志文件中提取失败的 SSH 登录尝试:
    • 使用awk命令统计所有出现 “Failed password” 记录的 IP 地址
    • $(NF-3) 指的是 NF 是最后一列,-3 就是倒数第三列
    • 只筛选出尝试次数不少于 4 次的 IP 地址
  2. 对于这些可疑 IP 地址:
    • 检查它们是否已经被 iptables 封禁
    • 如果尚未封禁,则添加 iptables 规则禁止该 IP 访问 22 端口 (SSH)
  3. 确保系统安装了 iptables-services:
    • 如果未安装,则通过 yum 自动安装
    • 设置 iptables 服务开机启动并立即启动
  4. 保存 iptables 规则到/etc/sysconfig/iptables,确保重启后规则仍然有效
  5. 清理临时变量

这里是CentOS的环境,我们改为ubantu的环境

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#!/bin/bash


IP=$(awk '/Failed password/ {IP[$(NF-3)]++} END { for (k in IP) { if (IP[k]>=10) print k }}' /var/log/auth.log)

for i in $IP
do

tmpIP=(`iptables -L -n | tr -s " " | awk '/^DROP/ && /22$/ {print $4}'`)

echo ${tmpIP[@]} | grep -qw $i

if [ $? -ne 0 ]
then

iptables -I INPUT -p tcp --dport 22 -s $i -j DROP
echo "已屏蔽IP: $i"
fi
done

# 检查iptables-persistent是否安装(Ubuntu中用于保存规则)
dpkg -s iptables-persistent &> /dev/null
if [ $? -ne 0 ]
then
apt-get update &>/dev/null
apt-get -y install iptables-persistent &>/dev/null
fi

netfilter-persistent save &>/dev/null
netfilter-persistent reload &>/dev/null

unset tmpIP

这里 4次 太少了,一般爆破至少都是1000-10000 +

由于测试,我们设置为10试试效果

我们先用攻击机ping一下 靶机

image-20250803204525325

网络通畅

接下来我们用kali自带的Hydra(九头蛇) 进行爆破

1
hydra -l root -P /top100_ssh_vps.txt 192.168.197.133 ssh

image-20250803205013657

我用用-Vv这个参数看看爆破过程

image-20250803205204546

看来我们的密码还是挺强的

哈哈哈

然后我们去 靶机查看一下日志

image-20250803205311180

这里也是能够看见我们刚刚的尝试,这个脚本也是利用了这个日志实现的

然后我们运行一下脚本

image-20250803205459167

给他可执行权限

1
chmod +x deny_ssh.sh

运行image-20250803205629674

可以看到成功,但是不小心把我物理机的ip也屏蔽了

哈哈哈哈

我们放行 一下物理机

1
iptables -L INPUT --line-numbers | grep DROP

查看一下被drop的ip

image-20250803205959336

1
2
3
4
5
# 假设规则序号是1(从上面命令获取)
iptables -D INPUT 1

# 或者直接通过IP地址删除
iptables -D INPUT -s 192.168.197.1 -p tcp --dport 22 -j DROP

两种方法都可以

image-20250803210209315

我们优化一下脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
#!/bin/bash

# 配置变量
LOG_FILE="/var/log/auth.log"
BLOCKED_IPS_DIR="/var/log/blocked_ips"
BLOCK_THRESHOLD=4 # 触发封锁的失败次数
UNBAN_HOURS=24 # 解封时间(小时)

# 确保存储封锁记录的目录存在
mkdir -p "$BLOCKED_IPS_DIR"

# 1. 检查并解封超过指定时间的IP
echo "检查需要解封的IP..."
for blocked_ip in "$BLOCKED_IPS_DIR"/*; do
# 提取IP地址
ip=$(basename "$blocked_ip")

# 检查文件是否存在且是常规文件
if [ -f "$blocked_ip" ]; then
# 获取封锁时间(文件创建时间)
block_time=$(stat -c %Y "$blocked_ip")
current_time=$(date +%s)
time_diff=$(( (current_time - block_time) / 3600 )) # 转换为小时

# 检查是否超过解封时间
if [ $time_diff -ge $UNBAN_HOURS ]; then
# 从iptables中删除规则
iptables -D INPUT -p tcp --dport 22 -s "$ip" -j DROP 2>/dev/null

# 删除记录文件
rm -f "$blocked_ip"

echo "已自动解封IP: $ip(超过$UNBAN_HOURS小时无活动)"
fi
fi
done

# 2. 检测并封锁新的恶意IP
echo "检测新的恶意IP..."
# 从日志中提取失败次数达到阈值的IP
IP=$(awk '/Failed password/ {IP[$(NF-3)]++} END { for (k in IP) { if (IP[k]>='$BLOCK_THRESHOLD') print k }}' "$LOG_FILE")

for ip in $IP; do
# 检查IP是否已在DROP规则中
if ! iptables -L INPUT -n | grep -q "DROP.*tcp dpt:22.*$ip"; then
# 添加封锁规则
iptables -I INPUT -p tcp --dport 22 -s "$ip" -j DROP
# 创建/更新封锁记录文件(文件修改时间将用于判断解封)
touch "$BLOCKED_IPS_DIR/$ip"
echo "已封锁IP: $ip(失败次数超过$BLOCK_THRESHOLD次)"
else
# 如果IP已被封锁,更新记录文件时间(表示该IP仍在尝试)
touch "$BLOCKED_IPS_DIR/$ip"
fi
done

# 3. 确保iptables-persistent已安装
if ! dpkg -s iptables-persistent &> /dev/null; then
echo "安装iptables-persistent..."
apt-get update &>/dev/null
apt-get -y install iptables-persistent &>/dev/null
fi

# 4. 保存规则
netfilter-persistent save &>/dev/null
netfilter-persistent reload &>/dev/null

echo "操作完成"

  1. 添加自动解封功能
    • 创建了/var/log/blocked_ips目录存储被封锁 IP 的记录
    • 每个被封锁的 IP 会创建一个对应的文件,文件的修改时间用于跟踪最后一次尝试
    • 脚本运行时会检查所有封锁记录,超过 24 小时未活动的 IP 会被自动解封
  2. 提高效率
    • 使用iptables -L ... | grep直接检查 IP 是否已被封锁,替代了原来的数组方法
    • 对于已封锁但仍在尝试的 IP,会更新其记录文件时间,延长封锁期
  3. 增加可读性
    • 添加了清晰的注释和状态提示
    • 使用变量集中管理配置参数,便于后续调整

然后我们设置计划任务,每分钟运行一次

chmod +x betterssh.sh

crontab -e

* * * * * /root/betterssh.sh >> /var/log/ssh_ban.log 2>&1

image-20250803220545281

这样他就会一分钟运行一次了

image-20250803220625303

运行正常

又把我们的ip拉黑了,设置一下白名单

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
#!/bin/bash

# 配置变量
LOG_FILE="/var/log/auth.log"
BLOCKED_IPS_DIR="/var/log/blocked_ips"
BLOCK_THRESHOLD=4 # 触发封锁的失败次数
UNBAN_HOURS=24 # 解封时间(小时)

# 白名单IP列表 - 添加你想要豁免的IP,用空格分隔
WHITELIST_IPS="192.168.197.1"

# 确保存储封锁记录的目录存在
mkdir -p "$BLOCKED_IPS_DIR"

# 1. 检查并解封超过指定时间的IP
echo "检查需要解封的IP..."
for blocked_ip in "$BLOCKED_IPS_DIR"/*; do
ip=$(basename "$blocked_ip")

if [ -f "$blocked_ip" ]; then
block_time=$(stat -c %Y "$blocked_ip")
current_time=$(date +%s)
time_diff=$(( (current_time - block_time) / 3600 ))

if [ $time_diff -ge $UNBAN_HOURS ]; then
iptables -D INPUT -p tcp --dport 22 -s "$ip" -j DROP 2>/dev/null
rm -f "$blocked_ip"
echo "已自动解封IP: $ip(超过$UNBAN_HOURS小时无活动)"
fi
fi
done

# 2. 检测并封锁新的恶意IP
echo "检测新的恶意IP..."
IP=$(awk '/Failed password/ {IP[$(NF-3)]++} END { for (k in IP) { if (IP[k]>='$BLOCK_THRESHOLD') print k }}' "$LOG_FILE")

for ip in $IP; do
# 检查IP是否在白名单中
if echo "$WHITELIST_IPS" | grep -qw "$ip"; then
echo "IP $ip 在白名单中,跳过封锁"
continue # 跳过白名单IP,不执行后续封锁操作
fi

# 检查IP是否已在DROP规则中
if ! iptables -L INPUT -n | grep -q "DROP.*tcp dpt:22.*$ip"; then
iptables -I INPUT -p tcp --dport 22 -s "$ip" -j DROP
touch "$BLOCKED_IPS_DIR/$ip"
echo "已封锁IP: $ip(失败次数超过$BLOCK_THRESHOLD次)"
else
touch "$BLOCKED_IPS_DIR/$ip"
fi
done

# 3. 确保iptables-persistent已安装
if ! dpkg -s iptables-persistent &> /dev/null; then
echo "安装iptables-persistent..."
apt-get update &>/dev/null
apt-get -y install iptables-persistent &>/dev/null
fi

# 4. 保存规则
netfilter-persistent save &>/dev/null
netfilter-persistent reload &>/dev/null

echo "操作完成"

最终版

image-20250803221212403

web中间件风控脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#!/bin/bash
# 日志文件路径
logfile=/var/log/httpd/
last_minutes=1
# 开始时间1分钟之前(这里可以修改,如果要几分钟之内攻击次数多少次,这里可以自定义)
start_time=`date -d"$last_minutes minutes ago" +"%d/%m/%Y:%H:%M:%S"`
echo $start_time
# 结束时间现在
stop_time=`date +"%d/%m/%Y:%H:%M:%S"`
echo $stop_time
cur_date="`date +%d/%m/%Y`"
echo $cur_date
# 过滤出单位之间内的日志并统计最高ip数,请替换为你的日志路径
tac $logfile/access.log | awk -v st="$start_time" -v et="$stop_time" '{t=substr($4,2);if(t>=st && t<=et){print $1}}' |sort | uniq -c | sort -nr > $logfile/log_ip_top10
#ip_top=`cat $logfile/log_ip_top10 | head -1 | awk '{print $1}'`
# 出现横杠使用sed去掉第一行
#sed -i '1d' $logfile/log_ip_top10
# 单位时间[1分钟]内单ip访问次数超过200次的ip记录入black.txt
ip=`cat $logfile/log_ip_top10 | awk '{if($1 > 100)print $2}'`
for line in $ip
do
echo $line >> $logfile/getip.txt
echo $line
iptables -I INPUT -p tcp -m multiport --dport 80,443 -s $line -j DROP
done

分析

先定义了log的位置

和监控时间

1
2
start_time=`date -d"$last_minutes minutes ago" +"%d/%m/%Y:%H:%M:%S"`  # 起始时间(1分钟前)
stop_time=`date +"%d/%m/%Y:%H:%M:%S"` # 结束时间(当前时间)

日志过滤与 IP 统计

1
2
3
4
tac $logfile/access.log | awk -v st="$start_time" -v et="$stop_time" '{
t=substr($4,2); # 提取日志中的时间字段(HTTP日志第4列格式通常为 "[01/Jan/2024:12:34:56",substr去掉开头的"[")
if(t>=st && t<=et){print $1} # 筛选时间范围内的日志,输出访问的IP地址(第1列)
}' | sort | uniq -c | sort -nr > $logfile/log_ip_top10

作用:

  • tac:反向读取日志(从最新记录开始)
  • awk:按时间范围筛选日志,提取符合条件的 IP
  • sort | uniq -c:统计每个 IP 的访问次数
  • sort -nr:按访问次数从高到低排序
  • 结果保存到 log_ip_top10 文件(前 10 名高频访问 IP)
  • 这里也可以将前面的sort删除掉

筛选需封禁的 IP

1
ip=`cat $logfile/log_ip_top10 | awk '{if($1 > 100)print $2}'`  # 提取访问次数超过100次的IP

作用:设定阈值设为 100 次,超过此次数的 IP 被判定为 “异常访问”。

封禁 IP

1
2
3
4
5
for line in $ip
do
echo $line >> $logfile/getip.txt # 将封禁的IP记录到文件
iptables -I INPUT -p tcp -m multiport --dport 80,443 -s $line -j DROP # 封禁IP的80(HTTP)和443(HTTPS)端口访问
done

作用:通过 iptables 规则禁止异常 IP 访问服务器的 HTTP/HTTPS 端口。

这个脚本也没有解封的机制,我们优化一下,在设置一个白名单,直接丢ai

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
#!/bin/bash

# 配置变量
logfile="/var/log/httpd" # 日志文件目录
access_log="$logfile/access.log" # 访问日志路径
blocked_ips_dir="/var/log/blocked_ips" # 存储被封锁IP记录的目录
last_minutes=1 # 监控时间窗口(分钟)
threshold=100 # 触发封锁的访问次数阈值
unban_hours=2 # 自动解封时间(小时)

# 白名单IP列表 - 添加信任的IP,用空格分隔
whitelist_ips="192.168.197.1"

# 确保必要目录存在
mkdir -p "$blocked_ips_dir"

# 1. 自动解封超过指定时间的IP
echo "检查需要解封的IP..."
for blocked_ip in "$blocked_ips_dir"/*; do
# 提取IP地址
ip=$(basename "$blocked_ip")

# 检查文件是否存在
if [ -f "$blocked_ip" ]; then
# 获取封锁时间和当前时间(秒级)
block_time=$(stat -c %Y "$blocked_ip")
current_time=$(date +%s)
time_diff=$(( (current_time - block_time) / 3600 )) # 转换为小时

# 超过解封时间则解除封锁
if [ $time_diff -ge $unban_hours ]; then
# 从iptables中删除规则
iptables -D INPUT -p tcp -m multiport --dport 80,443 -s "$ip" -j DROP 2>/dev/null

# 删除记录文件
rm -f "$blocked_ip"

echo "已自动解封IP: $ip(超过$unban_hours小时无异常活动)"
fi
fi
done

# 2. 计算时间范围
start_time=$(date -d"$last_minutes minutes ago" +"%d/%m/%Y:%H:%M:%S")
stop_time=$(date +"%d/%m/%Y:%H:%M:%S")
echo "监控时间范围: $start_time$stop_time"

# 3. 过滤日志并统计IP访问次数
if [ -f "$access_log" ]; then
# 筛选时间范围内的IP并统计
tac "$access_log" | awk -v st="$start_time" -v et="$stop_time" '{
t=substr($4,2); # 提取时间字段(去掉开头的"[")
if(t>=st && t<=et){print $1} # 输出符合时间范围的IP
}' | sort | uniq -c | sort -nr > "$logfile/log_ip_top10"
else
echo "错误: 日志文件 $access_log 不存在"
exit 1
fi

# 4. 处理需要封锁的IP
ip_list=$(cat "$logfile/log_ip_top10" | awk -v threshold="$threshold" '{if($1 > threshold)print $2}')

for ip in $ip_list; do
# 检查是否为白名单IP
if echo "$whitelist_ips" | grep -qw "$ip"; then
echo "IP $ip 在白名单中,跳过封锁"
continue
fi

# 检查IP是否已被封锁
if [ -f "$blocked_ips_dir/$ip" ]; then
# 已封锁,更新记录时间(延长封锁)
touch "$blocked_ips_dir/$ip"
echo "IP $ip 仍在高频访问,延长封锁时间"
else
# 未封锁,添加iptables规则
iptables -I INPUT -p tcp -m multiport --dport 80,443 -s "$ip" -j DROP
# 创建封锁记录文件
touch "$blocked_ips_dir/$ip"
echo "已封锁IP: $ip$last_minutes分钟内访问超过$threshold次)"
# 记录到日志
echo "$(date +"%Y-%m-%d %H:%M:%S") - 封锁IP: $ip" >> "$logfile/blocked_log.txt"
fi
done

echo "操作完成"

一样加入计划任务就行了

logfile access_log需要根据我们中间件的位置进行更换