數(shù)據(jù)包處理利器—Scapy高級使用(一)
主機(jī)探測
TCP SYN Ping
- 發(fā)送僅設(shè)置了SYN的空TCP數(shù)據(jù)包。
- SYN/ACK或RST響應(yīng)表示機(jī)器已啟動(dòng)并正在運(yùn)行。
- >>> ans,unans=sr(IP(dst="60.205.177.0/28")/TCP(dport=80,flags="S"))
- Begin emission:
- Finished sending 16 packets.
- .*********..................................................................................^C
- Received 92 packets, got 9 answers, remaining 7 packets
- >>> ans.summary(lambda s:s[1].sprintf("%IP.src% is alive"))
- 60.205.177.1 is alive
- 60.205.177.2 is alive
- 60.205.177.4 is alive
- 60.205.177.6 is alive
- 60.205.177.7 is alive
- 60.205.177.8 is alive
- 60.205.177.11 is alive
- 60.205.177.12 is alive
- 60.205.177.14 is alive
TCP ACK Ping
- 發(fā)送僅設(shè)置了ACK位的空TCP數(shù)據(jù)包。
- 未經(jīng)請求的ACK數(shù)據(jù)包應(yīng)通過RST進(jìn)行響應(yīng),RST顯示一臺(tái)機(jī)器。
- SYN-ping和ACK-ping看起來可能是多余的,但是大多數(shù)無狀態(tài)防火墻不會(huì)過濾未經(jīng)請求的ACK數(shù)據(jù)包,所以最好同時(shí)使用這兩種ping技術(shù)。
- >>> ans, unans = sr(IP(dst='60.205.177.90-105')/TCP(dport=80, flags='A'))
- Begin emission:
- Finished sending 16 packets.
- .*.******....................................................................................................................................................................^C
- Received 173 packets, got 7 answers, remaining 9 packets
- >>> ans.summary(lambda s:s[1].sprintf("{IP: %IP.src% is alive}"))
- 60.205.177.91 is alive
- 60.205.177.94 is alive
- 60.205.177.95 is alive
- 60.205.177.97 is alive
- 60.205.177.100 is alive
- 60.205.177.101 is alive
- 60.205.177.102 is alive
UDP Ping
- 將UDP數(shù)據(jù)包發(fā)送給給定的端口(無論是否帶有有效載荷),協(xié)議特定的有效載荷會(huì)使掃描更加有效。
- 選擇最有可能關(guān)閉的端口(開放的UDP端口可能會(huì)收到空數(shù)據(jù)包,但會(huì)忽略它們)。
- ICMP端口不可達(dá)表示機(jī)器是啟動(dòng)的。
- >>> ans, unans = sr(IP(dst='60.205.177.100-254')/UDP(dport=90),timeout=0.1)
- Begin emission:
- Finished sending 155 packets.
- ..******..*****...
- Received 18 packets, got 11 answers, remaining 144 packets
- >>> ans.summary(lambda s:s[1].sprintf("%IP.src% is unreachable"))
- 60.205.177.106 is unreachable
- 60.205.177.108 is unreachable
- 60.205.177.107 is unreachable
- 60.205.177.111 is unreachable
- 60.205.177.125 is unreachable
- 60.205.177.172 is unreachable
- 60.205.177.191 is unreachable
- 60.205.177.203 is unreachable
- 60.205.177.224 is unreachable
- 60.205.177.242 is unreachable
- 60.205.177.244 is unreachable
ARP Ping
- 在同一網(wǎng)絡(luò)/ LAN上探測存活主機(jī)時(shí),可以使用ARP Ping。
- 更快,更可靠,因?yàn)樗鼉H通過ARP在第2層上運(yùn)行。
- ARP是任何第2層通信的骨干協(xié)議
由于在 IPv6 中沒有 ARP協(xié)議,所以在 IPv6 上層定義了 NDP 協(xié)議實(shí)現(xiàn) ARP 的地址解析,沖突地址檢測等功能以及IPV6 的鄰居發(fā)現(xiàn)功能。
- >>> ans,unans=srp(Ether(dst="ff:ff:ff:ff:ff:ff")/ARP(pdst="172.17.51.0/24"),timeout=2)
- Begin emission:
- Finished sending 256 packets.
- *******************************************************************************.***********************************************************************************...........................
- Received 190 packets, got 162 answers, remaining 94 packets
- >>> ans.summary(lambda r: r[0].sprintf("%Ether.src% %ARP.pdst%") )
- 00:16:3e:0c:d1:ad 172.17.51.0
- 00:16:3e:0c:d1:ad 172.17.51.1
- 00:16:3e:0c:d1:ad 172.17.51.2
- 00:16:3e:0c:d1:ad 172.17.51.3
- 00:16:3e:0c:d1:ad 172.17.51.4
- 00:16:3e:0c:d1:ad 172.17.51.5
- 00:16:3e:0c:d1:ad 172.17.51.6
- 00:16:3e:0c:d1:ad 172.17.51.7
ICMP Ping
- ICMP掃描涉及無處不在的_ping程序_發(fā)送的標(biāo)準(zhǔn)數(shù)據(jù)包。
- 向目標(biāo)IP發(fā)送一個(gè)ICMP類型8(回顯請求)數(shù)據(jù)包,收到一個(gè)ICMP類型0(回顯應(yīng)答)的包表示機(jī)器存活。
- 現(xiàn)在許多主機(jī)和防火墻阻止這些數(shù)據(jù)包,因此基本的ICMP掃描是不可靠的。
- ICMP還支持時(shí)間戳請求和地址掩碼請求,可以顯示計(jì)算機(jī)的可用性。
- >>> ans,unans=sr(IP(dst="60.205.177.168-180")/ICMP())
- >>> ans.summary(lambda s:s[0].sprintf("{IP: %IP.dst% is alive}"))
- 60.205.177.168 is alive
- 60.205.177.169 is alive
- 60.205.177.171 is alive
- 60.205.177.172 is alive
- 60.205.177.175 is alive
- 60.205.177.174 is alive
- 60.205.177.176 is alive
- 60.205.177.179 is alive
- 60.205.177.178 is alive
- 60.205.177.180 is alive
服務(wù)發(fā)現(xiàn)(端口掃描)
TCP連接掃描
找了個(gè)網(wǎng)圖( 侵刪)
這里展示一下tcpdump抓到的握手包
- 192.168.2.1.35555 > 192.168.2.12.4444: Flags [S] seq=12345
- 192.168.2.12.4444 > 192.168.2.1.35555: Flags [S.], seq=9998 ack=12346
- 192.168.2.1.35555 > 192.168.2.12.4444: Flags [.] seq=12346 ack=9999
IP與端口號(hào)之間以'.'分隔,ACK用'.'表示,SYN用'S'表示,而[S.]則表示SYN+ACK
在Scapy中制作三次握手包
第1步-將客戶端的SYN發(fā)送到偵聽服務(wù)器
- 使用源IP地址和目標(biāo)IP地址制作一個(gè)IP頭。
- 制作一個(gè)TCP標(biāo)頭,在其中生成TCP源端口,設(shè)置服務(wù)器偵聽的目標(biāo)端口,設(shè)置TCP的flag SYN,并生成客戶端的seq。
- ip=IP(src="192.168.2.53", dst="60.205.177.168")
- syn_packet = TCP(sport=1500, dport=80, flags="S", seq=100)
第2步-監(jiān)聽服務(wù)器的響應(yīng)(SYN-ACK)
- 保存服務(wù)器的響應(yīng)。
- 獲取服務(wù)器的TCP序列號(hào),并將該值加1。
- synack_packet = sr1(ip/syn_packet)
- my_ack = synack_packet.seq+1
第3步從客戶端發(fā)送對服務(wù)器響應(yīng)的確認(rèn)(ACK)
- IP標(biāo)頭與初始SYN數(shù)據(jù)包具有相同的源和目標(biāo)。
- TCP報(bào)頭具有與syn數(shù)據(jù)包相同的TCP源端口和目標(biāo)端口,僅設(shè)置ACK位,由于SYN數(shù)據(jù)包消耗一個(gè)序列號(hào),因此將客戶端的ISN遞增1,將確認(rèn)值設(shè)置為遞增的服務(wù)器的序列號(hào)值。
- ack_packet = TCP(sport=1500, dport=80, flags="A", seq=101, ack=my_ack)
- send(ip/ack_packet)
完整代碼如下
- #!/usr/bin/python
- from scapy.all import *
- # 構(gòu)建payload
- get='GET / HTTP/1.0\n\n'
- #設(shè)置目的地址和源地址
- ip=IP(src="192.168.2.53",dst="60.205.177.168")
- # 定義一個(gè)隨機(jī)源端口
- port=RandNum(1024,65535)
- # 構(gòu)建SYN的包
- SYN=ip/TCP(sport=port, dport=80, flags="S", seq=42)
- # 發(fā)送SYN并接收服務(wù)器響應(yīng)(SYN,ACK)
- SYNACK=sr1(SYN)
- #構(gòu)建確認(rèn)包
- ACK=ip/TCP(sport=SYNACK.dport,dport=80,flags="A",seq=SYNACK.ack,ack=SYNACK.seq+1)/get
- #發(fā)送ack確認(rèn)包
- reply,error=sr(ACK)
- # 打印響應(yīng)結(jié)果
- print(reply.show())
SYN掃描
SYN掃描也稱為半開放掃描。可以使用這種策略來確定通信端口的狀態(tài)而無需建立完整的連接。客戶端首先向被測主機(jī)發(fā)送一個(gè)syn數(shù)據(jù)包,如果端口開放,那么服務(wù)端會(huì)響應(yīng)一個(gè)syn+ack的數(shù)據(jù)包,之后客戶端會(huì)發(fā)送rst數(shù)據(jù)包進(jìn)行重置。否則服務(wù)端會(huì)直接響應(yīng)一個(gè)rst包,表示端口沒有開放。如果我們發(fā)了大量的syn包而不去確認(rèn),服務(wù)端會(huì)繼續(xù)發(fā)送syn+ack的包,會(huì)不斷的消耗服務(wù)器的CPU和內(nèi)存,這也就是我們常說的syn泛洪攻擊了。
接下來我們使用scapy來模擬syn掃描
在單個(gè)主機(jī),單個(gè)端口上進(jìn)行SYN掃描
- 使用sr1功能發(fā)送并響應(yīng)數(shù)據(jù)包
- 使用sprintf方法在響應(yīng)中打印字段。(“ SA”標(biāo)志表示開放的端口,“ RA”標(biāo)志表示關(guān)閉的端口)
- >>> syn_packet = IP(dst='60.205.177.168')/TCP(dport=22,flags='S')
- >>> rsp=sr1(syn_packet)
- Begin emission:
- Finished sending 1 packets.
- ..*
- Received 3 packets, got 1 answers, remaining 0 packets
- >>> rsp.sprintf("%IP.src% %TCP.sport% %TCP.flags%")
- '60.205.177.168 ssh SA'
在單個(gè)主機(jī),多個(gè)端口上進(jìn)行SYN掃描
- >>> ans,unans=sr(IP(dst="60.205.177.168")/TCP(dport=(20,22),flags="S"))
- Begin emission:
- Finished sending 3 packets.
- ..*..**
- Received 7 packets, got 3 answers, remaining 0 packets
- >>> ans.summary(lambda s:s[1].sprintf("%TCP.sport% %TCP.flags%" ))
- ftp_data RA
- ftp RA
- ssh SA
對多個(gè)主機(jī),多個(gè)端口進(jìn)行SYN掃描
- make_table接受三個(gè)值,行,列和表數(shù)據(jù)。(在下面的示例中,目標(biāo)IP位于x軸上,目標(biāo)端口位于y軸上,響應(yīng)中的TCP標(biāo)志是表格數(shù)據(jù))
60.205.177.169的20和22端口沒有響應(yīng)數(shù)據(jù)包,猜測中間可能有設(shè)備(防火墻)給攔下了。
- >>> ans,unans = sr(IP(dst=["60.205.177.168-170"])/TCP(dport=[20,22,80],flags="S"))
- Begin emission:
- Finished sending 9 packets.
- ..*..**..*.................................................................................................................................................................................................................................................^C
- Received 251 packets, got 4 answers, remaining 5 packets
- >>> ans.make_table(lambda s: (s[0].dst, s[0].dport,s[1].sprintf("%TCP.flags%")))
- 60.205.177.168 60.205.177.169
- 20 RA -
- 22 SA -
- 80 SA SA
Fin 掃描
客戶端會(huì)發(fā)送帶有fin標(biāo)志(關(guān)閉連接)的數(shù)據(jù)包到服務(wù)端,當(dāng)服務(wù)端沒有響應(yīng)時(shí),表示端口是開放狀態(tài),否則會(huì)收到rst的包。
端口開放
- >>> fin_packet = IP(dst='60.205.177.168')/TCP(dport=4444,flags='F')
- >>> resp = sr1(fin_packet)
- Begin emission:
- Finished to send 1 packets.
- ^C
- Received 0 packets, got 0 answers, remaining 1 packets
端口關(guān)閉
- >>> fin_packet = IP(dst='60.205.177.168')/TCP(dport=4399,flags='F')
- >>> resp = sr1(fin_packet)
- >>> resp.sprintf('%TCP.flags%')
- 'RA'
NULL 掃描
null掃描會(huì)發(fā)送一個(gè)沒有設(shè)置任何flag的TCP數(shù)據(jù)包,當(dāng)收到rst的響應(yīng)包則表示端口關(guān)閉,否則表示端口開放,如果收到類型為3且代碼為1、2、3、9、10或13的ICMP錯(cuò)誤表示該端口已被過濾,獲取不到端口狀態(tài)。
端口關(guān)閉
- >>> null_scan_resp = sr1(IP(dst="60.205.177.168")/TCP(dport=4399,flags=""),timeout=1)
- >>> null_scan_resp.sprintf('%TCP.flags%')
- 'RA'
Xmas 掃描
XMAS掃描會(huì)發(fā)送帶有URG,PUSH,F(xiàn)IN標(biāo)志的TCP數(shù)據(jù)包,如果未接收到任何數(shù)據(jù)包,則認(rèn)為該端口處于打開狀態(tài);如果接收到RST數(shù)據(jù)包,則將該端口視為已關(guān)閉。如果收到類型為3且代碼為1、2、3、9、10或13的ICMP錯(cuò)誤表示該端口已被過濾,獲取不到端口狀態(tài)。
端口關(guān)閉
- >>> xmas_scan_resp=sr1(IP(dst="60.205.177.168")/TCP(dport=4399,flags=”FPU”),timeout=1)
- Begin emission:
- .Finished sending 1 packets.
- *
- Received 2 packets, got 1 answers, remaining 0 packets
- >>> xmas_scan_resp.sprintf('%TCP.flags%')
- 'RA'
UDP掃描
UDP掃描最常見于檢測DNS,SNMP和DHCP服務(wù)。客戶端會(huì)發(fā)送帶有要連接的端口號(hào)的UDP數(shù)據(jù)包。如果服務(wù)器使用UDP數(shù)據(jù)包響應(yīng)客戶端,那么該端口在服務(wù)器上是開放的。如果返回ICMP端口不可達(dá)的類型為3和code為3錯(cuò)誤數(shù)據(jù)包,表示該端口在服務(wù)器是關(guān)閉狀態(tài)。
- >>> udp_scan=sr1(IP(dst="60.205.177.168")/UDP(dport=53),timeout=1))
跟蹤路由
跟蹤路由技術(shù)基于IP協(xié)議的設(shè)計(jì)方式。IP標(biāo)頭中的TTL值被視為跳數(shù)限制。每當(dāng)路由器收到要轉(zhuǎn)發(fā)的數(shù)據(jù)包時(shí),它將TTL減1并轉(zhuǎn)發(fā)數(shù)據(jù)包。當(dāng)TTL達(dá)到0時(shí),路由器將向源計(jì)算機(jī)發(fā)送答復(fù),表示數(shù)據(jù)包已被丟棄。
各種工具背后的技術(shù)是相同的,但是實(shí)現(xiàn)它們的方式略有不同。Unix系統(tǒng)使用UDP數(shù)據(jù)報(bào)文,而Windows tracert則發(fā)送ICMP請求,Linux的tcptraceroute使用TCP協(xié)議。
使用ICMP進(jìn)行路由跟蹤
- >>> ans,unans=sr(IP(dst="49.232.152.189",ttl=(1,10))/ICMP())
- Begin emission:
- Finished sending 10 packets.
- *****.**........................................................................................................^C
- Received 112 packets, got 7 answers, remaining 3 packets
- >>> ans.summary(lambda s:s[1].sprintf("%IP.src%"))
- 10.36.76.142
- 10.54.138.21
- 10.36.76.13
- 45.112.216.134
- 103.216.40.18
- 9.102.250.221
- 10.102.251.214
使用tcp進(jìn)行路由跟蹤
- >>> ans,unans=sr(IP(dst="baidu.com",ttl=(1,10))/TCP(dport=53,flags="S"))
- Begin emission:
- Finished sending 10 packets.
- *********......................^C
- Received 31 packets, got 9 answers, remaining 1 packets
- >>> ans.summary(lambda s:s[1].sprintf("%IP.src% {ICMP:%ICMP.type%}"))
- 10.36.76.142 time-exceeded
- 10.36.76.13 time-exceeded
- 10.102.252.130 time-exceeded
- 117.49.35.150 time-exceeded
- 10.102.34.237 time-exceeded
- 111.13.123.150 time-exceeded
- 218.206.88.22 time-exceeded
- 39.156.67.73 time-exceeded
- 39.156.27.1 time-exceeded
Scapy包含一個(gè)內(nèi)置的traceroute()函數(shù)可以實(shí)現(xiàn)與上面相同的功能
- >>> traceroute("baidu.com")
- Begin emission:
- Finished sending 30 packets.
- ************************
- Received 24 packets, got 24 answers, remaining 6 packets
- 220.181.38.148:tcp80
- 2 10.36.76.13 11
- 3 10.102.252.34 11
- 4 117.49.35.138 11
- 5 116.251.112.185 11
- 6 36.110.217.9 11
- 7 36.110.246.201 11
- 8 220.181.17.150 11
- 14 220.181.38.148 SA
- 15 220.181.38.148 SA
- 16 220.181.38.148 SA
- 17 220.181.38.148 SA
- 18 220.181.38.148 SA
- 19 220.181.38.148 SA
- 20 220.181.38.148 SA
- 21 220.181.38.148 SA
- 22 220.181.38.148 SA
- 23 220.181.38.148 SA
- 24 220.181.38.148 SA
- 25 220.181.38.148 SA
- 26 220.181.38.148 SA
- 27 220.181.38.148 SA
- 28 220.181.38.148 SA
- 29 220.181.38.148 SA
- 30 220.181.38.148 SA
- (<Traceroute: TCP:17 UDP:0 ICMP:7 Other:0>,
- <Unanswered: TCP:6 UDP:0 ICMP:0 Other:0>
使用DNS跟蹤路由
我們可以通過在traceroute()函數(shù)的l4參數(shù)中指定完整的數(shù)據(jù)包來執(zhí)行DNS跟蹤路由
- >>> ans,unans=traceroute("60.205.177.168",l4=UDP(sport=RandShort())/DNS(qd=DNSQR(qname="thesprawl.org")))
- Begin emission:
- ****Finished sending 30 packets.
- .................
- Received 21 packets, got 4 answers, remaining 26 packets
- 60.205.177.168:udp53
- 1 10.2.0.1 11
- 2 114.242.29.1 11
- 4 125.33.185.114 11
- 5 61.49.143.2 11
本文轉(zhuǎn)載自微信公眾號(hào)「運(yùn)維開發(fā)故事」,可以通過以下二維碼關(guān)注。轉(zhuǎn)載本文請聯(lián)系運(yùn)維開發(fā)故事公眾號(hào)。