2014-12-18 13 views
5

Sto provando a codificare un semplice sniffer in Scapy, che stampa solo pacchetti HTTP con solo metodo GET. Ecco il codice:HTTP GET sniffer di pacchetti in Scapy

#!/usr/bin/python 
from scapy.all import * 

def http_header(packet): 
     http_packet=str(packet) 
     if http_packet.find('GET'): 
       print GET_print(packet) 
     print packet 
def GET_print(packet1): 
     print "***************************************GET PACKET****************************************************" 
     print packet1 

     print "*****************************************************************************************************" 


sniff(iface='eth0',prn=http_header) 

Ecco l'output:

***************************************************************************************************** 
None 
T��à 
    )�pEa��@@���h��#/��t 
          �}LGku���U 
oTE��I(��Ͻ�9qi���S��?�� 
          XuW�F=���-�k=X:� 
***************************************GET PACKET**************************************************** 
T��à 
    )�pE���@@���h��#/��t 
           ʪLGku���� 
oTE��I�K��AH�*�e��>�v1#D�(mG5T�o�?��8��喷╭���Ի�"�KT^�'�mB���]�����k> 
                       �_x�X�����8V?�Ǽw/�Z�=���N�À��\r�����)+}���l�c�9��j;���h��5�T�9Hۖ/O��)��P 
     މY�qf爂�%�_`��6x��5D�I3���O� 
t��tpI#�����$IC��E�� 
        �G� 
J��α���=�]��vһ���b5^|P��DK�)uq�2��ț�w� 
        tB������y=���n�i�r�.D6�kI�a���6iC���c'��0dPqED�4����[�[��hGh̃��~|Y/�>`\6yP Dq١?T��Mѵ���f�;���Җ��Ǵ gY���di�_x�8| 
eo�p�xW9��=���vŅYe�}�T�ۨɑy�^�C 
-�_(�<�{����}�������r 
$��J�k-�9����}�Ϡf�27��QKԛ�`�GY�8��Sh���[email protected]�E9�Rϔ�&a�/vkф��6�DF`�/9�I�d(��-��[A 
                        ��)pP��y\ռj]���8�_���vf�b����I7�������+�P<_` 
***************************************************************************************************** 

Quello che mi aspetto è:

GET/HTTP/1.1 
    Host: google.com 
    User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:24.0) Gecko/20140722 Firefox/24.0 Iceweasel/24.7.0 
    Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 
    Accept-Language: en-US,en;q=0.5 
    Accept-Encoding: gzip, deflate 
    Cookie: PREF=ID=758a20b5fbd4eac9:U=2b2dedf6c84b001f:FF=0:TM=1412150291:LM=1415430021:S=Q-QemmrLqsSsEA9i; NID=67=mRdkPVhtImrOTLi5I1e5JM22J7g26jAcdiDEjj9C5q0H5jj0DWRX27hCM7gLJBeiowW-8omSv-1ycH595SW2InWX2n1JMMNh6b6ZrRsZ9zOCC2a-vstOQnBDSJu6K9LO 
    Connection: keep-alive 

Che cosa posso fare per ottenere il mio risultato previsto?

risposta

8

È necessario utilizzare sprintf function del pacchetto anziché stampare il pacchetto stesso. È inoltre necessario dividere la stringa restituita da esso e unirsi di nuovo insieme con caratteri di nuova riga, altrimenti si sputa tutto su una riga:

#!/usr/bin/python 
from scapy.all import * 

def http_header(packet): 
     http_packet=str(packet) 
     if http_packet.find('GET'): 
       return GET_print(packet) 

def GET_print(packet1): 
    ret = "***************************************GET PACKET****************************************************\n" 
    ret += "\n".join(packet1.sprintf("{Raw:%Raw.load%}\n").split(r"\r\n")) 
    ret += "*****************************************************************************************************\n" 
    return ret 

sniff(iface='eth0', prn=http_header, filter="tcp port 80") 

Ho anche aggiunto un filtro per la porta TCP 80, ma questo potrebbe essere rimosso se necessario.

uscita Esempio:

***************************************GET PACKET**************************************************** 
'GET /projects/scapy/doc/usage.html HTTP/1.1 
Host: www.secdev.org 
Connection: keep-alive 
Cache-Control: max-age=0 
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8 
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.65 Safari/537.36 
Referer: https://www.google.co.uk/ 
Accept-Encoding: gzip, deflate, sdch 
Accept-Language: en-GB,en;q=0.8,en-US;q=0.6 
If-None-Match: "28c84-48498d5654df67640-gzip" 
If-Modified-Since: Mon, 19 Apr 2010 15:44:17 GMT 

' 
***************************************************************************************************** 

Pierre fa notare che è possibile farla finita con la funzione http_header interamente utilizzando l'argomento lfilter-sniff(). Mi sono permesso di rendere il codice un po 'più sintetica, allo stesso tempo:

#!/usr/bin/python 
from scapy.all import * 

stars = lambda n: "*" * n 

def GET_print(packet): 
    return "\n".join((
     stars(40) + "GET PACKET" + stars(40), 
     "\n".join(packet.sprintf("{Raw:%Raw.load%}").split(r"\r\n")), 
     stars(90))) 

sniff(
    iface='eth0', 
    prn=GET_print, 
    lfilter=lambda p: "GET" in str(p), 
    filter="tcp port 80") 
+2

Grazie! funziona come un fascino! –

+1

La tua risposta è molto buona. Vorrei aggiungere che puoi usare una funzione dedicata per fare il lavoro di filtraggio, sbarazzarti di 'http_header()' e cambiare la tua chiamata 'sniff()' a 'sniff (iface = 'eth0', prn = GET_print, lfilter = lambda p: "GET" in str (p), filter = "tcp port 80") '. – Pierre

+1

Grazie a @Pierre ho aggiornato la risposta per includere il tuo codice. –

2

C'è un modulo HTTP Scapy che è possibile installare eseguendo pip install scapy-http. Una volta installato, è possibile importarlo eseguendo import scapy_http.http. Questo è separato dal modulo scapy, ma aggiunge funzionalità allo scapy, quindi è ancora necessario importare scapy come faresti normalmente.

Una volta importati, modificare la linea di filtro per

sniff(iface="eth0", 
prn=GET_print, 
lfilter= lambda x: x.haslayer(scapy_http.http.HTTPRequest)) 

ho rimosso l'opzione filter="tcp and port 80" perché, utilizzando il lfilter http restituirà tutte le query richiesta HTTP indipendentemente dalla porta, ad eccezione SSL per l'ovvia ragione che non può essere annusato nelle solite circostanze. Si consiglia di mantenere l'opzione filter per motivi di prestazioni.

2

Avevo commentato un modo per migliorarlo ma ho deciso di montare insieme una soluzione più completa. Ciò non avrà il pacchetto asterisco si interrompe, ma invece stampa solo le intestazioni come dizionario piuttosto stampato in modo che questo possa funzionare o meno, ma puoi anche personalizzarlo in base alle tue esigenze. A parte la formattazione, questo sembra il mezzo più efficace pubblicato finora su questa domanda e puoi delegare a una funzione per aggiungere la formattazione e decostruire ulteriormente il dict.

#!/usr/bin/env python2 

import argparse 
import pprint 
import sys 

# Suppress scapy warning if no default route for IPv6. This needs to be done before the import from scapy. 
import logging 
logging.getLogger("scapy.runtime").setLevel(logging.ERROR) 


# Try to import sniff from scapy.all and show error w/ install instructions if it cannot be imported. 
try: 
    from scapy.all import sniff 
except ImportError: 
    sys.stderr.write("ERROR: You must have scapy installed.\n") 
    sys.stderr.write("You can install it by running: sudo pip install -U 'scapy>=2.3,<2.4'") 
    exit(1) 

# Try to import scapy_http.http and show error w/ install instructions if it cannot be imported. 
try: 
    import scapy_http.http 
except ImportError: 
    sys.stderr.write("ERROR: You must have scapy-http installed.\n") 
    sys.stderr.write("You can install it by running: sudo pip install -U 'scapy>=1.8'") 
    exit(1) 


if __name__ == "__main__": 
    # Parser command line arguments and make them available. 
    parser = argparse.ArgumentParser(
     formatter_class=argparse.ArgumentDefaultsHelpFormatter, 
     description="Print HTTP Request headers (must be run as root or with capabilities to sniff).", 
    ) 
    parser.add_argument("--interface", "-i", help="Which interface to sniff on.", default="eth0") 
    parser.add_argument("--filter", "-f", help='BPF formatted packet filter.', default="tcp and port 80") 
    parser.add_argument("--count", "-c", help="Number of packets to capture. 0 is unlimited.", type=int, default=0) 
    args = parser.parse_args() 

    # Sniff for the data and print it using lambda instead of writing a function to pretty print. 
    # There is no reason not to use a function you write for this but I just wanted to keep the example simply while 
    # demoing how to only match HTTP requests and to access the HTTP headers as pre-created dict's instead of 
    # parsing the data as a string. 
    sniff(iface=args.interface, 
      promisc=False, 
      filter=args.filter, 
      lfilter=lambda x: x.haslayer(scapy_http.http.HTTPRequest), 
      prn=lambda pkt: pprint.pprint(pkt.getlayer(scapy_http.http.HTTPRequest).fields, indent=4), 
      count=args.count 
    )