Skip to content

Latest commit

 

History

History
546 lines (396 loc) · 17.1 KB

File metadata and controls

546 lines (396 loc) · 17.1 KB

第六章:使用 Python 进行网络扫描

在本章中,我们将涵盖以下内容:

  • 简单端口扫描器

  • IP 范围/网络扫描器

  • 隐蔽扫描

  • FIN 扫描

  • XMAS 扫描

  • TCP ACK 扫描

  • LanScan

介绍

在渗透测试和网络分析中,网络扫描器在获取本地网络中可用主机和运行在这些主机上的应用程序的详细信息方面发挥着重要作用。网络扫描有助于识别主机上运行的可用 UDP 和 TCP 网络服务,并有助于确定主机使用的操作系统(OSs)。

简单端口扫描器

端口扫描器旨在检查服务器或主机机器上的开放端口。它帮助攻击者识别主机机器上运行的服务,并利用其中的漏洞。

准备工作

我们可以使用socket模块编写一个简单的端口扫描器。socket模块是 Python 中默认的低级网络接口。

如何做...

我们可以使用socket模块创建一个简单的端口扫描器,以下是步骤:

  1. 创建一个名为port-scanner.py的新文件并在编辑器中打开它。

  2. 导入所需的模块,如下所示:

import socket,sys,os 

导入socket模块以及sysos模块

  1. 现在我们可以定义我们的扫描器的变量:
host = 'example.com' 
open_ports =[] 
start_port = 1 
end_port = 10 

在这里,我们定义了我们计划扫描的起始和结束端口

  1. 从域名获取 IP:
ip = socket.gethostbyname(host) 

这里我们使用socket模块中的gethostbyname方法。这将返回域的 IP

  1. 现在我们可以编写一个函数来探测端口:
def probe_port(host, port, result = 1): 
  try: 
    sockObj = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
    sockObj.settimeout(0.5) 
    r = sockObj.connect_ex((host, port))   
    if r == 0: 
      result = r 
    sock.close() 
  except Exception ase: 
    pass 
  return result 

在这里,我们创建了一个名为sockObj的套接字对象,并尝试将其连接到端口。如果连接成功,则端口是打开的。创建的socket对象使用 IPv4 套接字系列(AF_INET)和 TCP 类型连接(SOCK_STREAM)。对于 UDP 类型连接,我们必须使用SOCK_DGRAM

最后,它将返回作为函数输出的结果。

  1. 现在我们将编写一个for循环来迭代端口范围,并使用probe_port方法探测端口:
for port in range(start_port, end_port+1): 
    sys.stdout.flush() 
    print (port) 
    response = probe_port(host, port) 
    if response == 0: 
        open_ports.append(port) 
    if not port == end_port: 
        sys.stdout.write('\b' * len(str(port))) 

如果端口是打开的,则将结果添加到列表open_port

  1. 最后,按以下方式打印结果列表:
if open_ports: 
  print ("Open Ports") 
  print (sorted(open_ports)) 
else: 
  print ("Sorry, No open ports found.!!") 
  1. 现在我们可以尝试更改前面的脚本以扫描默认端口列表。

为此,我们将定义一个默认端口列表:

common_ports = { 21, 22, 23, 25, 53, 69, 80, 88, 109, 110,  
                 123, 137, 138, 139, 143, 156, 161, 389, 443,  
                 445, 500, 546, 547, 587, 660, 995, 993, 2086,  
                 2087, 2082, 2083, 3306, 8443, 10000  
                } 

此外,我们更改循环以调用probe_port,如下所示:

for p in sorted(common_ports): 
  sys.stdout.flush() 
  print p 
  response = probe_port(host, p) 
  if response == 0: 
    open_ports.append(p) 
  if not p == end_port: 
    sys.stdout.write('\b' * len(str(p))) 

IP 范围/网络扫描器

我们可以使用 ICMP 数据包创建一个网络扫描器。由于 ICMP 不是 IP 协议,我们必须直接访问网络堆栈。因此,我们可以使用 Scapy 生成 ICMP 数据包并将其发送到主机。

准备工作

要开始抓取,我们必须安装所需的 Python 包。这里我们使用 Scapy 进行数据包生成。要安装 Scapy,我们可以使用pip。由于我们使用的是 Python 3,请确保为 Python 3 安装 Scapy。还要安装其依赖模块netifaces

pip3 install scapy-python3
pip3 install netifaces  

如何做...

以下是使用scapy模块创建简单网络扫描器的步骤:

  1. 创建一个名为network-scanner.py的文件并在编辑器中打开它。

  2. 导入脚本所需的模块:

import socket, re 
from scapy.all import * 
  1. 为了获取系统的本地 IP,我们使用socket模块中的getsockname方法。但是,它需要一个连接。因此,我们创建一个 UDP 套接字连接以连接到 Google DNS,并使用此连接来枚举本地 IP:
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 
s.connect(('8.8.8.8', 80)) 
ip = s.getsockname()[0] 
  1. 现在我们提取本地 IP 并使用正则表达式截断最后的 IP 数字:
end = re.search('^[\d]{1,3}.[\d]{1,3}.[\d]{1,3}.[\d]{1,3}', ip) 
create_ip = re.search('^[\d]{1,3}.[\d]{1,3}.[\d]{1,3}.', ip) 
  1. 现在创建一个生成 ICMP 数据包并将其发送到主机的函数。这里我们使用 Scapy:
def is_up(ip): 
    icmp = IP(dst=ip)/ICMP() 
    resp = sr1(icmp, timeout=10) 
    if resp == None: 
        return False 
    else: 
        return True  
  1. 创建另一个函数来检查 IP 是否为环回(127.0.0.1):
def CheckLoopBack(ip): 
    if (end.group(0) == '127.0.0.1'): 
        return True 
  1. 现在通过迭代最后的 IP 数字运行网络扫描以扫描网络中的所有 IP:
try: 
    if not CheckLoopBack(create_ip): 
        conf.verb = 0  
        for i in range(1, 10): 
            test_ip = str(create_ip.group(0)) + str(i) 
            if is_up(test_ip): 
                print (test_ip + " Is Up") 
except KeyboardInterrupt: 
    print('interrupted!') 

conf.verb = 0将禁用 Scapy 中的详细模式,以避免来自 Scapy 的日志

  1. 确保以管理员权限运行脚本,因为 Scapy 需要管理员访问权限来创建数据包:
sudo python3 network-scanner.py  

隐蔽扫描

隐蔽扫描是一种 TCP 扫描形式。在这里,端口扫描器创建原始 IP 数据包并将其发送到主机以监视响应。这种类型的扫描也被称为半开放扫描或 SYN 扫描,因为它从不打开完整的 TCP 连接。这种类型的扫描器创建一个 SYN 数据包并将其发送到主机。如果目标端口是打开的,主机将用一个 SYN-ACK 数据包做出响应。然后客户端将用一个 RST 数据包做出响应,以在完成握手之前关闭连接。如果端口是关闭但未被过滤,目标将立即用一个 RST 数据包做出响应。

要创建一个 SYN 扫描器,我们将使用 Scapy 模块。这是一个功能强大的交互式数据包操作程序和库。

准备工作

对于扫描端口,我们将向正在扫描的主机发送自定义数据包,并解析响应以分析结果。我们需要 Scapy 来生成并发送数据包到主机。确保系统中安装了scapy模块。

如何实现...

我们可以通过以下步骤创建一个 SYN 扫描器:

  1. 创建一个名为syn-scanner.py的新文件,并在编辑器中打开它。

  2. 像往常一样,导入所需的模块:

from scapy.all import * 

这将导入scapy模块

  1. 现在我们可以声明变量,并且如果需要,也可以将这些变量作为参数传递:
host = 'www.dvwa.co.uk' 
ip = socket.gethostbyname(host) 
openp = [] 
filterdp = [] 
common_ports = { 21, 22, 23, 25, 53, 69, 80, 88, 109, 110,  
                123, 137, 138, 139, 143, 156, 161, 389,       443, 445, 500, 546, 547, 587, 660, 995,       993, 2086, 2087, 2082, 2083, 3306, 8443,       10000 } 
  1. 现在我们可以创建一个函数来检查主机是正常运行还是宕机:
def is_up(ip): 
    icmp = IP(dst=ip)/ICMP() 
    resp = sr1(icmp, timeout=10) 
    if resp == None: 
        return False 
    else: 
        return True 

我们创建并发送一个 ICMP 数据包到主机。如果主机正常运行,它将做出响应。

  1. 接下来,我们可以创建一个使用 SYN 数据包扫描端口的函数:
def probe_port(ip, port, result = 1): 
    src_port = RandShort() 
    try: 
        p = IP(dst=ip)/TCP(sport=src_port, dport=port, flags='F') 
        resp = sr1(p, timeout=2) # Sending packet 
        if str(type(resp)) == "<type 'NoneType'>": 
            result = 1 
        elif resp.haslayer(TCP): 
            if resp.getlayer(TCP).flags == 0x14: 
                result = 0 
            elif (int(resp.getlayer(ICMP).type)==3 and int(resp.getlayer(ICMP).code) in [1,2,3,9,10,13]): 
                result = 2 
    except Exception as e: 
        pass 
    return result 

在这里,我们将一个随机端口设置为目标端口,然后创建一个带有源端口、目标端口和目标 IP 的 SYN 数据包。然后我们将发送数据包并分析响应。如果响应类型为None,则端口是关闭的。如果响应具有 TCP 层,则我们必须检查其中的标志值。标志有九位,但我们只检查控制位,它们有六位。它们是:

    • URG = 0x20
  • ACK = 0x10

  • PSH = 0x08

  • RST = 0x04

  • SYN = 0x02

  • FIN = 0x01

以下是 TCP 层的头部结构:

因此,如果标志值为 0x12,则响应具有 SYN 标志,我们可以认为端口是打开的。如果值为 0x14,则标志是 RST/ACK,因此端口是关闭的。

  1. 然后我们将检查主机是否正常运行,循环遍历常见端口列表,并在主机正常运行时扫描每个端口:
 if is_up(ip): 
        for port in common_ports: 
            print (port) 
            response = probe_port(ip, port) 
            if response == 1: 
                openp.append(port) 
            elif response == 2: 
                filterdp.append(port) 
        if len(openp) != 0: 
            print ("Possible Open or Filtered Ports:") 
            print (openp) 
        if len(filterdp) != 0: 
            print ("Possible Filtered Ports:") 
            print (filterdp) 
        if (len(openp) == 0) and (len(filterdp) == 0): 
            print ("Sorry, No open ports found.!!") 
    else: 
        print ("Host is Down") 

扫描常见端口列表中的每个端口,并将识别出的打开端口添加到打开端口列表中,然后打印列表

  1. 确保以sudo身份运行脚本,因为我们正在使用 Scapy,而 Scapy 需要管理员权限:
sudo python3 syn-scanner.py 

FIN 扫描

SYN 扫描可以被防火墙阻止。然而,设置了 FIN 标志的数据包具有绕过防火墙的能力。它的工作原理是这样的--对于一个 FIN 数据包,关闭的端口会用一个 RST 数据包做出响应,而打开的端口会忽略这些数据包。如果是一个 ICMP 数据包,类型为 3,代码为 1、2、3、9、10 或 13,我们可以推断出端口被过滤,端口状态无法被找到。我们可以使用 Scapy 创建 FIN 数据包并扫描端口。

如何实现...

我们可以按照以下方式创建一个 FIN 扫描器:

  1. 就像我们在上一个步骤中所做的那样,我们必须创建另一个文件fin-scanner.py,并在编辑器中打开它。

  2. 然后导入所需的模块:

from scapy.all import * 
  1. 就像我们为 SYN 扫描器所做的那样,设置变量并创建函数来检查服务器是否正常运行:
host = 'www.dvwa.co.uk' 
ip = socket.gethostbyname(host) 
openp = [] 
filterdp = [] 
common_ports = { 21, 22, 23, 25, 53, 69, 80, 88, 109, 110,  
                 123, 137, 138, 139, 143, 156, 161, 389, 443,  
                 445, 500, 546, 547, 587, 660, 995, 993, 2086,  
                 2087, 2082, 2083, 3306, 8443, 10000  
                } 
def is_up(ip): 
    icmp = IP(dst=ip)/ICMP() 
    resp = sr1(icmp, timeout=10) 
    if resp == None: 
        return False 
    else: 
        return True  
  1. 现在我们可以创建探测端口的函数如下:
def probe_port(ip, port, result = 1): 
    src_port = RandShort() 
    try: 
        p = IP(dst=ip)/TCP(sport=src_port, dport=port, flags='F') 
        resp = sr1(p, timeout=2) # Sending packet 
        if str(type(resp)) == "<type 'NoneType'>": 
            result = 1 
        elif resp.haslayer(TCP): 
            if resp.getlayer(TCP).flags == 0x14: 
                result = 0 
            elif (int(resp.getlayer(ICMP).type)==3 and int(resp.getlayer(ICMP).code) in [1,2,3,9,10,13]): 
                result = 2 
    except Exception as e: 
        pass 
    return result 

在这里,我们将标志更改为F以进行FIN,同时创建要发送的数据包

  1. 最后,我们将检查主机是否正常运行,循环遍历常见端口列表,并在主机正常运行时扫描每个端口:
 if is_up(ip): 
        for port in common_ports: 
            print (port) 
            response = probe_port(ip, port) 
            if response == 1: 
                openp.append(port) 
            elif response == 2: 
                filterdp.append(port) 
        if len(openp) != 0: 
            print ("Possible Open or Filtered Ports:") 
            print (openp) 
        if len(filterdp) != 0: 
            print ("Possible Filtered Ports:") 
            print (filterdp) 
        if (len(openp) == 0) and (len(filterdp) == 0): 
            print ("Sorry, No open ports found.!!") 
    else: 
        print ("Host is Down") 

XMAS 扫描

使用 XMAS 扫描,我们将发送一个 TCP 数据包,其中包含一堆标志(PSH,FIN 和 URG)。如果端口关闭,我们将收到一个 RST。如果端口是打开的或被过滤的,那么服务器将不会有任何响应。这与 FIN 扫描类似,只是在创建数据包的部分不同。

如何做...

使用 Scapy 创建 XMAS 扫描器的步骤如下:

  1. 创建我们为上一个配方创建的文件的副本(FIN 扫描)。由于它非常相似,我们只需要更改数据包创建部分。

  2. 要创建并发送一个带有 PSH、FIN 和 URG 标志的数据包,请更新probe_port方法中的数据包制作部分,如下所示:

p = IP(dst=ip)/TCP(sport=src_port, dport=port, flags='FPU') 

只更新标志参数。这里我们将标志设置为FPU,表示 PSH、FIN 和 URG 的组合。

TCP ACK 扫描

ACK 标志扫描对于验证服务器是否被防火墙、IPS 或其他网络安全控制所阻塞非常有用。与 FIN 扫描一样,我们将发送一个 TCP ACK 数据包。没有响应或 ICMP 错误表明存在有状态的防火墙,因为端口被过滤,如果我们收到一个 RST-ACK,那么有状态的防火墙就不存在:

如何做...

使用 Scapy 创建 TCP ACK 扫描器的步骤如下:

  1. 像往常一样,导入所需的模块并设置变量。还要定义检查主机状态的方法:
from scapy.all import * 
# define the host, port 
host = 'rejahrehim.com' 
ip = socket.gethostbyname(host) 
port = 80 
# define the method to check the status of host 
def is_up(ip): 
    icmp = IP(dst=ip)/ICMP() 
    resp = sr1(icmp, timeout=10) 
    if resp == None: 
        return False 
    else: 
        return True 
  1. 要发送一个带有ACK标志的 TCP 数据包,请更新上一个配方中的probe_port方法,如下所示:
def probe_port(ip, port, result = 1): 
    src_port = RandShort() 
    try: 
        p = IP(dst=ip)/TCP(sport=src_port, dport=port, flags='A', seq=12345) 
        resp = sr1(p, timeout=2) # Sending packet 
        if str(type(resp)) == "<type 'NoneType'>": 
            result = 1 
        elif resp.haslayer(TCP): 
            if resp.getlayer(TCP).flags == 0x4: 
                result = 0 
            elif (int(resp.getlayer(ICMP).type)==3 and int(resp.getlayer(ICMP).code) in [1,2,3,9,10,13]): 
                result = 1 
    except Exception as e: 
        pass 
    return result 

在这里,我们创建一个 TCP ACK 数据包并将其发送到主机

  1. 最后,运行扫描程序,如下所示:
 if is_up(ip): 
            response = probe_port(ip, port) 
            if response == 1: 
                 print ("Filtered | Stateful firewall present") 
            elif response == 0: 
                 print ("Unfiltered | Stateful firewall absent") 
    else: 
        print ("Host is Down") 

LanScan

LanScan 是一个 Python 3 模块,可以帮助扫描给定的本地网络。它可以列出所有设备及其开放的端口。LanScan 还可以帮助获取有关网络接口和网络的信息。

准备工作

我们可以使用pip安装lanscan

pip3 install lanscan  

如何做...

以下是 LanScan 的一些用例:

  1. LanScan 有一些我们可以用来扫描局域网的选项。要获取系统中可用接口的详细信息,我们可以使用interfaces选项:
sudo lanscan interfaces  

这将打印出可用的接口,如下所示:

  1. 我们可以使用 network 命令获取连接的网络列表:
sudo lanscan networks

  1. 我们可以从终端窗口开始本地网络扫描。这需要管理员权限:
sudo lanscan scan  

这将列出 LAN 网络中的 IP 地址以及每个系统中的开放端口