概述
TCP 协议的很多特性可以被攻击者广泛利用,如进行网络扫描和端口嗅探。基于 TCP 的常用的端口扫描类型包括:TCP 连接扫描、TCP SYN扫描、TCP Xmas 扫描、TCP FIN 扫描、TCP Null 扫描等等。
我们将使用 Scapy 第三方库来做实际的扫描测试。Scapy 可以用来做 package 嗅探和伪造 package,并且它已经在内部实现了大量的网络协议。
TCP 连接扫描
该方式尝试通过与目标主机上的待扫描 TCP 端口建立完整的 TCP 连接,根据连接的成败推断端口的工作状态。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| from scapy.all import * from scapy.layers.inet import TCP, IP
data = 'hello, world'
## 构造 package pkt = IP(src = '127.0.0.1', dst = '127.0.0.1')/TCP(sport=12345, dport=8088)/data resp = sr1(pkt, timeout=10) if str(type(resp)) == '': print("Closed") elif resp.haslayer(TCP): if resp.getlayer(TCP).flags == 0x12: # SYN+ACK # 发送 ACK sr(IP(src = '127.0.0.1', dst = '127.0.0.1')/TCP(sport=12345, dport=8088, flags="AR"), timeout=10) print("Opened") elif resp.getlayer(TCP).flags == 0x14: # RST+ACK print("Closed") else: print("Closed")
|
TCP SYN 扫描
该类型扫描是向主机上的待扫描端口发送一个 SYN 标志被设置为 1 的 TCP 报文, 此动作与 TCP 的三次握手中的第一阶段相同。如果被扫描的端口处于监听状态,它将返回 SYN+ACK,响应连接请求
源主机在收到响应报文后,发送 RST 标志为 1 的报文,中断与主机的连接。如果被扫描主机的端口是关闭的,则会回复 RST 为 1 的报文。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| from scapy.all import * from scapy.layers.inet import TCP, IP
pkt = IP(src = '127.0.0.1', dst = '127.0.0.1')/TCP(sport=12345, dport=8088, flags="S") resp = sr1(pkt, timeout=10) if str(type(resp)) == "": print("Filtered") elif resp.haslayer(TCP): if resp.getlayer(TCP).flags == 0x12: # SYN+ACK # 发送 ACK sr(IP(src='127.0.0.1', dst='127.0.0.1') / TCP(sport=12345, dport=8088, flags="R"), timeout=10) print("Opened") elif resp.getlayer(TCP).flags == 0x14: # RST + ACK print("Closed") else: print("Filtered")
|
TCP Xmas 扫描(圣诞树扫描)
在该扫描类型中,源主机会向目标主机发送设置 PSH、FIN、URG 标识的数据包。判断逻辑如下:
- 如果目标主机的端口是开放的,不会有任何响应
- 如果目标主机返回了 RST 报文,那么说明端口处于关闭状态
- 如果目标主机返回一个 ICMP 数据包,其中错误类型为 3 且状态码为 1, 2, 3, 9, 10 或 13, 则说明目标端口被过滤了无法确定是否处于开放状态。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| from scapy.all import * from scapy.layers.inet import TCP, IP, ICMP
pkt = IP(src = '127.0.0.1', dst = '127.0.0.1')/TCP(sport=12345, dport=8088, flags="FPU") resp = sr1(pkt, timeout=5) if str(type(resp)) == "" or resp is None: print("Open|Filtered") elif resp.haslayer(TCP): if resp.getlayer(TCP).flags == 0x14: print("Closed") elif resp.haslayer(ICMP): if (int(resp.getlayer(ICMP).type) == 3 and int(resp.getlayer(ICMP).code) in [1, 2, 3, 9, 10, 13]): print("Filtered") else: print("Filtered")
|
TCP ACK 扫描
ACK 扫描不是用于发现端口开启或关闭状态的,而是用于发现服务器上是否存在有状态防火墙的。它的结果只能说明端口是否被过滤。
TCP 窗口扫描
TCP 窗口扫描的流程同 ACK 扫描类似,同样是客户端向服务器发送一个带有 ACK 标识和端口号的 TCP 数据包,但是这种扫描能够用于发现目标服务器端口的状态。在 ACK 扫描中返回 RST 表明没有被过滤,但在窗口扫描中,当收到返回的 RST 数据包后,它会检查窗口大小的值。
- 如果窗口大小的值是个非零值,则说明目标端口是开放的。
- 如果返回的 RST 数据包中的窗口大小为0,则说明目标端口是关闭的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| from scapy.all import * from scapy.layers.inet import TCP, IP, ICMP
pkt = IP(src = '127.0.0.1', dst = '127.0.0.1')/TCP(sport=12346, dport=8089, flags="A") resp = sr1(pkt, timeout=10) if str(type(resp)) == "" or resp is None: print("No Response") elif resp.haslayer(TCP): if resp.getlayer(TCP).window == 0: print("Closed") elif resp.getlayer(TCP).window > 0: print("Opened") elif resp.haslayer(ICMP): if (int(resp.getlayer(ICMP).type) == 3 and int(resp.getlayer(ICMP).code) in [1, 2, 3, 9, 10, 13]): print("Filtered") else: print("Filtered")
|
参考 端口扫描之开放端口扫描方式