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提供所有信息,即發送的所有數據和接收的所有響應。對這些數據的檢查將為用戶提供所需的信息。
-
返回完整信息
報告類似 在端口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')