Scapy基础使用(一)

Scapy

基本介绍

简介

Scapy是一个基于Python的程序,是一个强大的交互式包操作程序。它能够发送或解码大量协议的数据包,用于发送、嗅探、伪造网络数据包等行为,因此可以作为探测、扫描或者网络攻击的工具。Scapy可以实现部分工具的功能:hping、arpsoof、apr-sk、arping、p0f,甚至Nmap、tcpdump等工具的部分功能。

scapy还可以很好地执行许多其他工具无法处理的其他特定任务,例如发送无效帧、注入自己的802.11帧、结合技术(VLAN跳跃+ARP缓存中毒、WEP加密通道上的VoIP解码等)

Scapy还可以跟踪路由并仅给出请求的起始TTL和应答的源IP。一种能发出整个网络信号并给出应答机列表的设备。一个执行portscan并返回一个 Latex 报告的函数。

特点

网络工具缺点

  • 大部分网络工具都是作者为了特定目的构建的,因此在使用上有一些限制,例如一个ARP缓存中毒程序不允许使用双802.1q封装。因此如果有新的需求,则需要寻找或者构造一个新的工具

  • 混淆解码和解释。机器擅长解码,可以帮助人类破解;而翻译是为人类保留的。有些程序试图模仿这种行为,例如说“The host is open“,而不是说”I received a SYN-ACK“,这对初学者比较友好,但是其中丢失了大量的可用信息。

  • 只进行解码的程序也不能提供它们接收到的所有信息

Spacy能够克服上述局限,能够准确地构建所需的数据包,它有一个灵活的模型,试图避免这样的任意限制,可以自由地将所需的任何值放入所需的任何字段中,并按所需方式对其进行堆叠。Spacy可以根据少量的代码来构建一个简易的工具程序。

Scapy优点

  • 快速分组设计

    scapy的范例是提出一种特定于领域的语言(DSL),它能够对任何类型的数据包进行强大而快速的描述。使用python语法和python解释器作为DSL语法和解释器有许多优点:不需要编写单独的解释器,用户不需要再学习另一种语言,他们从一种完整、简洁和强大的语言中获益。

    scapy允许用户将一个或一组数据包描述为层叠在一起的层。每个层的字段都有可以重载的有用默认值。scapy不强制用户使用预先确定的方法或模板。这减少了每次需要不同场景时编写新工具的需求。在C语言中,平均需要60行来描述一个包。使用scapy,将要发送的数据包只能在一行中描述,另一行用于打印结果。90%的网络探测工具可以用2行scapy重写。

  • 少量探测反馈大量信息

    网络发现是黑盒测试。当探测一个网络时,许多数据包被发送,而只有少数被应答。scapy提供所有信息,即发送的所有数据和接收的所有响应。对这些数据的检查将为用户提供所需的信息。_images/scapy-concept.png

  • 返回完整信息

    报告类似 在端口80上接收到TCP重置 不受解释错误的影响。报告 端口80关闭 是一种解释,在工具作者无法想象的特定环境中,大多数时候可能是正确的,但也可能是错误的。一些扫描器在接收到无法到达的ICMP目的地数据包时,往往会报告一个经过过滤的TCP端口。这可能是正确的,但在某些情况下,这意味着数据包没有被防火墙过滤,而是没有主机将数据包转发到。

​ scapy避免了这种现象,返回比较全面的信息供人员查看。

安装

Scapy对python的版本要求比较严格

Scapy version Python 2.2-2.6 Python 2.7 Python 3.4-3.6 Python 3.7 Python 3.8
2.2.X YES YES NO NO NO
2.3.3 YES YES NO NO NO
2.4.0 NO YES YES NO NO
2.4.2 NO YES YES YES NO
2.4.3-2.4.4 NO YES YES YES YES

安装方式

#conda
conda install -c conda-forge scrapy

#pip
pip install Scrapy

Scapy在版本2.4.3开始,可以分为三类机型安装
pip install scapy
pip install --pre scapy[basic](推荐)
pip install --pre scapy[complete](包含了依赖项)

#git
git clone https://github.com/secdev/scapy.git
cd scapy
sudo python setup.py install

依赖项

  • 作图: Matplotlib

    p=sniff(count=50)
    p.plot(lambda x:len(x))
    
  • 2D图形:PyX,要求安装 texlive (Unix) 或 MikTex (Windows)

    p=IP()/ICMP()
    p.pdfdump("test.pdf")
    
  • 图: Graphviz 和 ImageMagick (软件)

    p=rdpcap("myfile.pcap")
    p.conversations(type="jpg", target="> test.jpg")
    
  • 三维图形:VPython-Jupyter

    a,u=traceroute(["www.python.org", "google.com","slashdot.org"])
    a.trace3D()
    
  • WEP解密:cryptography

    enc=rdpcap("weplab-64bit-AA-managed.pcap")
    enc.show()
    enc[0]
    conf.wepkey="AA\x00\x00\x00"
    dec=Dot11PacketList(enc).toEthernet()
    dec.show()
    dec[0]
    
  • PKI操作和TLS解密:cryptography

  • 指纹图谱:Nmap

    load_module("nmap")
    nmap_fp("192.168.0.1")
    
  • VoIP:Sox

特定平台软件要求

Linux:tcpdump

Windows:Npcap,旧版为Winpcap,新版不支持

交互式使用

scapy可以通过终端进行交互式操作。注意发送数据包需要root权限,因此需要在root下或者sudo运行。若缺少相关安装依赖,则会有部分功能不可用

INFO: Can't import PyX. Won't be able to use psdump() or pdfdump().

启动命令

# scapy

常见命令

ls() - 不带参数则查看所有可持的layer,也可以指定一个layer名称从而查询此layer的详细详细,例如ls(TCP)
lsc() - 查看当前scapy的所有功能列表
help() - 查看功能的帮助,例如help(hexdump)可以查看hexdump的作用和参数介绍
conf - 查看当前的配置信息,有些scapy功能没有指定参数时,默认值都取自这里

构造数据包

在Scapy中每一个协议就是一个类。只需要实例化一个协议类,就可以创建一个该协议的数据包。例如

a=IP(ttl=10) #设置一个IP数据包,其ttl为10
asrc  # "127.0.0.1" 默认为本地回环地址
a.dst='192.168.220.1' 目的地址,可以设置为网段,如192.168.220.0/24,此时产生256个数据包,查看数据包使用[i for i in ip]
a.src # "192.168.220.154" 设置好目的地址后会更换会对应ip网段
a
<IP  ttl=10 dst=192.168.220.1 |>

del(a.ttl)
a
<IP  dst=192.168.220.1 |>
a.ttl
64

Scapy是根据分层来构造数据包的,大致基于TCP/IP体系;一般数据协议为Ether-IP-TCP/UDP等,根据不同的使用场景设置不同的数据包,例如IP()函数无法用来构造ARP请求和应答数据包,此时可以使用Ether(),这个函数可以设置发送方和接收方的MAC地址。如广播数据包。

Ether(dst="ff:ff:ff:ff:ff:ff")

构造HTTP数据包

IP()/TCP()/"GET/HTTP/1.0\r\n\r\n"

Scapy目前使用频率最高的类有Ether、IP、TCP和UDP。每个类都有自己的一些参数

  • Ether:源地址、目的地址和类型。

    dst - 目的MAC地址
    src - 源MAC地址
    type - 类型 IPv4:0x0800
    例如:
    Ether(dst='00:00:00:00:00:00',src='00:00:00:00:00:00',type=0x800)
    
  • IP:源地址和目的地址,还有版本、长度、协议类型、校验和等

    version - 版本,4表示IPv4,6表示IPv6
    proto - 协议,TCP,UDP
    chksum - 校验和
    src - 源地址
    dst - 目的地址
    
    IP(version=4,ihl=5, tos=84, id=19450, flags=0, ttl=64, proto=1,chksum=0xf090, src='192.168.1.6',dst='192.168.1.6')
    
  • TCP

    sport - 源端口
    dport - 目标端口
    seq - sequence值
    ack - acknowledgement值
    flags - S,A,SA,F,P
    window - 窗口大小
    chksum - 校验和
    
    TCP(sport=45486,dport=8888,seq=31,ack=31,window=342,flags='PA')
    
  • UDP

    sport - 源端口
    dport - 目的端口
    len - 包长度
    chksum - 校验和
    
    UDP(sport=1971,dport=9527,len=10,chksum=0)
    
    

​ 可以使用 ls() 函数来查看一个类拥有的属性。

ls(Ether())
WARNING: Mac address to reach destination not found. Using broadcast.
dst        : DestMACField                        = 'ff:ff:ff:ff:ff:ff' (None)
src        : SourceMACField                      = '00:0c:29:81:c8:a0' (None)
type       : XShortEnumField                     = 36864           (36864)

/ 运算符用作两层之间的合成运算符,下层可以根据上层重载一个或多个默认字段,字符串可以用作原始层,可以根据/进行去跟

>>> IP()
<IP  |>
>>> IP()/TCP()
<IP  frag=0 proto=tcp |<TCP  |>>
>>> Ether()/IP()/TCP()
<Ether  type=IPv4 |<IP  frag=0 proto=tcp |<TCP  |>>>
>>> IP()/TCP()/"GET / HTTP/1.0\r\n\r\n"
<IP  frag=0 proto=tcp |<TCP  |<Raw  load='GET / HTTP/1.0\r\n\r\n' |>>>                   
>>> Ether()/IP()/UDP()
<Ether  type=IPv4 |<IP  frag=0 proto=udp |<UDP  |>>>
>>> IP(protp=55)/TCP()
<IP  frag=0 proto=55 |<TCP  |>>

通过_查看数据包的最新信息

>> IP(_)
<IP  version=4 ihl=5 tos=0x0 len=20 id=1 flags= frag=0 ttl=64 proto=hopopt chksum=0x7ce7 src=127.0.0.1 dst=127.0.0.1 |>  

展示数据

  • show - 例如pcap.show,或者pcap[0].show

  • show() - 例如pcap.show(),或者pcap[0].show()

  • sniff获取的数据可以列表的方式操作,每一个元素都是一条捕获的消息,show可以显示概要信息,show()则会显示详细的内容,或者格式化显示内容

ptk = Ether()/IP()/TCP()/'HELLO WORLD'
>>> ptk.show()
###[ Ethernet ]### 
  dst= ff:ff:ff:ff:ff:ff
  src= 00:00:00:00:00:00
  type= IPv4
###[ IP ]### 
     version= 4
     ihl= None
     tos= 0x0
     len= None
     id= 1
     flags= 
     frag= 0
     ttl= 64
     proto= tcp
     chksum= None
     src= 127.0.0.1
     dst= 127.0.0.1
     \options\
###[ TCP ]### 
        sport= ftp_data
        dport= http
        seq= 0
        ack= 0
        dataofs= None
        reserved= 0
        flags= S
        window= 8192
        chksum= None
        urgptr= 0
        options= []
###[ Raw ]### 
           load= 'HELLO WORLD'
           
           
##查询相关信息
>>> ptk[TCP]
<TCP  |<Raw  load='HELLO WORLD' |>>
>>> ptk[TCP].show()
###[ TCP ]### 
  sport= ftp_data
  dport= http
  seq= 0
  ack= 0
  dataofs= None
  reserved= 0
  flags= S
  window= 8192
  chksum= None
  urgptr= 0
  options= []
###[ Raw ]### 
     load= 'HELLO WORLD'

>>> ptk[TCP].dport
80
>>> 

收发数据包

在创建好数据包后,需要收发数据包,Scapy中提供了多个用来完成发送数据包的函数,主要有只发不收既发又收两种情况

对于某种方法至于输入其名字即可获得其相关用法

send
<function scapy.sendrecv.send(x, inter=0, loop=0, count=None, verbose=None, realtime=None, return_packets=False, socket=None, iface=None, *args, **kargs)>
  • 只发不收

send和sendp

send(),在第三层发包

send(IP(dst="www.baidu.com",ttl=2)/ICMP())

sendp(),在第二层发包

sendp(Ether()/IP(dst=www.baidu.com))
sendp(rdpcap("/home/jma/Desktop/scapy/scapy_test.pcap")) #直接发送捕获的数据包

iface - 指定网卡
loop - 开启循环发送
inter - 设置间隔时间

如果希望发送一个内容是随机填充的数据包,而且又要保证这个数据包的正确性,可以使用fuzz()函数

sr(IP(dst="192.168.1.107")/fuzz(())
  • 既发又收

sr()、sr1()第三层

srp()第二层,与sr类似

src

sr()函数是Scapy的核心,它的返回值是两个列表,第一个列表是收到了应答的包和对应的应答,第二个列表是未收到应答的包。

>>> ans,unans=sr(IP(dst="192.168.220.2")/ICMP())
Begin emission:
Finished sending 1 packets.
.*
Received 2 packets, got 1 answers, remaining 0 packets
>>> ans
<Results: TCP:0 UDP:0 ICMP:1 Other:0>
>>> ans.summary()
IP / ICMP 192.168.220.154 > 192.168.220.2 echo-request 0 ==> IP / ICMP 192.168.220.2 > 192.168.220.154 echo-reply 0 / Padding

sr(IP(dst="192.168.220.157")/TCP(sport=666,dport=(440,443),flags="S")) #发送440到443的SYN标志包

这里使用ans和unans来保存sr()的返回值,因为发出的是一个ICMP请求数据包,而且也收到了一个应答包,所以这个发送的数据包和收到的应答包都被保存到了ans列表中,使用ans.summary()可以查看两个数据包的内容,而unans列表为空。

sr1

sr1()函数和sr()函数作用基本一样,但是值返回一个应答包。只需要使用一个列表就可以保存这个函数的返回值。因此可以使用sr1()函数来测试目标的某个端口是否开放,采用半开扫描(SYN)的办法。

>>> p = sr1(IP(dst="192.168.220.157")/TCP(dport=80,flags="A"))
Begin emission:
Finished sending 1 packets.
.*
Received 2 packets, got 1 answers, remaining 0 packets
>>> p
<IP  version=4 ihl=5 tos=0x0 len=40 id=23775 flags=DF frag=0 ttl=64 proto=tcp chksum=0xa367 src=192.168.220.157 dst=192.168.220.154 |<TCP  sport=http dport=ftp_data seq=0 ack=0 dataofs=5 reserved=0 flags=R window=0 chksum=0x74f3 urgptr=0 |<Padding  load='\x00\x00\x00\x00\x00\x00' |>>>  

可以发现目的地址回应了设置SYN标志位的数据包,说明开放了80端口

sniff()

与tcpdump比较相似,可以在自己的程序中捕获经过本机网卡的数据包,需要注意的是不可以实时回显,需要自己设置固定的输出或者终止嗅探后回显,可以使用filter进行过滤

  • 过滤参数 - filter,可以wireshark支持的形式设定相关参数,例如filter='tcp port 80'
  • 自定义过滤函数 - lfilter,可以指定一个函数,用来实现自己的过滤,例如lfilter=lambda p: p.haslayer(Raw),指定过滤所有带有data的包。filter和lfilter可以同时存在
  • 包个数 - count,例如count=20,捕获20个包之后停止
  • 接口 - iface,例如iface='eth1',多个接口可以通过list方式指定,例如iface=['eth0','eth1','lo']
  • 自定义停止捕获函数 - stop_filter,可以指定一个函数,返回true则停止捕获,例如stop_filter=lambda p: p.haslayer(Raw) and 'stop flag' in p.getlayer(Raw).load,就可以指定在接收到'stop flag'字符之后停止抓包。
在eth0网卡上监听源地址或目的地址为192.168.220.157的30个ICMP数据包
sniff(filter="icmp and host 192.168.220.157",count=30,iface="eth0")
# 此时在其他终端可以发送数据包或者直接ping
<Sniffed: TCP:0 UDP:0 ICMP:30 Other:0>
>>> a = _  #获取上一步结果
>>> a.nsummary() #摘要长度为一行
0000 Ether / IP / ICMP 192.168.220.154 > 192.168.220.157 echo-request 0 / Raw
0001 Ether / IP / ICMP 192.168.220.157 > 192.168.220.154 echo-reply 0 / Raw
0002 Ether / IP / ICMP 192.168.220.154 > 192.168.220.157 echo-request 0 / Raw
0003 Ether / IP / ICMP 192.168.220.157 > 192.168.220.154 echo-reply 0 / Raw
0004 Ether / IP / ICMP 192.168.220.154 > 192.168.220.157 echo-request 0 / Raw
0005 Ether / IP / ICMP 192.168.220.157 > 192.168.220.154 echo-reply 0 / Raw
0006 Ether / IP / ICMP 192.168.220.154 > 192.168.220.157 echo-request 0 / Raw
0007 Ether / IP / ICMP 192.168.220.157 > 192.168.220.154 echo-reply 0 / Raw
0008 Ether / IP / ICMP 192.168.220.154 > 192.168.220.157 echo-request 0 / Raw

存储捕获的数据

pcap=sniff(filter='tcp port 80',count=10, iface='lo')
wrpcap('my.pcap',pcap)

读取pcap文件

pcap=rdpcap('my.pcap')
通过sniff读取离线文件
pcap=sniff(offline='/home/jma/Desktop/scapy/scapy_test.pcap')
posted @ 2022-04-09 17:14  JICEY  阅读(117)  评论(0编辑  收藏  举报