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')


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM