端口扫描工具

端口扫描工具

使用ICMP协议探测主机是否开启

代码

1
2
3
4
5
6
7
8
def ping(host):
print("Testing:", host)
pkt = IP(dst=host) / ICMP() / b'I\'m a ping packet '
reply = sr1(pkt, timeout=2, verbose=False) # 接收一个回复包
if reply:
print(f"{host} is up!")
else:
print(f"{host} is down!")

Ping请求数据包。它使用了Scapy库来构建网络数据包。

  • IP(dst=host): 这部分代码创建了一个IP数据包,其中 dst 参数设置为传递给函数的主机地址。这是Ping请求的目标主机地址
  • ICMP(): 这部分代码创建了一个ICMP包,用于发送Ping请求
  • b'I\'m a ping packet ': payload包含文本消息的字节序列

结果

对于开启的主机来说当接收到ICMP之后会进行响应,但是对于关闭的主机则没有响应

image-20230920140322094

image-20230920140441749

半连接扫描

代码

1
2
3
4
5
6
7
8
def scan_S(ip, port):
p = IP(dst=ip) / TCP(dport=int(port), flags="S")
link = sr1(p, timeout=1, verbose=1)
if link.haslayer(TCP):
if link.getlayer(TCP).flags == 'SA':
print("[SYN]The PORT is open.")
else:
print("[SYN]The PORT is closed.")
  • p = IP(dst=ip) / TCP(dport=int(port), flags="S"): 这一行代码创建了一个TCP SYN请求数据包。

    • IP(dst=ip): 这部分代码创建了一个IP数据包,其中 dst 参数设置为传递给函数的目标主机的IP地址。

    • TCP(dport=int(port), flags="S"): 这部分代码创建了一个TCP数据包,其中 dport 参数设置为传递给函数的端口号,flags 参数设置为 “S”,表示发送一个TCP SYN请求。

    • link = sr1(p, timeout=1, verbose=1): 这一行代码使用Scapy库发送TCP SYN请求数据包到目标主机的指定端口,并等待回复。具体解释如下:

  • verbose=1: 这个参数设置为1,表示显示详细的Scapy输出信息,以便查看扫描进度。

  1. if link.haslayer(TCP):: 这是一个条件语句,用于检查接收到的数据包是否包含TCP层。
  2. if link.getlayer(TCP).flags == 'SA':: 如果数据包包含TCP层,这一行代码用于检查TCP层中的标志是否为 ‘SA’,表示TCP连接建立成功,端口是开放的。
  3. print("[SYN]The PORT is open."): 如果端口是开放的,代码将打印出 “[SYN]The PORT is open.” 消息。
  4. else:: 如果数据包不包含TCP层或标志不是 ‘SA’,则表示端口不是开放的。
  5. print("[SYN]The PORT is closed."): 在这种情况下,代码将打印出 “[SYN]The PORT is closed.” 消息。

结果

对于未开放的端口发送SYN时,包直接被丢弃

image-20230920141139468

对于开放的端口,服务器将回复SYN ACK

image-20230920141215628

FIN

代码

1
2
3
4
5
6
7
8
def scan_F(ip, port):
p = IP(dst=ip) / TCP(dport=int(port), flags="F")
link = sr1(p, timeout=1, verbose=1)
if link == None:
print("[FIN]The PORT is open.")
elif link != None and link.getlayer(TCP).flags == 'RA':
link.display()
print("[FIN]The PORT is closed.")
  1. p = IP(dst=ip) / TCP(dport=int(port), flags="F"): 这一行代码创建了一个TCP FIN请求数据包。具体解释如下:
    • IP(dst=ip): 这部分代码创建了一个IP数据包,其中 dst 参数设置为传递给函数的目标主机的IP地址。
    • TCP(dport=int(port), flags="F"): 这部分代码创建了一个TCP数据包,其中 dport 参数设置为传递给函数的端口号,flags 参数设置为 “F”,表示发送一个TCP FIN请求。
  2. link = sr1(p, timeout=1, verbose=1): 这一行代码使用Scapy库发送TCP FIN请求数据包到目标主机的指定端口,并等待回复。
  3. if link == None:: 这是一个条件语句,用于检查是否接收到了回复数据包。
  4. print("[FIN]The PORT is open."): 如果没有接收到回复数据包,说明端口是开放的,因为没有收到TCP FIN-ACK响应。
  5. elif link != None and link.getlayer(TCP).flags == 'RA':: 如果接收到了回复数据包,并且该数据包的TCP标志为 ‘RA’(RST-ACK),则表示端口是关闭的。
  6. link.display(): 这一行代码用于显示接收到的回复数据包的详细信息,包括IP和TCP头部的信息。
  7. print("[FIN]The PORT is closed."): 最后,代码打印出 “[FIN]The PORT is closed.” 消息,表示端口是关闭的。

结果

image-20230920141707152

NULL

代码

1
2
3
4
5
6
7
p = IP(dst=ip) / TCP(dport=int(port), flags="")
link = sr1(p, timeout=1, verbose=1)
if link == None:
print("[NULL]The PORT is open.")
elif link != None and link.getlayer(TCP).flags == 'RA':
link.display()
print("[NULL]The PORT is closed.")

检测指定主机的指定端口是否处于开放状态。与之前的FIN扫描和SYN扫描不同,NULL扫描发送一个没有标志位(flags)的TCP数据包,因此不应该触发目标主机的任何特定响应。

  1. p = IP(dst=ip) / TCP(dport=int(port), flags=""): 这一行代码创建了一个TCP NULL请求数据包。
    • IP(dst=ip): 这部分代码创建了一个IP数据包,其中 dst 参数设置为传递给函数的目标主机的IP地址。
    • TCP(dport=int(port), flags=""): 这部分代码创建了一个TCP数据包,其中 dport 参数设置为传递给函数的端口号,但 flags 参数设置为空字符串,表示发送一个TCP NULL请求,即没有设置任何标志位。
  2. link = sr1(p, timeout=1, verbose=1): 这一行代码使用Scapy库发送TCP NULL请求数据包到目标主机的指定端口,并等待回复。
  3. print("[NULL]The PORT is open."): 如果没有接收到回复数据包,说明端口是开放的,因为没有收到TCP响应(因为发送的是一个空的TCP数据包)。
  4. elif link != None and link.getlayer(TCP).flags == 'RA':: 如果接收到了回复数据包,并且该数据包的TCP标志为 ‘RA’(RST-ACK),则表示端口是关闭的。
  5. link.display(): 这一行代码用于显示接收到的回复数据包的详细信息,包括IP和TCP头部的信息。
  6. print("[NULL]The PORT is closed."): 最后,代码打印出 “[NULL]The PORT is closed.” 消息,表示端口是关闭的。

结果

image-20230920141915704

Xmas

代码

1
2
3
4
5
6
7
p = IP(dst=ip) / TCP(dport=int(port), flags="FPU")
link = sr1(p, timeout=1, verbose=1)
if link == None:
print("[Xmas]The PORT is open.")
elif link != None and link.getlayer(TCP).flags == 'RA':
link.display()
print("[Xmas]The PORT is closed.")
  1. p = IP(dst=ip) / TCP(dport=int(port), flags="FPU"): 这一行代码创建了一个TCP Xmas请求数据包。
    • TCP(dport=int(port), flags="FPU"): 这部分代码创建了一个TCP数据包,其中 dport 参数设置为传递给函数的端口号,而 flags 参数设置为 “FPU”,表示发送一个TCP Xmas请求,即将FIN、PSH和URG标志位都设置为1。
  2. link = sr1(p, timeout=1, verbose=1): 这一行代码使用Scapy库发送TCP Xmas请求数据包到目标主机的指定端口,并等待回复。
  3. if link == None:: 这是一个条件语句,用于检查是否接收到了回复数据包。
  4. print("[Xmas]The PORT is open."): 如果没有接收到回复数据包,说明端口是开放的,因为没有收到TCP响应(因为发送的是一个Xmas请求,不符合正常的TCP握手)。
  5. elif link != None and link.getlayer(TCP).flags == 'RA':: 如果接收到了回复数据包,并且该数据包的TCP标志为 ‘RA’(RST-ACK),则表示端口是关闭的。
  6. print("[Xmas]The PORT is closed."): 最后,代码打印出 “[Xmas]The PORT is closed.” 消息,表示端口是关闭的。

结果

image-20230920142024251

FIN NULL Xmas存在的问题

首先我使用wireshark对虚拟机的端口进行抓包,以FIN为例,使用nmap进行FIN扫描,命令为:

1
nmap 192.168.xx.xx -p xx -sF

分别对开放端口22 和关闭的端口8888进行扫描,发现nmap确实发了一个只有FIN置位的TCP包,但是服务器都没有响应

image-20230920161714505

之后我查看了LINUX的源码(内核版本5.4.0)

image-20230920161747708

在进行实验的过程中,使用Scapy直接对服务器进行连接,初始状态为TCP_LISTEN, 同时进行三个判断,分别为ack rst syn(再进行fin),其他情况全部丢弃。 所以我认为是linux系统自动把非法的包给丢弃了。