本文從實例代碼出發,講解了Python在網絡安全分析中的作用,以最基礎的示例向讀者展示了Python如何解析、發送、以及嗅探網絡中的數據包 系統環境:kali 并且具備了scapy,由于涉及底層網絡操作,文中的示例代碼均為linux下可用,無法直接復制到windows下使用
以下面代碼為例進行說明
首先導入socket(用于網絡編程)、struct(用于拆包解析數據包結構)、binascii(方便格式的轉化)
創建socket時指定socket.SOCK_RAW以接收原始數據包
Pkt為獲取到的數據包
Eth_hdr 為拆包得到的以太網頭部,使用unpack解出(請見下圖以太網幀格式)
以太網幀結構:
我們可以看到解出的第一部分(目的地址)為080027bc6ecc 我們可以看到解出的第二部分(源地址)為525400123502 我們可以看到解出的第三部分(長度\類型)為0800 圖片來自網絡,注意圖中說的4字節VLAN是不存在的 - -
下面看看自己的mac(eth1) 為080027bc6ecc,與上文結論相符
同理解出IP包頭里的源IP和目的IP,如第一張圖所示
IP包頭結構:
本節所用代碼如下:
#!python
#!/usr/bin/python
import socket
import struct
import binascii
s=socket.socket(socket.PF_PACKET,socket.SOCK_RAW,socket.htons(0x0800))
pkt = s.recvfrom(2048);
ethernetHeader=pkt[0][0:14]
eth_hdr = struct.unpack("!6s6s2s",ethernetHeader)
binascii.hexlify(eth_hdr[0])
binascii.hexlify(eth_hdr[1])
binascii.hexlify(eth_hdr[2])
ipHeader = pkt[0][14:34]
ip_hdr = struct.unpack("!12s4s4s",ipHeader)
print "Source IP address:"+socket.inet_ntoa(ip_hdr[1])
print "Destination IP address:"+socket.inet_ntoa(ip_hdr[2])
tcpHeader = pkt[0][34:54]
tcp_hdr = struct.unpack("!HH16s",tcpHeader)
本節示例輸出如下:
#!bash
[email protected]:~/Desktop/wooyunPython# ./0x01.py
Source IP address:123.125.125.99
Destination IP address:10.0.3.15
[email protected]:~/Desktop/wooyunPython# python
Python 2.7.3 (default, Jan 2 2013, 13:56:14)
[GCC 4.7.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import socket
>>> import struct
>>> import binascii
>>> s=socket.socket(socket.PF_PACKET,socket.SOCK_RAW,socket.htons(0x0800))
>>> pkt = s.recvfrom(2048);
>>> ethernetHeader=pkt[0][0:14]
>>> eth_hdr = struct.unpack("!6s6s2s",ethernetHeader)
>>> binascii.hexlify(eth_hdr[0])
'080027bc6ecc'
>>> binascii.hexlify(eth_hdr[1])
'525400123502'
>>> binascii.hexlify(eth_hdr[2])
'0800'
>>> ipHeader = pkt[0][14:34]
>>> ip_hdr = struct.unpack("!12s4s4s",ipHeader)
>>> print "Source IP address:"+socket.inet_ntoa(ip_hdr[1])
Source IP address:123.125.125.99
>>> print "Destination IP address:"+socket.inet_ntoa(ip_hdr[2])
Destination IP address:10.0.3.15
>>> tcpHeader = pkt[0][34:54]
>>> tcp_hdr = struct.unpack("!HH16s",tcpHeader)
本文所用代碼如下:
#!python
#!/usr/bin/python
import socket
import struct
rawSocket = socket.socket(socket.PF_PACKET,socket.SOCK_RAW,socket.htons(0x0800))
rawSocket.bind(("eth0",socket.htons(0x0800)))
packet = struct.pack("!6s6s2s",'\xaa\xaa\xaa\xaa\xaa\xaa','\xbb\xbb\xbb\xbb\xbb\xbb','\x08\x00')
rawSocket.send(packet+"Hello there")
首先socket類型選擇raw,然后bind一個interface就可以發包了,由于發送的是原始的數據包,因此數據包需要自己通過struct.pack包裝好
#!bash
[email protected]:~# scapy
INFO: Can't import python gnuplot wrapper . Won't be able to plot.
WARNING: No route found for IPv6 destination :: (no default route?)
Welcome to Scapy (2.2.0)
>>> ls()
ARP : ARP
這里還列出了一大堆的介紹,就不列出了
#!bash
>>> pkts = sniff(iface="eth1",count=3)
使用sniff監聽eth1接口的3個數據包
#!bash
>>> pkts
<Sniffed: TCP:3 UDP:0 ICMP:0 Other:0>
直接輸入pkts會顯示嗅探的概況
#!bash
>>> pkts[0]
<Ether dst=52:54:00:12:35:02 src=08:00:27:bc:6e:cc type=0x800 |<IP version=4L ihl=5L tos=0x0 len=116 id=30377 flags=DF frag=0L ttl=64 proto=tcp chksum=0xbdeb src=10.0.3.15 dst=123.125.125.99 options=[] |<TCP sport=49157 dport=http seq=2358039370 ack=1044674792 dataofs=5L reserved=0L flags=PA window=24120 chksum=0x656 urgptr=0 options=[] |<Raw load='GET /web_ime/patch.php HTTP/1.1\r\nHost: web.pinyin.sogou.com\r\nAccept: */*\r\n\r\n' |>>>>
>>> pkts[0].show()
使用show顯示更詳細
#!bash
###[ Ethernet ]###
dst= 52:54:00:12:35:02
src= 08:00:27:bc:6e:cc
type= 0x800
###[ IP ]###
version= 4L
ihl= 5L
tos= 0x0
len= 116
id= 30377
flags= DF
frag= 0L
ttl= 64
proto= tcp
chksum= 0xbdeb
src= 10.0.3.15
dst= 123.125.125.99
\options\
###[ TCP ]###
sport= 49157
dport= http
seq= 2358039370
ack= 1044674792
dataofs= 5L
reserved= 0L
flags= PA
window= 24120
chksum= 0x656
urgptr= 0
options= []
###[ Raw ]###
load= 'GET /web_ime/patch.php HTTP/1.1\r\nHost: web.pinyin.sogou.com\r\nAccept: */*\r\n\r\n'
>>>
>>> hexdump(pkts[1])
轉成十六進制形式更加親切
#!bash
0000 08 00 27 BC 6E CC 52 54 00 12 35 02 08 00 45 00 ..'.n.RT..5...E.
0010 00 28 82 39 00 00 40 06 F2 A7 7B 7D 7D 63 0A 00 .(.9..@...{}}c..
0020 03 0F 00 50 C0 05 3E 44 78 E8 8C 8C D3 96 50 10 ...P..>Dx.....P.
0030 FF FF D2 3F 00 00 00 00 00 00 00 00 ...?........
>>>
>>> wrpcap("demo.pcap",pkts)
寫出到pcap文件
#!bash
>>> read_pkts=rdpcap("demo.pcap")
從pcap文件中讀取
#!bash
>>> read_pkts
<demo.pcap: TCP:3 UDP:0 ICMP:0 Other:0>
檢查發現讀出與寫入是一致的
#!bash
>>>
>>> pkts=sniff(iface="eth1",filter="icmp",count=3)
使用filter來過濾想要捕獲的數據包類型
注意同時我另開一個shell做了一個ping來發送icmp包
#!bash
[email protected]:~# ping www.wooyun.org
PING wooyun.sinaapp.com (220.181.136.24) 56(84) bytes of data.
64 bytes from 220.181.136.24: icmp_req=1 ttl=54 time=17.1 ms
64 bytes from 220.181.136.24: icmp_req=3 ttl=54 time=11.8 ms
64 bytes from 220.181.136.24: icmp_req=4 ttl=54 time=23.8 ms
64 bytes from 220.181.136.24: icmp_req=5 ttl=54 time=17.1 ms
64 bytes from 220.181.136.24: icmp_req=6 ttl=54 time=5.63 ms
^C
--- wooyun.sinaapp.com ping statistics ---
6 packets transmitted, 5 received, 16% packet loss, time 5013ms
rtt min/avg/max/mdev = 5.636/15.135/23.824/6.086 ms
>>> pkts
<Sniffed: TCP:0 UDP:0 ICMP:3 Other:0>
發現截獲的通過filter果然都是icmp包
#!bash
>>>
>>> pkts[0]
<Ether dst=52:54:00:12:35:02 src=08:00:27:bc:6e:cc type=0x800 |<IP version=4L ihl=5L tos=0x0 len=84 id=0 flags=DF frag=0L ttl=64 proto=icmp chksum=0xc8cc src=10.0.3.15 dst=220.181.136.24 options=[] |<ICMP type=echo-request code=0 chksum=0xd856 id=0x218e seq=0x1 |<Raw load='\xcc\xa7\xc3S\x00\x00\x00\x00\xa2K\r\x00\x00\x00\x00\x00\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !"#$%&\'()*+,-./01234567' |>>>>
>>> pkts[1]
<Ether dst=08:00:27:bc:6e:cc src=52:54:00:12:35:02 type=0x800 |<IP version=4L ihl=5L tos=0x1 len=84 id=33433 flags= frag=0L ttl=54 proto=icmp chksum=0x9032 src=220.181.136.24 dst=10.0.3.15 options=[] |<ICMP type=echo-reply code=0 chksum=0xe056 id=0x218e seq=0x1 |<Raw load='\xcc\xa7\xc3S\x00\x00\x00\x00\xa2K\r\x00\x00\x00\x00\x00\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !"#$%&\'()*+,-./01234567' |>>>>
>>>
>>> icmp_str=str(pkts[0])
轉成str
#!bash
>>> icmp_str
'RT\x00\x125\x02\x08\x00\'\xbcn\xcc\x08\x00E\x00\x00T\x00\x00@\x00@\x01\xc8\xcc\n\x00\x03\x0f\xdc\xb5\x88\x18\x08\x00\xd8V!\x8e\x00\x01\xcc\xa7\xc3S\x00\x00\x00\x00\xa2K\r\x00\x00\x00\x00\x00\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !"#$%&\'()*+,-./01234567'
>>> recon=Ether(icmp_str)
從str重構數據包
#!bash
>>> recon
<Ether dst=52:54:00:12:35:02 src=08:00:27:bc:6e:cc type=0x800 |<IP version=4L ihl=5L tos=0x0 len=84 id=0 flags=DF frag=0L ttl=64 proto=icmp chksum=0xc8cc src=10.0.3.15 dst=220.181.136.24 options=[] |<ICMP type=echo-request code=0 chksum=0xd856 id=0x218e seq=0x1 |<Raw load='\xcc\xa7\xc3S\x00\x00\x00\x00\xa2K\r\x00\x00\x00\x00\x00\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !"#$%&\'()*+,-./01234567' |>>>>
>>> export_object(icmp_str)
eNprYApNCgphEDJl4mBQ35N3hoPBlYEhhIHBgcGB8cQZLgZm/jtbOyQ4GG6EKfYxMJ5ZfjiYAQgW
efOCKAYBQSFhEVExcQlJKWkZWTl5BUUlZRVVNXUNTS1tHV09fQNDI2MTUzPzQkY9AMQIFOY=
導出可讀base64對象
#!bash
>>> newPkt=import_object()
eNprYApNCgphEDJl4mBQ35N3hoPBlYEhhIHBgcGB8cQZLgZm/jtbOyQ4GG6EKfYxMJ5ZfjiYAQgW
efOCKAYBQSFhEVExcQlJKWkZWTl5BUUlZRVVNXUNTS1tHV09fQNDI2MTUzPzQkY9AMQIFOY=
輸入完成后按(ctrl+D)結束輸入 從base64中讀回對象
#!bash
>>> newPkt
'RT\x00\x125\x02\x08\x00\'\xbcn\xcc\x08\x00E\x00\x00T\x00\x00@\x00@\x01\xc8\xcc\n\x00\x03\x0f\xdc\xb5\x88\x18\x08\x00\xd8V!\x8e\x00\x01\xcc\xa7\xc3S\x00\x00\x00\x00\xa2K\r\x00\x00\x00\x00\x00\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !"#$%&\'()*+,-./01234567'
>>> Ether(newPkt)
<Ether dst=52:54:00:12:35:02 src=08:00:27:bc:6e:cc type=0x800 |<IP version=4L ihl=5L tos=0x0 len=84 id=0 flags=DF frag=0L ttl=64 proto=icmp chksum=0xc8cc src=10.0.3.15 dst=220.181.136.24 options=[] |<ICMP type=echo-request code=0 chksum=0xd856 id=0x218e seq=0x1 |<Raw load='\xcc\xa7\xc3S\x00\x00\x00\x00\xa2K\r\x00\x00\x00\x00\x00\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !"#$%&\'()*+,-./01234567' |>>>>
到此我們成功原路返回了導出的數據包
>>>
接下來就是利用scapy發送數據包了
首先Ether/ARP構造一個ARP數據包,之后srp1發送出去,從wireshark中看發現數據包已經成功發送了,不過在這里理論上是可以收到response的,測試時由于不明原因沒有收到
#!bash
[email protected]:~# scapy
INFO: Can't import python gnuplot wrapper . Won't be able to plot.
WARNING: No route found for IPv6 destination :: (no default route?)
Welcome to Scapy (2.2.0)
>>> from scapy.all import *
>>> pkt=Ether(dst="ff:ff:ff:ff:ff:ff")/ARP(pdst='192.168.1.3',hwdst="ff:ff:ff:ff:ff:ff")
>>> pkt
<Ether dst=ff:ff:ff:ff:ff:ff type=0x806 |<ARP hwdst=ff:ff:ff:ff:ff:ff pdst=192.168.1.3 |>>
>>> pkt.show()
###[ Ethernet ]###
dst= ff:ff:ff:ff:ff:ff
src= 00:00:00:00:00:00
type= 0x806
###[ ARP ]###
hwtype= 0x1
ptype= 0x800
hwlen= 6
plen= 4
op= who-has
hwsrc= 00:00:00:00:00:00
psrc= 192.168.1.3
hwdst= ff:ff:ff:ff:ff:ff
pdst= 192.168.1.3
>>> srp1(pkt)
Begin emission:
Finished to send 1 packets.
..............................................................................^C
Received 78 packets, got 0 answers, remaining 1 packets
>>>