偶然?还是必然? — 谁是罪魁祸首

宝子放假,转眼假期已经过了一半,想着找个周末带宝子回老家住几天,大姐家的孩子周日要办升学宴,既然都回来了,不去也不合适,刚好就可以周六回老家,周日去淄博。

对象说,周日青岛会下大暴雨,还是下班之后直接回吧,不然第二天很可能走不了。

就这样,马不停蹄,下班买点东西就直接接上孩子往回走。天气预报零点的雨,在九点多到潍坊的时候就开始逐渐大了。

对象开车的时候,自己打开手机想回复下评论,但是却发现一个问题,那就是页面非常卡。刚开始还以为是网络问题,但是切到短视频却异常流畅。

瞬间有种不祥的预感,那就是服务器的 cpu 肯定又跑满了。

之前看到威言威语发的《暂时停用腾讯EdgeOne了》 ,感觉自己的系统貌似没什么太大的问题。但是,实际上是在这之前,切换到 eo 之后,也出现过一次 cpu 跑满的情况,当时重启 php 之后一切正常了,以为是偶发事件。所以也没再关注,但是这次尝试远程重启服务之后,只是瞬间缓解。几分钟之后就又卡死了,所以昨天能发评论的宝子真的都是真爱,么么哒(๑•́₃•̀๑),因为我自己都打不开我的后台。😂

这种感觉还有一个前兆,那就是后台资源库突然多了个文件。

不过这个文件看起来是某个插件的压缩包,但是描述是另外一个域名,猜测是另外一个域名传上来的。但是。搜了下这个用户不存在,所以最后直接把插件和文件一起删除了。

但是这个 cpu 跑满的感觉还是感觉是 cc,不然不至于 php 能直接占满了全部资源。

到家之后,看了下 eo 的后台没什么能配置地方。但是,通过服务器的访问记录却发现,有几个地址在频繁请求登录页面:

当然,更诡异的是这里面好几个 ip 竟然都是腾讯的海外节点。这就有点奇怪了。

尤其是 163 这个,按照常理来说,如果是 cdn 节点回源,应该不会摁着这一个地址回源,并且这个请求频率大概率会被 cdn 拦截,然而,现在的现象是不单没被拦截,还高频访问,一个地址在一秒内发起了几十个请求。这尼玛就离谱了,查了一下加速域名的节点,发现这些都不在 eo 的节点列表内。

那么,此时会合理的猜的就是 eo 的机房内或者是在某些地方的节点回源会泄露原始服务器地址,这样这一系列的请求就说的通了。这些请求本身并没有结果 cdn,所以直接是从机房发起的请求。

在回源地址泄露之后,有人通过 bot 控制了大量机房内的机器,尝试对 wp 的登录地址进行暴力破解,而这告诉 cc 也导致了家里的 mac mini 的服务器资源瞬间被耗尽了。

所以这个问题不是 eo 本身导致的,但是确实是套了 eo 的国外节点之后,有 eo 的某些请求泄露回源地址导致出现了直接针对服务器的 cc 攻击。这个攻击目标很明确,就是破解登录密码。本来想直接封锁那几个 ip,但是封锁之后发现还是不断的有新的开始尝试,于是,最新版本的工具就出现了,直接写个脚本,通过检测 nginx 访问日志,实现对 ip 动态风控。

代码如下:

#!/bin/bash

# 实时监控日志文件并自动封锁恶意IP
# 使用方法: sudo ./realtime_block.sh

LOG_FILE="/home/wwwlogs/h4ck.org.cn.log"
BLOCKED_IPS_FILE="/tmp/blocked_ips.txt"
MAX_REQUESTS_PER_MINUTE=10
BLOCK_DURATION=3600  # 封锁时间(秒)
DEBUG_MODE=false  # 设置为true启用调试模式
QUIET_MODE=false  # 设置为true启用静默模式(只显示新增封锁)
ATTACK_HISTORY_FILE="/tmp/attack_history.txt"  # 攻击历史记录文件

# 检查是否以root权限运行
if [ "$EUID" -ne 0 ]; then
    echo "请使用sudo运行此脚本"
    exit 1
fi

# 检查inotify-tools是否安装
if ! command -v inotifywait &> /dev/null; then
    echo "请先安装inotify-tools:"
    echo "Ubuntu/Debian: sudo apt-get install inotify-tools"
    echo "CentOS/RHEL: sudo yum install inotify-tools"
    exit 1
fi

# 检查日志文件是否存在
if [ ! -f "$LOG_FILE" ]; then
    echo "错误: 日志文件 $LOG_FILE 不存在"
    exit 1
fi

# 创建iptables链(如果不存在)
iptables -N BLOCK_MALICIOUS_IPS 2>/dev/null

# 确保BLOCK_MALICIOUS_IPS链在INPUT链的最前面
iptables -C INPUT -j BLOCK_MALICIOUS_IPS 2>/dev/null || iptables -I INPUT 1 -j BLOCK_MALICIOUS_IPS

# 初始化文件
touch "$BLOCKED_IPS_FILE"
touch "$ATTACK_HISTORY_FILE"

# 函数:检查并修复iptables配置
check_iptables_config() {
    echo "检查iptables配置..."
    
    # 检查BLOCK_MALICIOUS_IPS链是否存在
    if ! iptables -L BLOCK_MALICIOUS_IPS >/dev/null 2>&1; then
        echo "创建BLOCK_MALICIOUS_IPS链..."
        iptables -N BLOCK_MALICIOUS_IPS
    fi
    
    # 检查INPUT链中是否包含BLOCK_MALICIOUS_IPS
    if ! iptables -C INPUT -j BLOCK_MALICIOUS_IPS 2>/dev/null; then
        echo "将BLOCK_MALICIOUS_IPS链插入到INPUT链的最前面..."
        iptables -I INPUT 1 -j BLOCK_MALICIOUS_IPS
    fi
    
    # 显示当前配置
    echo "当前iptables配置:"
    iptables -L INPUT -n --line-numbers | head -10
    echo "BLOCK_MALICIOUS_IPS链规则:"
    iptables -L BLOCK_MALICIOUS_IPS -n
}

# 函数:重新加载已封锁的IP到iptables
reload_blocked_ips() {
    echo "重新加载已封锁的IP到iptables..."
    
    if [ -f "$BLOCKED_IPS_FILE" ]; then
        local count=0
        while read -r ip; do
            if [ -n "$ip" ]; then
                # 检查iptables中是否已有此规则
                if ! iptables -C BLOCK_MALICIOUS_IPS -s "$ip" -j DROP 2>/dev/null; then
                    iptables -A BLOCK_MALICIOUS_IPS -s "$ip" -j DROP
                    count=$((count + 1))
                fi
            fi
        done < "$BLOCKED_IPS_FILE"
        echo "重新加载了 $count 个IP到iptables"
    fi
}

# 检查并修复iptables配置
check_iptables_config

# 重新加载已封锁的IP
reload_blocked_ips

echo "开始实时监控日志文件: $LOG_FILE"
echo "最大请求频率: $MAX_REQUESTS_PER_MINUTE 次/分钟"
echo "封锁时间: $BLOCK_DURATION 秒"

# 函数:封锁IP
block_ip() {
    local ip=$1
    local reason=$2
    
    # 检查IP是否已经被封锁
    if ! grep -q "^$ip$" "$BLOCKED_IPS_FILE"; then
        echo "$(date '+%Y-%m-%d %H:%M:%S') - 封锁IP: $ip (原因: $reason)"
        
        # 添加到iptables(确保规则正确添加)
        iptables -A BLOCK_MALICIOUS_IPS -s "$ip" -j DROP
        
        # 验证规则是否添加成功
        if iptables -C BLOCK_MALICIOUS_IPS -s "$ip" -j DROP 2>/dev/null; then
            echo "$(date '+%Y-%m-%d %H:%M:%S') - 成功添加iptables规则: $ip"
        else
            echo "$(date '+%Y-%m-%d %H:%M:%S') - 警告: iptables规则添加失败: $ip"
        fi
        
        # 记录到文件
        echo "$ip" >> "$BLOCKED_IPS_FILE"
        
        # 记录日志
        echo "$(date '+%Y-%m-%d %H:%M:%S') - 封锁IP: $ip (原因: $reason)" >> /var/log/realtime_block.log
    else
        echo "$(date '+%Y-%m-%d %H:%M:%S') - IP $ip 已经被封锁"
    fi
}

# 函数:记录攻击历史
record_attack() {
    local ip=$1
    local attack_type=$2
    local timestamp=$(date +%s)
    echo "$ip|$attack_type|$timestamp" >> "$ATTACK_HISTORY_FILE"
}

# 函数:检查攻击历史
check_attack_history() {
    local ip=$1
    local attack_type=$2
    local current_time=$(date +%s)
    local time_window=300  # 5分钟时间窗口
    
    # 清理过期的攻击记录
    local temp_file="/tmp/temp_attack_history.txt"
    while IFS='|' read -r record_ip record_type record_time; do
        if [ $((current_time - record_time)) -lt $time_window ]; then
            echo "$record_ip|$record_type|$record_time" >> "$temp_file"
        fi
    done < "$ATTACK_HISTORY_FILE"
    
    mv "$temp_file" "$ATTACK_HISTORY_FILE" 2>/dev/null
    
    # 统计该IP在时间窗口内的攻击次数
    local attack_count=$(grep "^$ip|" "$ATTACK_HISTORY_FILE" | wc -l)
    echo "$attack_count"
}

# 函数:分析新日志条目
analyze_new_logs() {
    local new_lines="$1"
    
    if [ -n "$new_lines" ]; then
        if [ "$DEBUG_MODE" = true ]; then
            echo "DEBUG: 分析新日志行数: $(echo "$new_lines" | wc -l)"
        fi
        
        # 统计IP请求频率
        local ip_counts=$(echo "$new_lines" | awk '{print $1}' | sort | uniq -c | sort -nr)
        
        if [ "$DEBUG_MODE" = true ] && [ -n "$ip_counts" ]; then
            echo "DEBUG: IP统计结果:"
            echo "$ip_counts"
        fi
        
        # 检查每个IP的请求频率
        while read -r count ip; do
            if [ -n "$ip" ] && [ "$count" -gt "$MAX_REQUESTS_PER_MINUTE" ]; then
                record_attack "$ip" "high_frequency"
                local total_attacks=$(check_attack_history "$ip" "high_frequency")
                if [ "$total_attacks" -gt 2 ]; then
                    block_ip "$ip" "请求频率过高: ${count}次/分钟 (累计${total_attacks}次)"
                fi
            fi
        done <<< "$ip_counts"
        
        # 检查WordPress登录尝试
        local wp_login_attempts=$(echo "$new_lines" | grep "POST /wp-login.php" | awk '{print $1}' | sort | uniq -c | sort -nr)
        
        if [ "$DEBUG_MODE" = true ] && [ -n "$wp_login_attempts" ]; then
            echo "DEBUG: WordPress登录尝试统计:"
            echo "$wp_login_attempts"
        fi
        
        if [ -n "$wp_login_attempts" ]; then
            while read -r count ip; do
                if [ -n "$ip" ]; then
                    record_attack "$ip" "wp_login"
                    local total_attacks=$(check_attack_history "$ip" "wp_login")
                    if [ "$total_attacks" -gt 1 ]; then  # 只要超过1次WordPress登录尝试就封锁
                        block_ip "$ip" "WordPress暴力破解: ${count}次尝试 (累计${total_attacks}次)"
                    fi
                fi
            done <<< "$wp_login_attempts"
        fi
        
        # 检查404错误(可能的扫描行为)
        local error_404_attempts=$(echo "$new_lines" | grep " 404 " | awk '{print $1}' | sort | uniq -c | sort -nr)
        
        if [ -n "$error_404_attempts" ]; then
            while read -r count ip; do
                if [ -n "$ip" ] && [ "$count" -gt 5 ]; then  # 降低阈值到5次404错误
                    record_attack "$ip" "error_404"
                    local total_attacks=$(check_attack_history "$ip" "error_404")
                    if [ "$total_attacks" -gt 2 ]; then
                        block_ip "$ip" "扫描行为: ${count}次404错误 (累计${total_attacks}次)"
                    fi
                fi
            done <<< "$error_404_attempts"
        fi
        
        # 检查可疑的扫描行为(访问不存在的文件)
        local suspicious_requests=$(echo "$new_lines" | grep -E "(\.php|\.asp|\.jsp|\.exe|\.bat|\.cmd)" | awk '{print $1}' | sort | uniq -c | sort -nr)
        
        if [ -n "$suspicious_requests" ]; then
            while read -r count ip; do
                if [ -n "$ip" ] && [ "$count" -gt 3 ]; then  # 降低阈值到3次可疑请求
                    record_attack "$ip" "suspicious"
                    local total_attacks=$(check_attack_history "$ip" "suspicious")
                    if [ "$total_attacks" -gt 1 ]; then
                        block_ip "$ip" "可疑扫描: ${count}次可疑请求 (累计${total_attacks}次)"
                    fi
                fi
            done <<< "$suspicious_requests"
        fi
    fi
}

# 函数:监控日志文件变化
monitor_log_file() {
    echo "开始实时监控..."
    
    # 记录当前文件行数
    local last_line_count=$(wc -l < "$LOG_FILE" 2>/dev/null || echo 0)
    
    # 使用inotifywait监控文件变化
    inotifywait -m -e modify "$LOG_FILE" | while read -r directory events filename; do
        # 获取当前文件行数
        local current_line_count=$(wc -l < "$LOG_FILE" 2>/dev/null || echo 0)
        
        if [ "$current_line_count" -gt "$last_line_count" ]; then
            if [ "$DEBUG_MODE" = true ]; then
                echo "DEBUG: 检测到新日志行,从第 $((last_line_count + 1)) 行开始"
            fi
            
            # 获取新增的日志行
            local new_lines=$(tail -n +$((last_line_count + 1)) "$LOG_FILE" 2>/dev/null)
            
            if [ -n "$new_lines" ]; then
                # 记录分析前的封锁数量
                local old_blocked_count=$(wc -l < "$BLOCKED_IPS_FILE" 2>/dev/null || echo 0)
                
                # 分析新日志
                analyze_new_logs "$new_lines"
                
                # 获取分析后的封锁数量
                local new_blocked_count=$(wc -l < "$BLOCKED_IPS_FILE" 2>/dev/null || echo 0)
                
                # 根据模式输出不同的信息
                if [ "$new_blocked_count" -gt "$old_blocked_count" ]; then
                    # 有新增封锁
                    if [ "$QUIET_MODE" = true ]; then
                        # 静默模式:只显示新增数量
                        echo "$(date '+%Y-%m-%d %H:%M:%S') - 新增封锁 $(($new_blocked_count - $old_blocked_count)) 个IP"
                    else
                        # 正常模式:显示详细信息
                        echo "$(date '+%Y-%m-%d %H:%M:%S') - 新增封锁 $(($new_blocked_count - $old_blocked_count)) 个IP,总计 $new_blocked_count 个"
                    fi
                elif [ "$QUIET_MODE" = false ] && [ "$DEBUG_MODE" = false ]; then
                    # 非静默且非调试模式:显示处理状态(可选)
                    echo "$(date '+%Y-%m-%d %H:%M:%S') - 处理日志,当前封锁 $new_blocked_count 个IP"
                fi
            fi
            
            # 更新行数
            last_line_count=$current_line_count
        fi
    done
}

# 函数:定期清理过期封锁
cleanup_expired_blocks() {
    while true; do
        sleep 3600  # 每小时执行一次
        
        local current_time=$(date +%s)
        local temp_file="/tmp/temp_blocked_ips.txt"
        
        # 重新创建封锁文件(简化处理)
        if [ -f "$BLOCKED_IPS_FILE" ]; then
            # 这里可以根据需要实现更精确的时间管理
            # 目前简化处理,每小时清理一次
            echo "$(date '+%Y-%m-%d %H:%M:%S') - 清理过期封锁" >> /var/log/realtime_block.log
        fi
    done
}

# 函数:测试模式
test_mode() {
    echo "测试模式: 模拟恶意请求..."
    
    # 创建测试日志条目
    local test_log_entries=(
        "192.168.1.100 - - [$(date '+%d/%b/%Y:%H:%M:%S')] \"GET / HTTP/1.1\" 200 1234"
        "192.168.1.100 - - [$(date '+%d/%b/%Y:%H:%M:%S')] \"GET / HTTP/1.1\" 200 1234"
        "192.168.1.100 - - [$(date '+%d/%b/%Y:%H:%M:%S')] \"GET / HTTP/1.1\" 200 1234"
        "192.168.1.100 - - [$(date '+%d/%b/%Y:%H:%M:%S')] \"GET / HTTP/1.1\" 200 1234"
        "192.168.1.100 - - [$(date '+%d/%b/%Y:%H:%M:%S')] \"GET / HTTP/1.1\" 200 1234"
        "192.168.1.100 - - [$(date '+%d/%b/%Y:%H:%M:%S')] \"GET / HTTP/1.1\" 200 1234"
        "192.168.1.100 - - [$(date '+%d/%b/%Y:%H:%M:%S')] \"GET / HTTP/1.1\" 200 1234"
        "192.168.1.100 - - [$(date '+%d/%b/%Y:%H:%M:%S')] \"GET / HTTP/1.1\" 200 1234"
        "192.168.1.100 - - [$(date '+%d/%b/%Y:%H:%M:%S')] \"GET / HTTP/1.1\" 200 1234"
        "192.168.1.100 - - [$(date '+%d/%b/%Y:%H:%M:%S')] \"GET / HTTP/1.1\" 200 1234"
        "192.168.1.100 - - [$(date '+%d/%b/%Y:%H:%M:%S')] \"GET / HTTP/1.1\" 200 1234"
        "192.168.1.100 - - [$(date '+%d/%b/%Y:%H:%M:%S')] \"GET / HTTP/1.1\" 200 1234"
    )
    
    # 将测试数据写入日志文件
    for entry in "${test_log_entries[@]}"; do
        echo "$entry" >> "$LOG_FILE"
        sleep 0.1
    done
    
    echo "测试完成,请检查是否封锁了IP 192.168.1.100"
}

# 检查命令行参数
if [ "$1" = "--test" ]; then
    test_mode
    exit 0
elif [ "$1" = "--status" ]; then
    echo "当前封锁状态:"
    if [ -f "$BLOCKED_IPS_FILE" ]; then
        local blocked_count=$(wc -l < "$BLOCKED_IPS_FILE" 2>/dev/null || echo 0)
        echo "已封锁IP数量: $blocked_count"
        if [ "$blocked_count" -gt 0 ]; then
            echo "封锁的IP列表:"
            cat "$BLOCKED_IPS_FILE"
        fi
    else
        echo "暂无封锁的IP"
    fi
    exit 0
elif [ "$1" = "--debug" ]; then
    DEBUG_MODE=true
    echo "调试模式已启用"
elif [ "$1" = "--quiet" ]; then
    QUIET_MODE=true
    echo "静默模式已启用(只显示新增封锁)"
elif [ "$1" = "--minimal" ]; then
    QUIET_MODE=true
    DEBUG_MODE=false
    echo "最小输出模式已启用(只显示新增封锁)"
elif [ "$1" = "--check-iptables" ]; then
    echo "检查iptables配置..."
    check_iptables_config
    echo ""
    echo "当前封锁的IP列表:"
    if [ -f "$BLOCKED_IPS_FILE" ]; then
        cat "$BLOCKED_IPS_FILE"
    else
        echo "暂无封锁的IP"
    fi
    exit 0
elif [ "$1" = "--attack-history" ]; then
    echo "攻击历史记录:"
    if [ -f "$ATTACK_HISTORY_FILE" ]; then
        echo "IP地址 | 攻击类型 | 时间戳"
        echo "------------------------"
        while IFS='|' read -r ip type timestamp; do
            time_str=$(date -d "@$timestamp" '+%Y-%m-%d %H:%M:%S' 2>/dev/null || echo "未知时间")
            echo "$ip | $type | $time_str"
        done < "$ATTACK_HISTORY_FILE"
    else
        echo "暂无攻击历史记录"
    fi
    exit 0
fi

# 启动清理进程
cleanup_expired_blocks &
CLEANUP_PID=$!

# 启动监控
monitor_log_file

# 清理进程
kill $CLEANUP_PID 2>/dev/null

使用说明:

### 基本使用
```bash
sudo ./realtime_block.sh
```

### 调试模式
```bash
sudo ./realtime_block.sh --debug
```

### 静默模式(只显示新增封锁)
```bash
sudo ./realtime_block.sh --quiet
```

### 最小输出模式(最简洁的输出)
```bash
sudo ./realtime_block.sh --minimal
```

### 查看当前状态
```bash
sudo ./realtime_block.sh --status
```

### 测试模式
```bash
sudo ./realtime_block.sh --test
```

## 配置参数

在脚本开头可以修改以下参数:

```bash
LOG_FILE="/home/wwwlogs/h4ck.org.cn.log"  # 日志文件路径
BLOCKED_IPS_FILE="/tmp/blocked_ips.txt"    # 封锁IP记录文件
MAX_REQUESTS_PER_MINUTE=10                 # 每分钟最大请求数
BLOCK_DURATION=3600                        # 封锁时间(秒)
DEBUG_MODE=false                           # 调试模式
QUIET_MODE=false                           # 静默模式
```

最终效果:

这,文正写完了,雨也停了。

_cuva
☆版权☆

* 网站名称:obaby@mars
* 网址:https://h4ck.org.cn/
* 个性:https://oba.by/
* 本文标题: 《偶然?还是必然? — 谁是罪魁祸首》
* 本文链接:https://h4ck.org.cn/2025/08/21237
* 短链接:https://oba.by/?p=21237
* 转载文章请标明文章来源,原文标题以及原文链接。请遵从 《署名-非商业性使用-相同方式共享 2.5 中国大陆 (CC BY-NC-SA 2.5 CN) 》许可协议。


You may also like

8 comments

  1. Level 6
    Google Chrome 109 Google Chrome 109 Windows 10 Windows 10 cn中国–上海–上海 腾讯云

    不是偶然,也不是必然,是高产

  2. Level 6
    Google Chrome 138 Google Chrome 138 Windows 11 Windows 11 cn中国–四川–成都 联通

    我域名解析,迄今为止屏蔽了国外的访问,国外ip太乱了。

  3.  Level 6
    Firefox 134 Firefox 134 GNU/Linux GNU/Linux cn中国–广东–珠海 电信

    第一张照片好帅,最后一张照片也很帅,可是车是要撞到了树上吗

  4. Level 4
    Safari 18 Safari 18 Mac OS X 10.15 Mac OS X 10.15 cn中国–广东–清远 电信

    看来动态的站点使用 EO 都或多或少会有点问题?

  5.  Level 4
    Google Chrome 138 Google Chrome 138 Windows 11 Windows 11 cn中国–河北–秦皇岛 电信

    看起来都是用免费的套餐加速动态站点的问题,静态站点还没有这个问题

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注