Se anche voi siete appassionati di penetration testing, vi potrà tornare utile creare dei tools personalizzati da utilizzare durante le tue analisi.
Python è un fantastico e potente linguaggio di programmazione che mette a disposizione tantissime librerie che possono fare al case nostro, con il vantaggio di essere di facile comprensione.
Qui di seguito troverete il codice sorgente, commentato in maniera dettagliata, di uno sniffer di rete principalmente dedicato alla cattura del traffico POP/IMAP.
Non vi spaventate per la lunghezza del codice, la maggior parte delle righe sono commenti.
Requisiti
1) Interprete Python 2.7.x ( versione su cui ho fatto i test, ma anche la 2.6.x non dovrebbe aver problemi)
2) Conoscenze minime di Python
3) Conoscenze minime dei protocolli, in particolare Internet Protocol
4) Account privilegiato (Administrator/root) di sistema per eseguire lo script
Codice
# Python POP/IMAP Sniffer by Claudio Viviani # # http://www.homelab.it # [email protected] # [email protected] # # https://www.facebook.com/homelabit # https://twitter.com/homelabit # https://plus.google.com/+HomelabIt1/ # https://www.youtube.com/channel/UCqqmSdMqf_exicCe_DjlBww # # # Moduli - 0x1 - Questo modulo gestisce i socket di sistema # http://docs.python.it/html/lib/module-socket.html import socket # Moduli - 0x2 - Questo modulo gestisce prametri e funzioni specifiche per il sistema # http://docs.python.it/html/lib/module-sys.html import sys # Moduli - 0x3 - Questo modulo effettua conversioni tra i valori Python e le strutture C rappresentate come stringhe Python. # Puo' venire impiegato per gestire dati binari memoprizzati su file o provienienti da una connessione di rete, # come esempio di sorgenti di dati, fra i tanti possibili. # http://docs.python.it/html/lib/module-struct.html from struct import * banner=''' ############################# Python POP/IMAP Sniffer by Claudio Viviani ############################# ''' print(banner) # Creo il socket utilizzando la funzione PF_PACKET e SOCK_RAW per operare al livello di device driver layer, # rimanendo in ascolto su tutti i protocolli ETH_P_ALL (0x0003) con l'ordine dei byte da network a host (ntohs) # https://it.wikipedia.org/wiki/Ordine_dei_byte # # E' necessario un account privilegiato (Administrator/root) per creare un un socket raw pf_packet try: s = socket.socket( socket.PF_PACKET , socket.SOCK_RAW , socket.ntohs(0x0003)) # Creo un ciclo infinito per raccogliere tutti i pacchetti che transitano dal device while True: # Nel socket creo un buffer per memorizzarci il pacchetto packet = s.recvfrom(65565) # I pacchetti arrivano come tuple, a noi interessano i dati presenti nella prima tupla [0] packet = packet[0] # Phisical Layer - 0x1 - I primi 14 byte di un frame ethernet compongono l'ethernet header e sono suddivisi in 3 parti: # # 1) Destination MAC Address (6 byte chars) # 2) Source MAC Address (6 byte chars) # 4) Ether type o Protocollo (2 byte int) # # Rif: https://it.wikipedia.org/wiki/Frame_Ethernet # # Lunghezza frame interessata 6+6+2 eth_length = 14 # Del pacchetto prendo i primi 14 byte (Vedi commento Phisical Layer - 0x1 -) eth_header = packet[:eth_length] # Tramite la funzione unpack del modulo struct formatto i dati del pacchetto ricevuto da binario al formato desiderato. # (Per la tabella dei formatti fare riferimento al link nel commento Moduli - 0x3 -) # Seguendo la tabella dei formatti, utilizzo i parametri: # 6s (dest macs - stringa di 6 byte) # 6s (source mac - stringa di 6 byte) # H (ether type - intero di 2 byte) # # Utilizzo il simbolo "!" per specificare che l'ordine dei byte del pacchetto binario e' big-endian. # I pacchetti di rete utilizzano l'ordine big-endian # https://it.wikipedia.org/wiki/Ordine_dei_byte eth = unpack('!6s6sH' , eth_header) # # Tramite la funzione ntohs del modulo socket converto l'ordine dei byte da network ad host # estrapolando il protocollo che si trova nella terza tupla (eth[2]) eth_protocol = socket.ntohs(eth[2]) # # Considero solo il protocollo IP che viene classificato col valore intero 8 if eth_protocol == 8 : # Per estrapolare l'ip header del pacchetto, prendo i 20 byte successivi all'ethernet,quindi da 14 al 34 ip_header = packet[eth_length:20+eth_length] # Tramite la funzione unpack del modulo struct formatto i dati del pacchetto ricevuto da binario al formato desiderato. # (Per la tabella dei formatti fare riferimento al link nel commento Moduli - 0x3 -) iph = unpack('!BBHHHBBH4s4s' , ip_header) # Per estrapolare dal pacchetto il valore di lunghezza dell'ip header devo: # 1) Selezionare la prima tupla di 1 byte (8bit) dove trovero' i valori dell'ip version(4 bit)+header length(4bit) # https://it.wikipedia.org/wiki/IPv4 version_ihl = iph[0] # 2) Tramite gli operatori bitwise, in particolare con l'opratore AND (&), # estrapolo gli ultimi 4 bit(0xF = 1111) dove e' presente il campo header length # http://docs.python.it/html/ref/bitwise.html iph_length = version_ihl & 0xF # 3) Moltiplico la lunghezza per 4 per avere il valore in byte iph_length = iph_length * 4 # Per estrapolare il protocollo dal pacchetto, seleziona la quinta tupla protocol = iph[6] # Per selezionare il source e destination address seleziono la settima e ottava tupla convertendola nella rappresentazione # standard in quartine-puntate tramite la funzione inet_ntoa del modulo socket. s_addr = socket.inet_ntoa(iph[8]); d_addr = socket.inet_ntoa(iph[9]); # Considero solo il protocollo TCP che viene classificato col valore intero 6 if protocol == 6 : # Per estrapolare il tcp header del pacchetto, prendo i 20 byte successivi all'ip header,quindi da 34 al 54 tcp_header = packet[34:54] # Tramite la funzione unpack del modulo struct formatto i dati del pacchetto ricevuto da binario al formato desiderato. # (Per la tabella dei formatti fare riferimento al link nel commento Moduli - 0x3 -) tcph = unpack('!HHLLBBHHH' , tcp_header) # Per estrapolare la source eport dal pacchetto, seleziono la prima tupla source_port = tcph[0] # Per estrapolare la destination port dal pacchetto, seleziono la seconda tupla dest_port = tcph[1] # Per estrapolare il sequence number dal pacchetto, seleziono la terza tupla sequence = tcph[2] # Per estrapolare l'acknowledgement number dal pacchetto, seleziono la quarta tupla acknowledgement = tcph[3] # Per estrapolare dal pacchetto il valore di lunghezza del tcp header devo: # 1) Selezionare la quinta tupla di 1 byte (8bit) dove trovero' i valori del Data offset(4 bit)+reserved(4bit) # https://it.wikipedia.org/wiki/Transmission_Control_Protocol doff_reserved = tcph[4] # 2) Tramite gli operatori bitwise, in particolare l'operatore shift (>>), # estrapolo i primi 4 bit dove e' presente il campo data offset tcph_length = doff_reserved >> 4 # Moltiplico la lunghezza per 4 per avere il valore in byte tcph_length = tcph_length * 4 # Calcolo l'inizio dei dati del pacchetto sommando i byte dell'ethernet, ip e tcp lenght h_size = eth_length + iph_length + tcph_length data = packet[h_size:] # Intercetto solo la source port e destination port pop3/imap (110/143 tcp ports) if dest_port == 110 or source_port == 110 or dest_port == 143 or source_port == 143: # Se i dati non sono vuoti li stampo a video if data != "": print 'Source IP : ' +str(s_addr) print 'Source Port : ' +str(source_port) print 'Dest IP : ' +str(d_addr) print 'Dest Port : ' +str(dest_port) print 'Sequence Number : ' + str(sequence) + ' Acknowledgement : ' + str(acknowledgement) print 'Data : ' + data except socket.error , msg: print 'Errore nella creazione del socket: %s' %msg[1] sys.exit() except KeyboardInterrupt: print "Sniffer interrotto dall'utente " sys.exit()
Test sniffer
Eseguire lo sniffer con i permessi di root:
[email protected]:~/home/claudio$ sudo python sniffer.py
Emulare una connessione pop3 tramite telnet:
[email protected]:~$ telnet 192.168.0.10 110 Trying 192.168.0.10... Connected to 192.168.0.10. Escape character is '^]'. +OK Dovecot ready. user claudio +OK pass pippo +OK Logged in. quit +OK Logging out. Connection closed by foreign host. [email protected]:~$
Controllare l’output dello sniffer:
############################# Python POP/IMAP Sniffer by Claudio Viviani ############################# Source IP : 192.168.0.10 Source Port : 110 Dest IP : 192.168.0.5 Dest Port : 34742 Sequence Number : 4138504174 Acknowledgement : 3055068377 Data : +OK Dovecot ready. Source IP : 192.168.0.5 Source Port : 34742 Dest IP : 192.168.0.10 Dest Port : 110 Sequence Number : 3055068377 Acknowledgement : 4138504194 Data : user claudio Source IP : 192.168.0.10 Source Port : 110 Dest IP : 192.168.0.5 Dest Port : 34742 Sequence Number : 4138504194 Acknowledgement : 3055068390 Data : +OK Source IP : 192.168.0.5 Source Port : 34742 Dest IP : 192.168.0.10 Dest Port : 110 Sequence Number : 3055068390 Acknowledgement : 4138504199 Data : pass pippo Source IP : 192.168.0.10 Source Port : 110 Dest IP : 192.168.0.5 Dest Port : 34742 Sequence Number : 4138504199 Acknowledgement : 3055068405 Data : +OK Logged in. Source IP : 192.168.0.5 Source Port : 34742 Dest IP : 192.168.0.10 Dest Port : 110 Sequence Number : 3055068405 Acknowledgement : 4138504215 Data : quit Source IP : 192.168.0.10 Source Port : 110 Dest IP : 192.168.0.5 Dest Port : 34742 Sequence Number : 4138504215 Acknowledgement : 3055068411 Data : +OK Logging out.
Conclusioni
Come avete notato lo script è semplice da comprendere e modificare, può essere una buona base di partenza per la creazione di uno sniffer più raffinato; Per esempio potrebbe essere utile salvare i dati intercettati all’interno di un file log:
# Se i dati non sono vuoti li stampo a video e loggo in un file if data != "": print 'Source IP : ' +str(s_addr) print 'Source Port : ' +str(source_port) print 'Dest IP : ' +str(d_addr) print 'Dest Port : ' +str(dest_port) print 'Sequence Number : ' + str(sequence) + ' Acknowledgement : ' + str(acknowledgement) print 'Data : ' + data # Se i dati non sono vuoti li loggo aggiungendo le righe al file log = open('log.txt','a') log.write('\nSource IP : ' +str(s_addr)) log.write('\nSource Port : ' +str(source_port)) log.write('\nDest IP : ' +str(d_addr)) log.write('\nDest Port : ' +str(dest_port)) log.write('\nSequence Number : ' + str(sequence) + ' Acknowledgement : ' + str(acknowledgement)) log.write('\nData : ' + data)
Per info o delucidazioni non esistate a contattarmi o a commentare il post!
Referenze
http://www.binarytides.com/python-packet-sniffer-code-linux/