Python Scapy ile Pcap Dosya Analizi
Selamlar, Bu yazıda Python’un en sevdiğim kütüphanelerinden biri olan scapy ile bir Pcap dosyasını nasıl analiz edebiliriz ve içerisinden işimize yarayacak verileri nasıl çıkarabiliriz, bu sorulara yanıt vermeye çalışacam.
Scapy Kurulumu
Scapy’nin kurulumu ile başlayabiliriz. Scapy kurulumunda sıkıntı yaşadığım çok olmuştur. O nedenle Scapy’i sorunsuz indirmek için bu komutları kullanabilirsiniz.
sudo apt-get update
sudo apt-get install python3-pip
sudo python3 -m pip install --pre scapy[complete]
Pcap Dosyasını okuma
Analiz için bir pcap dosyasını tshark ile oluşturabilirsiniz.
sudo tshark -w [dosya_Adı] -c [paket_sayısı]
Bu dosyayı okumak için Scapy’nin rdpcap fonksiyonunu kullanabiliriz. Aşağıdaki kodlar ile dosyamızı okuyarak bir değişkene atayabiliriz.
from scapy.all import *
file_path = "analysis.pcap"
packet = rdpcap(file_path)
Dosyayı okuyarak packet değişkenine atadık. Sırada analiz kısmı var.
Pcap Dosya Analizi
Dosyamızda kaç adet TCP paketinin bulunduğuna bakalım hemen.
tcp = [] # Paketleri eklemek için bir dizi oluşturdum
for packet in packets: # Her paketin analizi için bir for döngüsü oluşturdum.
if packet.haslayer('TCP'): # Eğer paketimiz TCP protkolüne ait ise
tcp.append(packet) # Bu paketi diziye ekliyorum.
print(len(tcp)) # TCP paket sayısına dizinin eleman sayısı ile ulaşıyoruz.
Yukardaki adımları izlediğimde pcap dosyasında bulunan TCP paketlerinin sayısını veriyor. Bu işlemleri UDP ve ICMP için de gerçekleştirebilirip bir tabloda gösterebiliriz.
tcp, udp, icmp = [], [], []
for packet in packets:
if packet.haslayer('TCP'):
tcp.append(packet)
if packet.haslayer('UDP'):
udp.append(packet)
if packet.haslayer('ICMP'):
icmp.append(packet)
print("TCP",len(tcp))
print("UDP",len(udp))
print("ICMP",len(icmp))
Bir önceki kod bloğunda gerçekleştirdiğim adımları bu kez TCP, UDP ve ICMP protokolleri için gerçekleştiriyorum. Çıktı aşağıdaki gibi oluyor.
Bunu biraz daha geliştirip bir tablo haline getirmek istiyorum. Bu nedenle prettytable kütüphanesini kullanıyorum.
from prettytable import PrettyTable
table = PrettyTable(['Protokol',
'Toplam Paket',
'İlk Timestamp',
'Son Timestamp']) # Her sütun için başlığı ekliyoruz.
protocols = {
'TCP': tcp,
'UDP': udp,
'ICMP': icmp
} # protokolleri bir dictionary içine ekliyorum
for protocol in protocols.items(): # ve bu protokolleri for döngüsü ile alıyorum
try:
pkt = protocol[1]
table.add_row([protocol[0],len(pkt),pkt[0].time,pkt[-1].time]) # Satır ekleme
except:
table.add_row([protocol[0],0,0,0]) # Satır ekleme
print(table)
Burada ise timestampleri yani paketlerin ilk görüldüğü ve son görüldüğü zamanları aldım.Bunu da ilk tcp paketi ile son tcp paketinin zaman değerini alarak yapıyorum. Try except yapısını kullanmamdaki amaç ise herhangi bir protokolden 0 adet dönmesi durumunda programın hata vermeden devam etmesidir. Çıktı aşağıdaki gibi olacaktır.
Gördüğünüz üzere timestampler okunabilecek bir düzeyde değil. Sıradaki işlemimiz bunu düzeltmek olsun.
import time
def convert_time(timestamp):
local_time = time.localtime(timestamp)
readable = time.strftime('%A, %d/%m/%y, %I:%M:%S %p', local_time)
return readable
Bu işlevi yerine getirmesi için convert_time isimli bir fonksiyon oluşturdum. Time kütüphanesini bu çevirme işlemi için kullandım. Bir önceki kod bloğunda kullandığımız add_row satırını aşağıdaki şekilde değiştiriseniz timestampdeki değişimi görebilirsiniz.
table.add_row([protocol[0],len(pkt),convert_time(float(pkt[0].time)), convert_time(float(pkt[-1].time))])
Çıktı:
Paketlerin sayısı ve timestampler düzenli bir şekilde gözüküyor. Bu aşamada amacımıza ulaştık. Şimdi DNS paketlerini analiz ederek ziyaret edilen web sitelerinin domainlerine ulaşmaya çalışalım.
for packet in packets:
if packet.haslayer('DNS'):
print(packet.getlayer('DNS').qd.qname.decode())
Paketleri tek tek ele alarak DNS paketleri içinden domainleri alarak yazdırdım. Çıktı aşağıdaki gibi olacaktır.
Bu çıktı da gördüğümüz gibi sadece domainler bulunuyor. Peki tam URL’e ulaşmak istersem ne yapmam gerekiyor. Bunun için HTTP paketlerini ele almam lazım. HTTP request içinden HTTP katmanına giderek host ve path bilgilerini almam gerekiyor.
from scapy.layers import http
for pkt in packets:
if pkt.haslayer("HTTPRequest"):
http_layer = pkt.getlayer("HTTPRequest")
print(http_layer)
Çıktı:
HTTP katmanını yazdırdığımda header kısmının yazdırıldığını görüyorsunuz. Şimdi bunu detaylı incelersek işimize yarayacak verilerin host ve path olduğunu görebiliriz. Sıradaki hedefim bu bilgileri çıkararak yazdırmak.
for pkt in packets:
if pkt.haslayer("HTTPRequest"):
http_layer = pkt.getlayer("HTTPRequest")
url = "{0[Host]}{0[Path]}".format(http_layer.fields)
url = url.replace("b'","")
url = url.replace("'","")
print(f'\n{url} ')
HTTP header kısımdan host ve path değerlerini aldım ve replace küçük bir temizlik işlemi yaptım. Daha sonra ekrana yazdırdığımda ise şekildeki gibi bir sonuç çıkıyor.
Çıktı:
Peki bu web sitesilerine kimlerin istek attığını merak etmiyor musunuz ? Bunu da öğrenelim.
for pkt in packets:
if pkt.haslayer("HTTPRequest"):
http_layer = pkt.getlayer("HTTPRequest")
ip_layer = pkt.getlayer('IP')
source = "{0[src]}".format(ip_layer.fields)
url = "{0[Host]}{0[Path]}".format(http_layer.fields)
url = url.replace("b'","")
url = url.replace("'","")
print(f'\n{source} istek gönderdi {url} ')
Bir önceki kod bloğuna ek olarak IP katmanını alarak kaynak IP i source değişkenine atıyorum. Bu source değişkenini yazdırıyorum. Çıktı aşağıdaki gibi olacaktır.
Ve yazının sonuna geldik. Bir yerde hatam varsa bildirirseniz sevinirim. Umarım faydalı olmuştur. Hoşçakalın.