攻击者通过精心构造的TCP序列号攻击和恶意标志组合绕过防火墙DPI检测,核心手法如下:
TCP连接建立(正常握手)
45 00 00 34 d5 f8 40 00 40 06 00 00 c0 a8 00 18 ac fd 72 5a
45 00 00 34 a0 8a 00 00 32 06 08 22 ac fd 72 5a c0 a8 00 18
45 00 00 28 d5 fa 40 00 40 06 00 00 c0 a8 00 18 ac fd 72 5a
TLS握手拆分攻击
1055:发送 TLS ClientHello头部(4字节)
PUSH+ACK
16 03 01 02
(TLS握手头,版本TLS1.0,长度512字节)45 00 00 2c d5 fb 40 00 40 06 00 00 c0 a8 00 18 ac fd 72 5a
1056:插入 恶意MD5-SIG报文
PUSH+ACK
→ 强制DPI流控重置状态45 00 02 2d d5 fb 40 00 40 06 00 00 c0 a8 00 18 ac fd 72 5a
1057:发送 ClientHello主体(130字节)
ACK
(无PUSH)→ 伪装控制包77 77 77 2e 67 6f 6f
(“www.goo”)45 00 00 aa d5 fb 40 00 40 06 00 00 c0 a8 00 18 ac fd 72 5a
00 01 00 01 fc 03 03 fc 9e d4 bd e7 e3 e2 90 12 79 b7 4b 68...
1058:重传 1056恶意包
45 00 02 2d d5 fb 40 00 40 06 00 00 c0 a8 00 18 ac fd 72 5a
1059:发送 ClientHello剩余部分(383字节)
67 6c 65 2e 63 6f 6d
(“gle.com” → “www.google.com”)45 00 01 a7 d5 fb 40 00 40 06 00 00 c0 a8 00 18 ac fd 72 5a
67 6c 65 2e 63 6f 6d 00 17 00 00 ff 01 00 01 00...
攻击原理
防火墙漏洞根源
TCP流控缺陷
PUSH
标志判断数据边界 → 被 ACK without PUSH
(1057)绕过重组机制缺失
02 00
=512字节)验证完整性协议处理漏洞
IF (TCP.FLAGS & TCP_PUSH) do…; PSH-only 漏洞
模块 | 修复措施 | 防御目标 |
---|---|---|
流重组引擎 | 实现状态化TCP重组 | 防止序列号跳跃攻击(1056插入) |
- 缓冲数据直到应用层消息完整 | 重组被拆分的ClientHello | |
- 严格验证SEQ/ACK连续性 | ||
标志处理 | 取消对PUSH标志的依赖 | 防御1057帧伪装控制包 |
- 基于应用层长度判断消息边界 | ||
- 验证标志组合合理性 | ||
选项过滤 | 默认丢弃MD5-SIG选项包 | 阻止1056/1058攻击载体 |
if (tcp.option == MD5-SIG) DROP |
在TCP协议中:
ACK
(最常见)PSH|ACK
(带数据的推送)RST|ACK
(连接重置)FIN|ACK
(连接终止)丢弃规则:
验证逻辑伪代码:
if (tcp.flags == PSH && !(tcp.flags & ACK)) {
log("Invalid PSH-only packet", packet);
DROP;
}
if (connection_state == ESTABLISHED && !(tcp.flags & ACK)) {
log("Non-ACK packet in established connection", packet);
DROP;
}
异常标志组合 | 风险等级 | 处理措施 | 攻击防护目标 |
---|---|---|---|
PSH-only | 高危 | 立即丢弃 | 防止强制刷新攻击 |
Non-ACK | 高危 | 丢弃并记录日志 | 防止状态机污染 |
**SYN | PSH** | 中危 | 丢弃 |
FIN-only | 中危 | 丢弃 | 防止连接异常终止 |
RST-only | 高危 | 丢弃并生成警报 | 防止拒绝服务攻击 |
ACK-only | 低危 | SEQ验证+流重组 | 防止伪装控制包(如1057) |
PSH/ACK | 正常 | 延迟处理至应用层消息完整 | 防TLS拆分绕过 |
测试用例 | 测试场景 | 测试包构造 | 预期结果 | 验证指标 |
---|---|---|---|---|
PSH-only攻击 | 基本漏洞验证 | flags=PSH 无负载 |
立即丢弃 | 1. 防火墙丢弃计数增加 2. 无审计日志记录 3. 目标主机无接收 |
合法PSH+ACK | 正常业务验证 | flags=PSH+ACK 含TLS ClientHello |
正常处理 | 1. SNI成功提取 2. 审计日志完整记录 3. 应用层正常响应 |
混合标志攻击 | 高级绕过尝试 | flags=PSH+URG flags=PSH+SYN |
立即丢弃 | 1. 异常标志告警触发 2. 连接成功率=0% 3. CPU占用<5% |
洪水攻击 | 压力测试 | 1000+ PSH-only包 10Gbps速率 |
全部丢弃 | 1. 吞吐量保持>9.5Gbps 2. 内存波动<2% 3. 无漏包 |
渗透测试 | 真实攻击模拟 | 使用Metasploit模块:auxiliary/scanner/portscan/tcp_psh |
完全阻断 | 1. IDS告警率100% 2. 扫描成功率=0% 3. 无异常连接 |
def handle_tcp_packet(packet):
# 丢弃所有PSH-only包
if packet.flags == TCP.PSH:
log("Invalid PSH-only packet", packet)
return DROP
# 在已建立连接中丢弃所有non-ACK包
if connection.state == ESTABLISHED and not (packet.flags & TCP.ACK):
log("Non-ACK packet in established connection", packet)
return DROP
# 特殊标志组合处理
if packet.flags & TCP.SYN and packet.flags & TCP.PSH:
log("Suspicious SYN|PSH packet", packet)
return DROP
# ACK-only包需要严格SEQ验证
if packet.flags == TCP.ACK:
if not validate_seq(packet.seq, expected_seq):
return handle_out_of_order(packet)
# PSH|ACK包进入重组缓冲区
if packet.flags == (TCP.PSH | TCP.ACK):
buffer_packet(packet)
if is_application_message_complete():
process_application_layer()
return PROCESS
“The ACK bit MUST be set in all packets after the initial SYN packet, unless the RST bit is set.”
(在初始SYN包之后的所有数据包中,除非设置了RST位,否则必须设置ACK位)
# TLS重组伪代码
def handle_tls_packet(packet):
session = get_session(packet.stream_id)
# 验证SEQ连续性(防御1056插入)
if packet.seq != session.expected_seq:
if not is_valid_tls_header(packet.data): # 检查0x16握手标识
return DROP # 丢弃插入包
session.buffer += packet.data
# 检查TLS头完整性(前5字节)
if len(session.buffer) >= 5:
tls_len = (session.buffer[3] << 8) + session.buffer[4] + 5
# 缓冲完整消息(防御1055/1057/1059拆分)
if len(session.buffer) >= tls_len:
process_clienthello(session.buffer[:tls_len]) # 提取SNI
session.buffer = session.buffer[tls_len:]
# 设置超时(防御阻塞攻击)
session.timer = set_timeout(500ms, flush_buffer)
class SeqTracker:
def __init__(self):
self.expected_seq = None # 预期序列号
self.reassembly_buffer = [] # 重组缓冲区
self.max_gap = 8192 # 最大允许序列号间隔
def process_packet(self, packet):
# 初始序列号设置
if self.expected_seq is None:
self.expected_seq = packet.seq + len(payload)
return PROCESS
# 计算序列号差距
seq_gap = packet.seq - self.expected_seq
# 完美连续包
if seq_gap == 0:
self.expected_seq += len(packet.payload)
return PROCESS
# 可接受范围内的乱序包
if 0 < seq_gap <= self.max_gap:
self._buffer_packet(packet)
return BUFFERED
# 恶意序列号跳跃 (如1056插入)
if seq_gap > self.max_gap:
log_alert(f"Sequence jump attack detected: gap={seq_gap}")
return DROP
# 重复或旧包 (如1058重传)
if seq_gap < 0:
return DUP
def _buffer_packet(self, packet):
# 按序列号插入缓冲区
bisect.insort(self.reassembly_buffer, packet, key=lambda p: p.seq)
# 检查连续性
self._check_continuity()
def _check_continuity(self):
# 尝试重组连续数据
next_seq = self.expected_seq
for p in sorted(self.reassembly_buffer, key=lambda x: x.seq):
if p.seq == next_seq:
self.reassembly_buffer.remove(p)
next_seq += len(p.payload)
else:
break
# 更新预期序列号
if next_seq > self.expected_seq:
self.expected_seq = next_seq
重传检测矩阵:
特征 | 正常重传 | 恶意重传 | 处理方式 |
---|---|---|---|
SEQ差异 | =0 | =0 | 需进一步检查 |
有效负载 | 相同 | 不同 | 标记恶意 |
时间间隔 | >RTO | <最小阈值(10ms) | 标记恶意 |
标志位 | 相同 | 异常组合 | 丢弃 |
防御规则:
1049:
0000 45 00 00 34 d5 f8 40 00 40 06 00 00 c0 a8 00 18
0010 ac fd 72 5a
1053:
0000 45 00 00 34 a0 8a 00 00 32 06 08 22 ac fd 72 5a
0010 c0 a8 00 18
1054:
0000 45 00 00 28 d5 fa 40 00 40 06 00 00 c0 a8 00 18
0010 ac fd 72 5a
1055:
0000 45 00 00 2c d5 fb 40 00 40 06 00 00 c0 a8 00 18
0010 ac fd 72 5a
1056:
0000 45 00 02 2d d5 fb 40 00 40 06 00 00 c0 a8 00 18
0010 ac fd 72 5a
1057:
0000 45 00 00 aa d5 fb 40 00 40 06 00 00 c0 a8 00 18
0010 ac fd 72 5a
1058:
0000 45 00 02 2d d5 fb 40 00 40 06 00 00 c0 a8 00 18
0010 ac fd 72 5a
1059:
0000 45 00 01 a7 d5 fb 40 00 40 06 00 00 c0 a8 00 18
0010 ac fd 72 5a
1067:
0000 01 bb 05 ea b3 21 4d 70 f2 2a 75 ec 80 12 ff ff
0010 1e b8 00 00 02 04 05 96 01 01 04 02 01 03 03 08
1068:
0000 01 bb 05 ea b3 21 4d 70 f2 2a 75 ec 80 12 ff ff
0010 1e b8 00 00 02 04 05 96 01 01 04 02 01 03 03 08
1069:
0000 45 00 00 34 d5 fe 40 00 40 06 00 00 c0 a8 00 18
0010 ac fd 72 5a
1070:
0000 45 00 00 28 a1 39 00 00 32 06 07 7f ac fd 72 5a
0010 c0 a8 00 18
1071:
0000 45 00 00 28 a1 3a 00 00 32 06 07 7e ac fd 72 5a
0010 c0 a8 00 18
1072:
0000 45 00 00 28 a1 3b 00 00 32 06 07 7d ac fd 72 5a
0010 c0 a8 00 18
1073:
0000 01 bb 05 ea b3 21 4d 71 f2 2a 77 f1 50 10 01 09
0010 dc 87 00 00
1074:
0000 45 00 05 be a1 3d 00 00 32 06 01 e5 ac fd 72 5a
0010 c0 a8 00 18