iptables:仅匹配已建立 TCP 连接的第一个数据包

iptables:仅匹配已建立 TCP 连接的第一个数据包

在我的 Apache 日志文件中,我发现很多条目包含"GET /w00tw00t.at.ISC.SANS.DFind:) HTTP/1.1" 400或类似垃圾信息。它们来自非 RFC2616 连接(无主机名的 HTTP/1.1)。

我不想让我的日志文件充斥着这些消息。所以我想使用 iptables 拒绝这些连接。因此,我想在数据包485454502f312e310d0a0d0a负载中搜索字符串“HTTP/1.1”,后面跟着两个后续的 CR/LF(CR/LF/CR/LF)(总共给出十六进制字符串)。

但当我知道它就在第一个数据包中时,浪费 CPU 周期在所有 TPC 数据包中搜索此字符串是愚蠢的。这甚至是错误的,因为“HTTP/1.1”后跟两个后续 CR/LF 可能是 http 请求有效负载中传输的合法部分。

这里http://spamcleaner.org/en/misc/w00tw00t.html是该问题的解决方案,但我不明白标识已建立的 TCP 连接的第一个数据包的部分。

我不明白的是,为什么初始 TCP 握手的所有 3 个数据包(SYN、ACK+SYN、ACK)都可以在 INPUT 链或只能从 INPUT 到达的链中看到。据我对 iptables 及其链的了解,第二个数据包(ACK+SYN)永远不会通过 INPUT。我认为它通过了 OUTPUT,因为是我(即服务器)发送的。

这是来自 spamcleaner.org 的脚本,我只更改了脚本第一部分的一些注释,但所有命令保持不变:

#!/bin/bash

# allow loopback
iptables -A INPUT -i lo -j ACCEPT

# DROP any IP that is in the blacklist "w00tlist" and set the
# blacklist-timeout to 6 hour
iptables -A INPUT -p tcp -m recent --name w00tlist --update --seconds 21600 -j DROP

# create the chain "w00tchain"
iptables -N w00tchain

# this chain will add the IP to the blacklist "w00tlist"
# and will reset the connection:
iptables -A w00tchain -m recent --set --name w00tlist -p tcp \
-j REJECT --reject-with tcp-reset

# create another chain named "w00t". It's purpose is to identify the first packet
# of an newly established tcp-connection and to search for a string in it:
iptables -N w00t

# redirect all incoming (no outgoing!) TCP packets to the chain "w00t":
iptables -A INPUT -p tcp -j w00t

# all remaining rules are part of the chain "w00t"
#---------------------------------------------------------------
# all following comments in lowercase are unchanged from spamcleaner.org
# COMMENTS IN UPPERCASE ARE FROM ME
#---------------------------------------------------------------

# look for the SYN packet and create the list :
iptables -A w00t -m recent -p tcp --syn --dport 80 --set  

# look for the SYN,ACK packet and update the list :
iptables -A w00t -m recent -p tcp --tcp-flags PSH,SYN,ACK SYN,ACK --sport 80 --update
#---------------------------------------------------------------------------------
# THIS IS WHAT I DON'T UNDERSTAND:
# THE CHAIN w00t CAN ONLY BE REACHED FROM THE CHAIN INPUT. SO WE ARE DEALING HERE
# WITH PACKETS THAT THE CLIENT IS SENDING AND THAT THE SERVER IS RECEIVING. BUT IN
# STEP 2 OF TCP-HANDSHAKE ITS THE SERVER WHO IS SENDING AND THE CLIENT WHO IS
# RECEIVING. SO THE PACKET WITH SYN AND ACK SET AND WITH sport 80 GOES THROUGH THE
# CHAIN "OUTPUT", NOT "INPUT". SO HOW CAN IT BE DETECTED IN CHAIN w00t?
#---------------------------------------------------------------------------------

# look for the ACK packet and update the list :
iptables -A w00t -m recent -p tcp --tcp-flags PSH,SYN,ACK ACK --dport 80 --update

# look for the hexadecimal string in the first PSH+ACK.
# If found, redirect to w00tchain in order to blacklist the IP and
# to close the connection.
# Delete our list, we do not want to filter any further packet from that connection :
iptables -A w00t -m recent -p tcp --tcp-flags PSH,ACK PSH,ACK --dport 80 --remove \
-m string --to 80 --algo bm --hex-string '|485454502f312e310d0a0d0a|' -j w00tchain

还有第二件事我不明白:

最后一条规则是在设置了 PSH 和 ACK 标志的数据包中搜索十六进制字符串。但我如何确定我的数据包是否设置了 PSH?我不确定,但我认为发送未设置 PSH 标志的 TCP 数据包是可能的,也是合法的。

编辑:还有第三个问题:如果服务器同时从相同的 IP 地址通过 TCP 接收到两个以上 HTTP 请求(每个请求都有自己的端口号)会怎样?

答案1

忘记 IPTables。您可以简单地使用 mod_security 和 nolog 操作。类似这样(未经测试):

SecRule REQUEST_URI "^/w00tw00t\.at\.ISC\.SANS\.DFind" phase:1,nolog,deny,id:1000

或者您可以创建一个带有单独日志的虚拟主机,它只会拒绝所有请求并将其配置为第一个。不提供主机名或提供未知主机名的客户端将始终在那里结束。

相关内容