这几天不知道是发生什么事了,说是不知道什么事情,但是大概率是被打了。只是这次打的挺高级的,外层的 eo 貌似也没什么反应。只是那个访问量通过 umami 看,直接爆炸了。
平常几百的访问量,昨天的时候,结果到了 2000 多,当然这不是最奇怪的,奇怪的是服务器过了会儿卡死了。之前都是因为请求太多 php-fpm 耗尽 cpu 资源卡死了,这次以为还是同样的问题。然而,并不是,发现 mysql 把 cpu 跑满了,查看日志的时候发现大量的 wp-cron.php 的请求,这尼玛,请求直接透传过来了。
另外还有一大堆 bot 的请求,包括 bing 以及一些乱起八糟的爬虫遍历。
最开始没想到什么好办法,简单粗暴的把 wp-cron.php 改名了,暂时解决了这个问题。
不过这个方法的确是高明,带着参数透传过来,wp 就是疯狂的执行,一条没执行完就到了下一条。然而,对于这种事情直接改名的确是可以解决办法,不过后来想了一下还是直接从 eo 下手吧。
尽管 eo 防住了 22 万次的攻击,但是,这些透传的请求,直接让 mysql 耗尽了 cpu 资源,也是个不错的办法,甚至请求频率都不用太高。流量到了 144g,这也不知道是哪个哥们又闲的蛋疼了,如果真的蛋疼来找姐姐啊,姐姐帮你治疗,直接给你割下来,塞你自己嘴里!
昨天晚上发现这个情况的时候,本来是想去处理下的,结果对象在用电脑,自己又不想去开笔记本,就用手机处理了一下,简单的改下了文件名。
今天早上才处理了一下,加到了 eo 的访问规则里:
尽管如此,还是对这几天的访问记录比较好奇,想看看请求了多少次。去拉 nginx 日志的时候发现文件已经 1.5G 了。直接截取这几天的记录,用 goaccess 跑了一下,但是比较奇怪的是这个 wp-cron.php 的请求竟然没有。
暂时放弃 goaccess 直接使用 ngxtop 进行数据分析:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
使用ngxtop分析Nginx日志中的POST请求
提供交互式菜单和多种分析选项
"""
import subprocess
import sys
import os
from pathlib import Path
def run_ngxtop(cmd_args):
"""运行ngxtop命令"""
venv_python = Path(__file__).parent / "venv" / "bin" / "python"
ngxtop_script = Path(__file__).parent / "venv" / "bin" / "ngxtop"
if not ngxtop_script.exists():
print("错误: ngxtop未安装,请先运行: source venv/bin/activate && pip install ngxtop")
sys.exit(1)
try:
result = subprocess.run(
[str(ngxtop_script)] + cmd_args,
capture_output=True,
text=True,
check=False
)
print(result.stdout)
if result.stderr and "error" in result.stderr.lower():
print(result.stderr, file=sys.stderr)
return result.returncode == 0
except Exception as e:
print(f"错误: {e}", file=sys.stderr)
return False
def show_menu():
"""显示菜单"""
print("\n" + "="*60)
print("Nginx日志POST请求分析 - ngxtop工具")
print("="*60)
print("1. POST请求总览")
print("2. 按URL统计POST请求 (Top 20)")
print("3. 按IP统计POST请求 (Top 20)")
print("4. 按状态码统计POST请求")
print("5. POST请求中状态码为404的URL")
print("6. POST请求中状态码为200的URL")
print("7. 可疑POST请求 (xmlrpc, wp-login等)")
print("8. POST请求详情示例")
print("9. 自定义查询")
print("0. 退出")
print("="*60)
def analyze_post_requests(log_file):
"""分析POST请求"""
if not os.path.exists(log_file):
print(f"错误: 日志文件 {log_file} 不存在")
return
base_args = ["-l", log_file, "--no-follow", "-i", 'request.startswith("POST")']
while True:
show_menu()
choice = input("\n请选择分析选项 (0-9): ").strip()
if choice == "0":
print("退出分析")
break
elif choice == "1":
print("\n【POST请求总览】")
print("-" * 60)
run_ngxtop(base_args + ["--limit", "0"])
elif choice == "2":
print("\n【按URL统计POST请求 (Top 20)】")
print("-" * 60)
run_ngxtop(base_args + ["--group-by", "request_path", "--limit", "20"])
elif choice == "3":
print("\n【按IP统计POST请求 (Top 20)】")
print("-" * 60)
run_ngxtop(base_args + ["--group-by", "remote_addr", "--limit", "20"])
elif choice == "4":
print("\n【按状态码统计POST请求】")
print("-" * 60)
run_ngxtop(base_args + ["--group-by", "status", "--limit", "0"])
elif choice == "5":
print("\n【POST请求中状态码为404的URL (Top 10)】")
print("-" * 60)
run_ngxtop(["-l", log_file, "--no-follow",
"-i", 'request.startswith("POST") and status == 404',
"--group-by", "request_path", "--limit", "10"])
elif choice == "6":
print("\n【POST请求中状态码为200的URL (Top 10)】")
print("-" * 60)
run_ngxtop(["-l", log_file, "--no-follow",
"-i", 'request.startswith("POST") and status == 200',
"--group-by", "request_path", "--limit", "10"])
elif choice == "7":
print("\n【可疑POST请求统计】")
print("-" * 60)
run_ngxtop(["-l", log_file, "--no-follow",
"-i", 'request.startswith("POST") and (request_path == "/xmlrpc.php" or request_path == "/wp-login.php" or request_path.startswith("/wp-admin"))',
"--group-by", "request_path", "--limit", "0"])
elif choice == "8":
print("\n【POST请求详情示例 (前10条)】")
print("-" * 60)
run_ngxtop(base_args + ["print", "remote_addr", "time_local", "request", "status", "bytes_sent", "--limit", "10"])
elif choice == "9":
print("\n【自定义查询】")
print("-" * 60)
print("示例查询:")
print(" - 查看特定URL: ngxtop -l <file> -i 'request.startswith(\"POST\") and request_path == \"/wp-cron.php\"'")
print(" - 查看特定IP: ngxtop -l <file> -i 'request.startswith(\"POST\") and remote_addr == \"114.66.247.160\"'")
print(" - 查看错误请求: ngxtop -l <file> -i 'request.startswith(\"POST\") and status >= 400'")
print("\n请输入自定义ngxtop命令参数 (用空格分隔):")
custom_args = input("> ").strip().split()
if custom_args:
run_ngxtop(["-l", log_file, "--no-follow"] + custom_args)
else:
print("无效的选择,请重试")
input("\n按回车键继续...")
def main():
"""主函数"""
if len(sys.argv) < 2:
# 查找默认日志文件
log_files = list(Path(".").glob("*.txt"))
if log_files:
default_log = str(log_files[0])
print(f"未指定日志文件,使用默认: {default_log}")
log_file = default_log
else:
print("用法: python analyze_with_ngxtop.py <日志文件路径>")
print("示例: python analyze_with_ngxtop.py 11-08_org.txt")
sys.exit(1)
else:
log_file = sys.argv[1]
analyze_post_requests(log_file)
if __name__ == "__main__":
main()
运行命令:
python3 analyze_with_ngxtop.py 11-08_org.txt
分析结果:
【按URL统计POST请求 (Top 20)】 ------------------------------------------------------------ running for 7 seconds, 23670 records processed: 3508.50 req/sec Summary: | count | avg_bytes_sent | 2xx | 3xx | 4xx | 5xx | |---------+------------------+-------+-------+-------+-------| | 23670 | 2381.924 | 4678 | 21 | 18574 | 397 | Detailed: | request_path | count | avg_bytes_sent | 2xx | 3xx | 4xx | 5xx | |---------------------------------+---------+------------------+-------+-------+-------+-------| | /wp-cron.php | 16454 | 731.309 | 3413 | 0 | 13034 | 7 | | /xmlrpc.php | 3102 | 416.754 | 248 | 0 | 2853 | 1 | | /wp-login.php | 2519 | 15204.250 | 0 | 0 | 2519 | 0 | | /wp-admin/admin-ajax.php | 1017 | 542.043 | 971 | 0 | 44 | 2 | | /wp-comments-post.php | 401 | 2551.357 | 0 | 14 | 0 | 387 | | /xmrpc.php | 41 | 915.000 | 0 | 0 | 41 | 0 | | /tslogin | 20 | 30543.150 | 16 | 4 | 0 | 0 | | /alfacgiapi/perl.alfa | 11 | 51292.455 | 0 | 0 | 11 | 0 | | /ALFA_DATA/alfacgiapi/perl.alfa | 11 | 51323.636 | 0 | 0 | 11 | 0 | | /index.php | 10 | 34570.900 | 10 | 0 | 0 | 0 | | /wp-plain.php | 9 | 1331.000 | 0 | 0 | 9 | 0 | | / | 9 | 28609.556 | 7 | 0 | 2 | 0 | | | 8 | 415.000 | 8 | 0 | 0 | 0 | | /flow.php | 7 | 915.000 | 0 | 0 | 7 | 0 | | /wp-admin/async-upload.php | 5 | 736.000 | 5 | 0 | 0 | 0 | | /php-cgi/php-cgi.exe | 4 | 33911.500 | 0 | 0 | 4 | 0 | | /graphql | 4 | 33469.750 | 0 | 0 | 4 | 0 | | /wp-admin/post.php | 3 | 5.000 | 0 | 3 | 0 | 0 | | /member/success.aspx | 2 | 16784.500 | 0 | 0 | 2 | 0 | | /e/aspx/upload.aspx | 2 | 16628.500 | 0 | 0 | 2 | 0 | 【按IP统计POST请求 (Top 20)】 ------------------------------------------------------------ running for 7 seconds, 23670 records processed: 3586.40 req/sec Summary: | count | avg_bytes_sent | 2xx | 3xx | 4xx | 5xx | |---------+------------------+-------+-------+-------+-------| | 23670 | 2381.924 | 4678 | 21 | 18574 | 397 | Detailed: | remote_addr | count | avg_bytes_sent | 2xx | 3xx | 4xx | 5xx | |----------------+---------+------------------+-------+-------+-------+-------| | 221.204.26.162 | 4407 | 696.960 | 1125 | 1 | 3279 | 2 | | 221.204.26.233 | 4291 | 738.947 | 1054 | 1 | 3235 | 1 | | 101.71.101.44 | 3168 | 686.088 | 911 | 4 | 2252 | 1 | | 101.71.101.106 | 2564 | 868.693 | 183 | 2 | 2379 | 0 | | 43.174.53.229 | 2094 | 7795.611 | 6 | 0 | 2088 | 0 | | 43.174.53.236 | 2090 | 7811.496 | 4 | 0 | 2086 | 0 | | 114.66.247.160 | 1810 | 743.818 | 520 | 1 | 1288 | 1 | | 114.66.246.149 | 1123 | 507.375 | 538 | 1 | 582 | 2 | | 101.71.105.47 | 104 | 574.404 | 57 | 0 | 47 | 0 | | 43.175.19.192 | 29 | 5430.241 | 1 | 0 | 15 | 13 | | 43.175.17.169 | 26 | 2520.500 | 0 | 0 | 8 | 18 | | 43.175.18.81 | 25 | 2049.720 | 1 | 0 | 6 | 18 | | 43.175.18.253 | 25 | 1835.800 | 1 | 0 | 8 | 16 | | 43.175.18.195 | 25 | 5997.720 | 0 | 0 | 8 | 17 | | 43.175.18.137 | 25 | 2101.840 | 1 | 0 | 5 | 19 | | 43.175.17.87 | 24 | 2210.208 | 0 | 0 | 5 | 19 | | 43.175.17.47 | 23 | 7488.043 | 0 | 0 | 9 | 14 | | 43.175.18.51 | 22 | 3213.455 | 0 | 0 | 8 | 14 | | 43.175.17.205 | 21 | 7011.381 | 1 | 0 | 10 | 10 | | 43.175.169.137 | 16 | 1386.562 | 3 | 0 | 6 | 7 |
而至于这些 IP 地址,多数都是国内的,这个倒是也在意料之内,毕竟国外的被拦截的概率会更高一些。
然而,goaccess 就无法分析吗?也可以,添加忽略请求参数的参数就可以了:
#!/bin/bash
# 使用goaccess的--no-query-string参数移除查询参数
# 不需要修改日志文件!
LOG_FILE="${1:-11-08_org.txt}"
OUTPUT_FILE="${2:-goaccess_no_query_report.html}"
if [ ! -f "$LOG_FILE" ]; then
echo "错误: 日志文件 $LOG_FILE 不存在"
exit 1
fi
echo "=========================================="
echo "使用GoAccess分析(移除查询参数)"
echo "=========================================="
echo "日志文件: $LOG_FILE"
echo "输出文件: $OUTPUT_FILE"
echo ""
echo "使用参数: --no-query-string (或 -q)"
echo "这将移除URL中的查询参数,只保留路径"
echo ""
# 使用--no-query-string参数
goaccess "$LOG_FILE" \
--log-format='%h %^[%d:%t %^] "%r" %s %b "%R" "%u"' \
--date-format='%d/%m/%Y' \
--time-format='%H:%M:%S' \
--no-query-string \
-o "$OUTPUT_FILE"
if [ $? -eq 0 ]; then
echo ""
echo "✅ 报告生成成功: $OUTPUT_FILE"
echo ""
echo "现在wp-cron.php应该能正确合并统计了!"
echo ""
echo "在浏览器中打开报告查看:"
echo " open $OUTPUT_FILE # macOS"
echo " xdg-open $OUTPUT_FILE # Linux"
echo ""
echo "在交互界面中使用:"
echo " goaccess $LOG_FILE \\"
echo " --log-format='%h %^[%d:%t %^] \"%r\" %s %b \"%R\" \"%u\"' \\"
echo " --date-format='%d/%m/%Y' \\"
echo " --time-format='%H:%M:%S' \\"
echo " --no-query-string"
else
echo "❌ 报告生成失败"
exit 1
fi
主要就是:–no-query-string参数。
实际效果:
文件没改名之前:
文件改名之后:
虽然加起来之后不到两万次,但是却让 mysql 把 cpu 资源耗尽了,这的确不失为一个低成本的攻击方式。
爬虫占比:
这几天也不知道爬虫是发什么疯
今天的访问量:
百度的统计:
咱就是说,有点时间干点正事不好吗?真是闲的。











37 comments
wp真好玩
要不要尝试一下,乐趣多多
感觉被攻击都是优秀的站点。哈哈。
不错。哪天被攻击了可以试试。
各种攻击也是与时俱进,服了
遇到这种情况就会感叹,访问量大有大的烦恼、少有少的好处了
嗐,我这纯粹就是受害者
WP最容易被攻击吧,我用小众程序就很少遇到这种情况。
是的,wp 的用户还是太多了
各种攻击防不胜防。
是哒 关键是闲的蛋疼的傻屌实在太多了
这种统计很好玩,我的网站好久没人打过了,可能对原创个人程序别人是不会打的,打之前扫描不到典型文件
你这也算是自建的优势
这种应该不算攻击,就是有爬虫爬到了程序,然后后面的大量的爬虫跟着一起要攻克这几个文件吧,应该不是主动打的。 在服务器的防火墙里面增加基础的限制,访问这个超过几文件的爬虫ip直接ban掉就行。 这些目录和文件每天很多爬虫再爬,只要是外部链接多的wp老博客,几乎都能检查到,但是更新后台版本这些其实无所谓的,wp新版都修复这些,纯纯针对不升级的老系统攻击多。
谁家正经爬虫会发 post 请求~~
我这个措辞 可能不太准确,应该是漏洞爬虫。主要是伪装后爬各种漏洞文件,然后黑进系统,应该在waf或者cdn层面就能直接过滤掉,写个过滤规则就行。爬你多,可能是你的外链多,摸着链接就来了,各处评论区都能看到你,你的外部链链至少是我的一百倍。。。。。
这些 ip 其实感觉都不像家宽,前两个是同一个机房的,后面的是有个什么安全云 cdn,还有个联通的。但是这这些 ip 基本都是联通的。
这种扫描方式,如果是家宽或者境外的可以理解,但是国内的 ip 用来干这些事情有些傻逼了。
那就很离谱了,机房ip干这个,难道机房被漏洞拿下?不然不可能这样大规模的来流量干这种吃力不讨好事,这个多少有点离谱。有没有可能是哪家训练的ai爬虫被滥用了😳理解不了。
以 221.204.26.162 这个ip 为例,这个东西,最开始感觉还是正常的,不外乎就是各种下载图片,包括 09 年的图片都在下载,但是,最后就变成了疯狂的 post,去掉了其他的所有的请求。

如果说是扫描器或者 ai。通常不会伪装的这么彻底,请求没有系统信息,没有 ua 信息。没有任何请求信息,出了 post。并且请求频率比较稳定,怀疑是存在学习能力,在探测那种攻击效果更好,逐步停止掉那些无效攻击和请求,最后只保留这个 post,降低了请求量,但是数据库会消耗掉所有的 cpu 资源,这种攻击是效率最高的。
确实这种效率高,因为见过一次类似的日志。Cpu会拉爆,量大了服务器网站就会被干瘫痪,mysql会被查到暂停,这种简单粗暴的,就是纯攻击了,和爬虫无关了。你配置足够大,小点服务器早瘫了。
我刚翻了下日志 发现自己可能错了 由于前端套了cdn所以nginx记录的这个ip是错的,大概率是eo的节点ip。大意了,日志记录没改,现在没法溯源了。
至于源ip大概率还是比较分散的。
国内攻击IP很多都是机房啦,通过脚本批量获取试用机,月抛机进行攻击攻击成本几乎趋近于0
嗯嗯 也有可能 不过 现在我发现自己可能错了 ngjnx记录没改,记录的可能是cdn节点ip😂
从昨天到现在已经 39 万次了,如果是普通扫描器早就停了。
我的404响应记录里,95%都是wp相关的请求,还好我没用wp
那这就是被扫描器扫啦,沙雕脚本小子太多了,防不胜防
所以灵妹妹好久下定决心换掉WP?
换也很麻烦啊
我直接禁止公网访问cron.php了,negix直接返回444
嗯嗯 是得禁掉
哈哈,你这个比较少见,现在面板都是自带防火墙可以限制访问频率,所以反而小白基本遇不到单ip缓存穿透的情况
说起来我前天也遇到一个神人,挑了个凌晨给我eo刷流量,就一个3m的gif给我刷了33G…
是哒 有那种摁着文件刷下载的 我也遇到过
攻防战!wp很多容易被扫,然后根据wp特征进行攻击。
是的 研究wp的人太多了
有个地区一直在慢慢刷我流量直接禁了,都禁了半年了今天放出来,一看后台还在慢慢刷流量,这你不得不佩服,这种干其他的干啥干不成
是哒 有缓慢刷量的 贼烦
我就一个人自己记录的wp博客都被国外的攻击了很多次,闲得很他们
是的 无聊的人太多了