1. 程式人生 > >Python黑客程式設計3網路資料監聽和過濾

Python黑客程式設計3網路資料監聽和過濾

Python黑客程式設計3網路資料監聽和過濾

課程的實驗環境如下:

作業系統:kali Linux 2.0

程式設計工具:Wing IDE

Python版本:2.7.9

涉及到的主要python模組:pypcapdpkt,scapyscapy-http

涉及到的幾個python網路抓包和分析的模組,dpktscapykali linux 2.0 中預設已經被安裝,如果你的系統中沒有需要手動安裝一下,下面是軟體包安裝的簡單說明。

kali下安裝pypcap需要兩步(如果在其他系統上可能還需要安裝python-dev):

apt-get install libpcap-dev

pip install pypcap

使用pip安裝scapy

[email protected]:/home/pycharm# pip install scapy

使用pip安裝scapy-http

[email protected]:/home/pycharm# pip install scapy-http

基礎環境準備好之後,我還要再嘮叨一下你必須要掌握的基礎。

1.1  技術沒有黑白,網路程式設計的基礎是協議

把程式設計掛上黑客的名義,多少有些標題黨。程式碼怎麼寫,程式怎麼用,完全是技術問題。不會因為叫網路程式設計就低人一等,叫黑客程式設計也不會變得神祕或者高大上,程式碼就在那裡,不卑微也不高尚。所以學習程式設計,要有顆平常心。

很多聽課的同學和我反應,網路程式設計格外的吃力,繁瑣,要實現某種功能,如果Google不到類似的程式碼就無法下手。各種語言或者框架針對網路程式設計的實現基本都相同,因為我們接觸到網路通訊都基於統一的規範和標準,語言和框架只是在用自己的方式去描述這個規範而已。本質的問題來了,如果你連基本的網路通訊的四層模型都不懂,對TCP/IP協議族毫無概念,那麼我奉勸你先不要著急敲程式碼,找本書,開啟WireShark這樣的工具好好做做練習。

本次課程中的所有案例,其實都在遵循一個基本的思路(其他網路通訊場景類似):

初始化乙太網資料包物件à乙太網資料包分離出ip資料包àIP資料包分離傳輸層資料包à傳輸層資料包分離應用層資料包。

只要我們具備基礎的網路知識,結合程式中各個物件提供的欄位就能得到我們想要的任何基礎資訊,在此基礎上做些資訊處理就能完成大部分網路監聽和資料處理的任務。附上幾幅圖,如果這方面有欠缺的話,請立即去充電吧!

乙太網幀格式

ip資料包格式

Tcp資料包格式

1.2  使用pypcap實時抓包

pypcap進行實時的資料包捕獲,使用上很簡單,我們先看一小段示例程式碼:

import pcap

pc=pcap.pcap('wlan0')  #注,引數可為網絡卡名,如eth0

pc.setfilter('tcp port 80')    #2.設定監聽過濾器

for ptime,pdata in pc:    #ptime為收到時間,pdata為收到資料

    print ptime,pdata    #...

在上面的程式碼中,我們通過import pcap首先引入pypcap包,然後初始化一個pcap類例項,建構函式需要傳入一個網絡卡用來監聽,我們可以通過ifconfig獲取當前機器上的網絡卡。

pcap類的setfilter方法用來設定監聽過濾條件,這裡我們設定過濾的資料包為tcp協議80埠的資料。之後程式就進入監聽狀態了。

接下來我們迴圈輸出接收到的資料,ptime為時間,pdata的資料,預設資料列印為ascii字元,效果如下:

在抓到資料包之後,下一步就需要對資料進行解析,這裡我們引入dpkt元件包。

1.3  使用dpkt 解析資料包

dpkt,簡單來說是一個數據包解析工具,可以解析離線/實時pcap資料包。

1.3.1 實時解析

我們以下面的程式碼為例,講解基本應用。

import pcap

import dpkt

def captData():

    pc=pcap.pcap('wlan0')  #注,引數可為網絡卡名,如eth0

    pc.setfilter('tcp port 80')    #設定監聽過濾器

    for ptime,pdata in pc:    #ptime為收到時間,pdata為收到資料

        anlyCap(pdata);

def anlyCap(pdata):

    p=dpkt.ethernet.Ethernet(pdata)

    if p.data.__class__.__name__=='IP':

        ip='%d.%d.%d.%d'%tuple(map(ord,list(p.data.dst)))

        if p.data.data.__class__.__name__=='TCP':

            if p.data.data.dport==80:

                print p.data.data.data # http 請求的資料

captData();

在上面程式碼中,我們首先匯入dpkt包。這段程式碼中新增了一個anlyCap方法,該方法接收由pcap捕獲的http資料包,然後先取得ip資料報文,從ip報文中再提取tcp資料包,最後從tcp資料包中提取http請求的資料,將其打印出來。

對於資料包的分析,新手可能會感到迷茫,如何選擇合適的協議和方法來分析呢?這個問題的答案不在程式碼,而在於網路通訊協議本身的掌握和理解。

回到上面的程式碼,我們想要分析http請求的資料,http是應用層協議,通過TCP協議來傳輸資料,那麼TCP資料又被封裝在IP資料報文中。使用dpkt的第一步就是選擇資料包型別,這裡當然是要選擇乙太網資料包了。

按照網路協議,層層剝離,會解析到所有你想要的資料。

1.3.2 解析離線資料包

下面我們來看一個解析離線資料包的例子。

import dpkt

import socket

#----------------------------------------------------------------------

def printPcap(pcap):

    """"""

    for(ts,buf) in pcap:

        try:

            eth=dpkt.ethernet.Ethernet(buf);

            ip=eth.data;

            src=socket.inet_ntoa(ip.src);

            dst=socket.inet_ntoa(ip.dst);

            tcp=ip.data;#tcp.dport  tcp.sport

            print '[+]Src: '+src+ ' --->Dst: '+ dst

        except:

            pass;

#----------------------------------------------------------------------

def main():

    """"""

    f=open('/home/pcap/test.pcap');#1.open file

    pcap=dpkt.pcap.Reader(f);# init pcap obj

    printPcap(pcap);

if __name__ == '__main__':

    main();

首先我準備了一個測試的抓包檔案—test.pcap,該檔案是我使用wiresharkwindows上抓取的資料包,現在使用程式碼對齊進行基本的分析。在方法printPcap中,獲取ip資料報的內容,然後獲取它的源ip和目標ip資料,通過socket.inet_ntoa方法轉換成ip字串,最後打印出來。結果如下圖所示:

1.4 使用Scapy進行資料監聽

Scapy的是一個強大的互動式資料包處理程式(使用python編寫)。它能夠偽造或者解碼大量的網路協議資料包,能夠傳送、捕捉、匹配請求和回覆包等等。它可以很容易地處理一些典型操作,比如埠掃描,tracerouting,探測,單元 測試,攻擊或網路發現(可替代hpingNMAParpspoofARP-SKarpingtcpdumptetherealP0F等)。 最重要的他還有很多更優秀的特性——傳送無效資料幀、注入修改的802.11資料幀、在WEP上解碼加密通道(VOIP)、ARP快取攻擊(VLAN 等,這也是其他工具無法處理完成的。

Scapy可以單獨使用,也可以在python中呼叫。

1.4.1 Scapy基本使用

瞭解Scapy的基本使用和支援的方法,首先我們從終端啟動scapy,進入互動模式。

ls()顯示scapy支援的所有協議。

ls()函式的引數還可以是上面支援的協議中的任意一個的型別屬性,也可以是任何一個具體的資料包,如ls(TCP),ls(newpacket)等。

lsc()列出scapy支援的所有的命令

本篇文章使用的只是scapy眾多命令中的一個,sniff

conf:顯示所有的配置資訊。conf變數儲存了scapy的配置資訊。

help()顯示某一命令的使用幫助,如help(sniff)

show()顯示指定資料包的詳細資訊。例如,這裡我們先建立一個IP資料包,然後呼叫show方法。

sprintf()輸出某一層某個引數的取值,如果不存在就輸出”??”,具體的format格式是:%[[fmt][r],][layer[:nb].]field%,詳細的使用參考<Security Power Tools>146頁。

%[[fmt][r],][layer[:nb].]field%

layer:協議層的名字,如EtherIPDot11TCP等。

filed:需要顯示的引數。

nb:當有兩個協議層有相同的引數名時,nb用於到達你想要的協議層。

r:是一個標誌。當使用r標誌時,意味著顯示的是引數的原始值。例如,TCP標誌中使用人類可閱讀的字串’SA’表示SYNACK標誌,而其原始值是18.

1.4.2  sniff

Scapy的功能如此強大,足夠寫個系列了,本文只關注sniff這一個方法。

sniff方法是用來嗅探資料的,我們首先使用help檢視一下此方法的使用說明:

sniff(count=0, store=1, offline=None, prn=None, lfilter=None, L2socket=None, timeout=None, opened_socket=None, stop_filter=None, *arg, **karg)

    Sniff packets

    sniff([count=0,] [prn=None,] [store=1,] [offline=None,] [lfilter=None,] + L2ListenSocket args) -> list of packets

      count: number of packets to capture. 0 means infinity

      store: wether to store sniffed packets or discard them

        prn: function to apply to each packet. If something is returned,

             it is displayed. Ex:

             ex: prn = lambda x: x.summary()

    lfilter: python function applied to each packet to determine

             if further action may be done

             ex: lfilter = lambda x: x.haslayer(Padding)

    offline: pcap file to read packets from, instead of sniffing them

    timeout: stop sniffing after a given time (default: None)

    L2socket: use the provided L2socket

    opened_socket: provide an object ready to use .recv() on

    stop_filter: python function applied to each packet to determine

                 if we have to stop the capture after this packet

                 ex: stop_filter = lambda x: x.haslayer(TCP)

除了上面介紹的幾個引數,sniff()函式還有一個重要的引數是filter,用來表示想要捕獲資料包型別的過濾器,如只捕獲ICMP資料包,則filter=”ICMP”;只捕獲80埠的TCP資料包,則filter=”TCP and (port 80)”其他幾個重要的引數有:count表示需要不活的資料包的個數;prn表示每個資料包處理的函式,可以是lambda表示式,如prn=lambda x:x.summary()timeout表示資料包捕獲的超時時間。

sniff(filter="icmp and host 66.35.250.151", count=2)

這段程式碼過濾icmp協議,host地址為66.35.250.151,捕獲資料包個數為2個。

sniff(iface="wifi0", prn=lambda x: x.summary())

這段程式碼繫結網絡卡wifi0,對捕獲的資料包使用summary進行資料彙總。

sniff(iface="eth1", prn=lambda x: x.show())

這段程式碼繫結網絡卡eth1,對資料包呼叫show方法,顯示基本資訊。

下面我們看具體的一段程式碼:

from scapy.all import *

ap_list = []

def PacketHandler(pkt) :

    if pkt.haslayer(Dot11) :

        if pkt.type == 0 and pkt.subtype == 8 :

            if pkt.addr2 not in ap_list :

                ap_list.append(pkt.addr2)

                print "AP MAC: %s with SSID: %s " %(pkt.addr2, pkt.info)

sniff(iface="wlan0mon", prn = PacketHandler)

上面這段程式碼對繫結網絡卡WLAN0mon,對每個資料包呼叫PacketHandler方法進行解析。PacketHandler實際上是通過資料包過濾可訪問的無線網路SSID

1.4.3 Scapy-http

Scapy-http直接將 資料包格式化成 http資料資訊,免去自己構建http資料結構進行解析的麻煩。

import scapy_http.http as HTTP

from scapy.all import *

from scapy.error import Scapy_Exception

count=0

def pktTCP(pkt):

    global count

    count=count+1

    print count

    if HTTP.HTTPRequest or HTTP.HTTPResponse in pkt:

        src=pkt[IP].src

        srcport=pkt[IP].sport

        dst=pkt[IP].dst

        dstport=pkt[IP].dport

        test=pkt[TCP].payload

        if HTTP.HTTPRequest in pkt:

            #print "HTTP Request:"

            #print test

            print "======================================================================"

        if HTTP.HTTPResponse in pkt:

            print "HTTP Response:"

            try:

                headers,body= str(test).split("\r\n\r\n", 1)

                print headers

            except Exception,e:

                print e

            print "======================================================================"

    else:

        #print pkt[IP].src,pkt[IP].sport,'->',pkt[TCP].flags

        print 'other'

sniff(filter='tcp and port 80',prn=pktTCP,iface='wlan0')

上面的這段程式碼,我們引入scapy_http.http,該元件包可以直接將Http請求的TCP資料包格式化成HTTPRequest或者HTTPResponse物件,這大大方便了我們對HTTP資料的分析。

1.4.4  綜合例項--net-creds

net-creds是一個小的開源程式,由python編寫,主要是從網路或者pcap中嗅探敏感資料。可以嗅探以下型別的資料:

·URLs visited

·POST loads sent

·HTTP form logins/passwords

·HTTP basic auth logins/passwords

·HTTP searches

·FTP logins/passwords

·IRC logins/passwords

·POP logins/passwords

·IMAP logins/passwords

·Telnet logins/passwords

·SMTP logins/passwords

·SNMP community string

·NTLMv1/v2 all supported protocols like HTTP, SMB, LDAP, etc

·Kerberos

基本用法如下:

自動選擇網絡卡進行嗅探:

sudo python net-creds.py

指定嗅探的網絡卡:

sudo python net-creds.py -i eth0

忽略指定IP的資料包:

sudo python net-creds.py -f 192.168.0.2

pcap檔案中過濾資訊:

python net-creds.py -p pcapfile

建議讀者能夠靜下心來閱讀該程式的原始碼,本身並不是很複雜,難度不高。下面摘幾段程式碼。

    ipr = Popen(['/sbin/ip', 'route'], stdout=PIPE, stderr=DN)

        for line in ipr.communicate()[0].splitlines():

            if 'default' in line:

                l = line.split()

                iface = l[4]

                return iface

上面這一段程式碼是利用Popen元件和PIPE元件來自動查詢網絡卡。

def telnet_logins(src_ip_port, dst_ip_port, load, ack, seq):

    '''

    Catch telnet logins and passwords

    '''

    global telnet_stream

    msg = None

    if src_ip_port in telnet_stream:

        # Do a utf decode in case the client sends telnet options before their username

        # No one would care to see that

        try:

            telnet_stream[src_ip_port] += load.decode('utf8')

        except UnicodeDecodeError:

            pass

        # \r or \r\n or \n terminate commands in telnet if my pcaps are to be believed

        if '\r' in telnet_stream[src_ip_port] or '\n' in telnet_stream[src_ip_port]:

            telnet_split = telnet_stream[src_ip_port].split(' ', 1)

            cred_type = telnet_split[0]

            value = telnet_split[1].replace('\r\n', '').replace('\r', '').replace('\n', '')

            # Create msg, the return variable

            msg = 'Telnet %s: %s' % (cred_type, value)

            printer(src_ip_port, dst_ip_port, msg)

            del telnet_stream[src_ip_port]

    # This part relies on the telnet packet ending in

    # "login:", "password:", or "username:" and being <750 chars

    # Haven't seen any false+ but this is pretty general

    # might catch some eventually

    # maybe use dissector.py telnet lib?

    if len(telnet_stream) > 100:

        telnet_stream.popitem(last=False)

    mod_load = load.lower().strip()

    if mod_load.endswith('username:') or mod_load.endswith('login:'):

        telnet_stream[dst_ip_port] = 'username'

    elif mod_load.endswith('password:'):

        telnet_stream[dst_ip_port] = 'passwo