我有一台装有 apache 的服务器,最近我安装了 mod_security2,因为我经常受到以下攻击:
我的 apache 版本是 apache v2.2.3,我使用 mod_security2.c
这是错误日志中的条目:
[Wed Mar 24 02:35:41 2010] [error]
[client 88.191.109.38] client sent HTTP/1.1 request without hostname
(see RFC2616 section 14.23): /w00tw00t.at.ISC.SANS.DFind:)
[Wed Mar 24 02:47:31 2010] [error]
[client 202.75.211.90] client sent HTTP/1.1 request without hostname
(see RFC2616 section 14.23): /w00tw00t.at.ISC.SANS.DFind:)
[Wed Mar 24 02:47:49 2010] [error]
[client 95.228.153.177] client sent HTTP/1.1 request without hostname
(see RFC2616 section 14.23): /w00tw00t.at.ISC.SANS.DFind:)
[Wed Mar 24 02:48:03 2010] [error]
[client 88.191.109.38] client sent HTTP/1.1 request without hostname
(see RFC2616 section 14.23): /w00tw00t.at.ISC.SANS.DFind:)
以下是来自 access_log 的错误:
202.75.211.90 - -
[29/Mar/2010:10:43:15 +0200]
"GET /w00tw00t.at.ISC.SANS.DFind:) HTTP/1.1" 400 392 "-" "-"
211.155.228.169 - -
[29/Mar/2010:11:40:41 +0200]
"GET /w00tw00t.at.ISC.SANS.DFind:) HTTP/1.1" 400 392 "-" "-"
211.155.228.169 - -
[29/Mar/2010:12:37:19 +0200]
"GET /w00tw00t.at.ISC.SANS.DFind:) HTTP/1.1" 400 392 "-" "-"
我尝试像这样配置 mod_security2:
SecFilterSelective REQUEST_URI "w00tw00t\.at\.ISC\.SANS\.DFind"
SecFilterSelective REQUEST_URI "\w00tw00t\.at\.ISC\.SANS"
SecFilterSelective REQUEST_URI "w00tw00t\.at\.ISC\.SANS"
SecFilterSelective REQUEST_URI "w00tw00t\.at\.ISC\.SANS\.DFind:"
SecFilterSelective REQUEST_URI "w00tw00t\.at\.ISC\.SANS\.DFind:\)"
mod_security2 中的问题是无法使用 SecFilterSelective,它会给出错误。相反,我使用如下规则:
SecRule REQUEST_URI "w00tw00t\.at\.ISC\.SANS\.DFind"
SecRule REQUEST_URI "\w00tw00t\.at\.ISC\.SANS"
SecRule REQUEST_URI "w00tw00t\.at\.ISC\.SANS"
SecRule REQUEST_URI "w00tw00t\.at\.ISC\.SANS\.DFind:"
SecRule REQUEST_URI "w00tw00t\.at\.ISC\.SANS\.DFind:\)"
即使这样也不起作用。我不知道该怎么办了。有人有什么建议吗?
更新 1
我发现没有人能用 mod_security 解决这个问题。到目前为止,使用 ip-tables 似乎是最好的选择,但我认为文件会变得非常大,因为 ip 一天会改变好几次。
我想出了另外 2 个解决方案,有人可以评论一下它们是否好吗?
我想到的第一个解决方案是从我的 Apache 错误日志中排除这些攻击。这将使我更容易发现其他紧急错误,而不必查看长日志。
我认为第二个选项更好,即阻止未以正确方式发送的主机。在此示例中,w00tw00t 攻击是在没有主机名的情况下发送的,因此我认为我可以阻止格式不正确的主机。
更新 2
在查看了这些答案之后,我得出了以下结论。
为 apache 进行自定义日志记录将会消耗一些不必要的资源,如果确实存在问题,您可能希望查看完整的日志而没有任何遗漏。
最好忽略这些命中,集中精力寻找更好的方法来分析错误日志。使用日志过滤器是一种很好的方法。
关于这个主题的最终想法
如果您至少拥有最新的系统,上述攻击将不会影响到您的机器,因此基本上无需担心。
一段时间后,很难从真正的攻击中过滤出所有的虚假攻击,因为错误日志和访问日志都会变得非常大。
以任何方式防止这种情况发生都会耗费您的资源,因此不要将您的资源浪费在不重要的事情上是一种很好的做法。
我现在使用的解决方案是Linux 日志监视。它会向我发送日志摘要,并对其进行筛选和分组。这样,您可以轻松地区分重要和不重要的内容。
谢谢大家的帮助,我希望这篇文章也能对其他人有所帮助。
答案1
从您的错误日志来看,他们发送的 HTTP/1.1 请求没有请求的 Host: 部分。据我所知,Apache 在移交给 mod_security 之前,会用 400(错误请求)错误回复此请求。因此,您的规则似乎不会被处理。(Apache 在要求移交给 mod_security 之前会处理它)
尝试一下:
telnet 主机名 80 GET /blahblahblah.html HTTP/1.1 (输入) (进入)
您应该会收到 400 错误,并在日志中看到相同的错误。这是一个错误的请求,而 Apache 给出了正确的答案。
正确的请求应该是这样的:
获取 /blahblahblah.html HTTP/1.1 主办方:blah.com
解决此问题的一个方法是修补 mod_uniqueid,即使请求失败,也可以生成唯一 ID,以便 apache 将请求传递给其请求处理程序。以下 URL 是关于此解决方法的讨论,并包含您可以使用的 mod_uniqueid 补丁: http://marc.info/?l=mod-security-users&m=123300133603876&w=2
找不到任何其他解决方案,想知道是否确实需要解决方案。
答案2
在我看来,过滤 IP 不是一个好主意。为什么不尝试过滤您知道的字符串呢?
我是说:
iptables -I INPUT -p tcp --dport 80 -m string --to 60 --algo bm --string 'GET /w00tw00t' -j DROP
答案3
我也开始在我的日志文件中看到这些类型的消息。防止这些类型攻击的一种方法是设置fail2ban(http://www.fail2ban.org/) 并在 iptables 规则中设置特定的过滤器以将这些 IP 地址列入黑名单。
以下是一个过滤器的示例,该过滤器将阻止与发送这些消息相关的 IP 地址
[2011 年 8 月 16 日星期二 02:35:23] [错误] [客户端] 文件不存在:/var/www/skraps/w00tw00t.at.blackhats.romanian.anti-sec:) === apache w00t w00t messages jail - 正则表达式和过滤器 === Jail
[apache-wootwoot]
enabled = true
filter = apache-wootwoot
action = iptables[name=HTTP, port="80,443", protocol=tcp]
logpath = /var/log/apache2/error.log
maxretry = 1
bantime = 864000
findtime = 3600
筛选
# Fail2Ban configuration file
#
# Author: Jackie Craig Sparks
#
# $Revision: 728 $
#
[Definition]
#Woot woot messages
failregex = ^\[\w{1,3} \w{1,3} \d{1,2} \d{1,2}:\d{1,2}:\d{1,2} \d{1,4}] \[error] \[client 195.140.144.30] File does not exist: \/.{1,20}\/(w00tw00t|wootwoot|WootWoot|WooTWooT).{1,250}
ignoreregex =
答案4
我亲自编写了一个 Python 脚本来自动添加 IPtables 规则。
这是一个略微简化的版本,没有日志记录和其他垃圾:
#!/usr/bin/python
from subprocess import *
import re
import shlex
import sys
def find_dscan():
p1 = Popen(['tail', '-n', '5000', '/usr/local/apache/logs/error_log'], stdout=PIPE)
p2 = Popen(['grep', 'w00t'], stdin=p1.stdout, stdout=PIPE)
output = p2.communicate()[0].split('\n')
ip_list = []
for i in output:
result = re.findall(r"\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b", i)
if len(result):
ip_list.append(result[0])
return set(ip_list)
for ip in find_dscan():
input = "iptables -A INPUT -s " + ip + " -j DROP"
output = "iptables -A OUTPUT -d " + ip + " -j DROP"
Popen(shlex.split(input))
Popen(shlex.split(output))
sys.exit(0)