内容简介:关于 TCP 建立连接和断开连接的流程,很多人都能大致说出来,可以参考正常的数据传输是在三次握手结束之后进行的,但是如果打破了这个流程,数据传输仍然可能成功,而部分防火墙 IDS 就可能被绕过,下面的两个例子来自 https://github.com/kirillwow/ids_bypass。Suricata IDS 在 4.0.4 版本之前存在这个问题
关于 TCP 建立连接和断开连接的流程,很多人都能大致说出来,可以参考 协议森林
正常的数据传输是在三次握手结束之后进行的,但是如果打破了这个流程,数据传输仍然可能成功,而部分防火墙 IDS 就可能被绕过,下面的两个例子来自 https://github.com/kirillwow/ids_bypass。
CVE-2018-6794
# 客户端开始三次握手 发送 SYN Client -> [SYN] [Seq=0 Ack=0] -> Evil Server # 服务器端正常的响应了 SYN-ACK Client <- [SYN, ACK] [Seq=0 Ack=1] <- Evil Server # 但是服务器端在握手结束之前就发送了 PSH,里面包含了一些数据 Client <- [PSH, ACK] [Seq=1 Ack=1] <- Evil Server # 服务器端主动关闭了连接 Client <- [FIN, ACK] [Seq=83 Ack=1] <- Evil Server # 三次握手完成 Client -> [ACK] [Seq=1 Ack=84] -> Evil Server # 客户端正常的发送数据 Client -> [PSH, ACK] [Seq=1 Ack= 4] -> Evil Server
Suricata IDS 在 4.0.4 版本之前存在这个问题
RST 导致的绕过
有些 Windows 客户端在收到 RST 包之后,如果紧接着又收到了其他的 TCP 数据,那仍然是可以读取和处理的,有些 IDS 正确处理了这个问题,有的在收到 RST 包之后就停止了检查 TCP 包。
# Client starts a TCP 3-way handshake Client -> [SYN] [Seq=0 Ack=0] -> Evil Server # Server responses with TCP RST Client <- [RST, ACK] [Seq=0x0 Ack=1] <- Evil Server # And SYN-ACK shortly after RST Client <- [SYN, ACK] [Seq=1 Ack=1] <- Evil Server ... 三次握手继续 ...
Suricata IDS(全版本?)存在这个问题。对于 UDP 数据包,也有一个类似的问题。
应用
某些云服务器厂商会实时的去过滤每台机器的 HTTP 请求的域名,也就是 Host 字段,一旦发现是没有[[(备)]]案的,就会返回一个拦截页面,怎么绕过这个呢。经过测试发现某云应该是不检测 HTTPS的,如果可以让 80 端口重定向到 443,然后设置 HSTS 头,这样基本长时间内浏览器就不会再访问 80 端口了,虽然 SSL SNI 和 证书中也是含有域名信息的。
访问 80 端口,发现三次握手是正常进行的,而拦截发生在客户端发送了 HTTP 请求包之后,这也说明,防火墙不是无条件封禁的和屏蔽端口的,而是实时的过滤。如果可以抢在防火墙发包之前发送,那就可以实现重定向了。
写了一个 Python 的脚本来完成这个事情
# coding=utf-8 from scapy.all import IP, TCP, send, sniff SERVER_DOMAIN = "example.me" SERVER_PORT = 4445 FIN = 0x01 SYN = 0x02 ACK = 0x10 def build_synack(syn): seq = 1 # 确认 SYN ack = syn[TCP].seq + 1 ip = IP(src=syn[IP].dst, dst=syn[IP].src) tcp = TCP( sport=syn[IP].dport, dport=syn[TCP].sport, flags="SA", seq=seq, ack=ack, options=[("MSS", 1460)] ) return ip / tcp def build_finack(syn): """ 带重定向指令的包 """ seq = 2 ack = syn[TCP].seq + 1 ip = IP(src=syn[IP].dst, dst=syn[IP].src) tcp = TCP( sport=syn[IP].dport, dport=syn[TCP].sport, flags="FA", seq=seq, ack=ack, options=[("MSS", 1460)] ) resp = b"HTTP/1.1 307 Internal Redirect\r\n" \ b"Content-Length: 0\r\n" \ b"Location: https://%s:443\r\n" \ b"Strict-Transport-Security: max-age=31536000\r\n" \ b"\r\n" % SERVER_DOMAIN return ip / tcp / resp def build_ack(p): seq = 3 ack = p[TCP].seq + 1 ip = IP(src=p[IP].dst, dst=p[IP].src) tcp = TCP( sport=p[IP].dport, dport=p[TCP].sport, flags="A", seq=seq, ack=ack, options=[("MSS", 1460)] ) return ip / tcp def handle_packet(p): # 如果是 SYN 就回复 SYN-ACK 和 FIN-ACK if p[TCP].flags & SYN and not p[TCP].flags & ACK: send(build_synack(p)) print("SYN ACK sent") send(build_finack(p)) send("FIN ACK sent") elif p[TCP].flags & FIN and p[TCP].flags & ACK: # 如果不 ACK,客户端可能一直重传 send(build_ack(p)) send("ACK sent") if __name__ == "__main__": # 对于 TCP 和 SERVER PORT 端口的包,回调 handle_packet 函数 sniff(filter="tcp and port %d" % SERVER_PORT, prn=handle_packet)
使用 scapy 框架,监听一个端口,在接收到 SYN 包之后,按照正常的握手流程返回 SYN-ACK,然后不等接收到 ACK 就继续发送 FIN-ACK,告诉客户端我要断开连接了,然后在这个包中包含有重定向的 HTTP 包。
在服务器端视角看是这样的
在客户端视角看是这样的
42 号包是代码的重定向,47 号包就是防火墙的重定向,可以看到 TTL 明显不一致,而且 seq 被我们代码扰乱,导致被认为 out-of-order 了。
因为 scapy 是用户态的,防止内核不知道整个连接流程而发送 rst 包,可以使用下面的命令屏蔽掉
iptables -A OUTPUT -p tcp --tcp-flags RST RST -s 172.21.0.3 -j DROP
也有人使用内核模块实现了这个功能
https://github.com/ptpt52/hstshack
以上所述就是小编给大家介绍的《不按顺序来的 TCP 包》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- ViewGroup 默认顺序绘制子 View,如何修改?什么场景需要修改绘制顺序?
- JavaScript万物产生顺序
- SpringBoot配置加载顺序
- SQL语句执行顺序详解
- golang 初始化顺序
- Python 模块的加载顺序
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Introduction to Computation and Programming Using Python
John V. Guttag / The MIT Press / 2013-7 / USD 25.00
This book introduces students with little or no prior programming experience to the art of computational problem solving using Python and various Python libraries, including PyLab. It provides student......一起来看看 《Introduction to Computation and Programming Using Python》 这本书的介绍吧!