132 lines
4.3 KiB
Python
132 lines
4.3 KiB
Python
from scapy.all import rdpcap, Raw, DNS, DNSQR
|
|
try:
|
|
from scapy.layers.tls.all import TLS
|
|
except ImportError:
|
|
# Fallback or mock if TLS not available, though we use a try-except block in usage
|
|
TLS = None
|
|
import re
|
|
import os
|
|
from IoTAnalyzer.models import UrlRaw, HashRaw
|
|
|
|
|
|
def analyze_pcap_file(pcap_file, raw_dataset_id):
|
|
|
|
"""
|
|
Analyzes a PCAP file and stores found hashes and URLs in the database.
|
|
"""
|
|
print("Start analyzing...")
|
|
|
|
# Adjust the path to the file
|
|
# Check multiple locations for the file
|
|
possible_paths = [
|
|
os.path.join('/app/files/uploads', pcap_file), # Mounted frontend uploads
|
|
os.path.join('/app/files', pcap_file), # Shared files
|
|
os.path.join('Frontend', 'iot-pre-tester-frontend', 'public', 'filesAttachment', pcap_file), # Local dev fallback
|
|
]
|
|
|
|
filepath = None
|
|
for path in possible_paths:
|
|
if os.path.exists(path):
|
|
filepath = path
|
|
break
|
|
|
|
if not filepath:
|
|
print(f"Fehler: Datei {pcap_file} nicht gefunden in: {possible_paths}")
|
|
return
|
|
|
|
# Regular expressions for hashes and URLs
|
|
hash_pattern = re.compile(r"\b[a-fA-F0-9]{32,64}\b") # MD5, SHA1, SHA256
|
|
|
|
# Stricter URL pattern:
|
|
# 1. Requires http://, https://, or www. (Standard)
|
|
# 2. OR Naked domain with AT LEAST 2 dots (e.g. sub.domain.com) to catch cloud URLs but avoid "J.mINT"
|
|
url_pattern = re.compile(
|
|
r"\b(?:"
|
|
r"(?:https?://|www\.)(?:[a-zA-Z0-9.-]+\.[a-zA-Z]{2,6})" # Protocol/www required
|
|
r"|"
|
|
r"(?:[a-zA-Z0-9-]+\.){2,}[a-zA-Z]{2,6}" # Naked domain with >= 2 dots
|
|
r"|"
|
|
r"\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}" # IPv4
|
|
r")"
|
|
r"(?:/[a-zA-Z0-9._%/-]*)?\b"
|
|
)
|
|
|
|
# Load PCAP file
|
|
try:
|
|
packets = rdpcap(filepath)
|
|
except Exception as e:
|
|
print(f"Fehler beim Öffnen der PCAP-Datei: {e}")
|
|
return
|
|
|
|
found_hashes = set()
|
|
found_urls = set()
|
|
|
|
# Analyze packets
|
|
for pkt in packets:
|
|
# 1. DNS Queries (Reliable source for domains)
|
|
if pkt.haslayer(DNS) and pkt[DNS].qd:
|
|
try:
|
|
qname = pkt[DNS].qd.qname.decode('utf-8', errors='ignore')
|
|
if qname.endswith('.'):
|
|
qname = qname[:-1]
|
|
if qname:
|
|
found_urls.add(qname)
|
|
except Exception:
|
|
pass
|
|
|
|
# 2. TLS Server Name Indication (SNI)
|
|
# Attempt to extract SNI if TLS layer is present
|
|
if TLS and pkt.haslayer(TLS):
|
|
try:
|
|
# Iterate through TLS messages to find ClientHello and extensions
|
|
if hasattr(pkt[TLS], 'msg'):
|
|
for msg in pkt[TLS].msg:
|
|
if hasattr(msg, 'ext'):
|
|
for ext in msg.ext:
|
|
if hasattr(ext, 'servernames'):
|
|
for servername in ext.servernames:
|
|
if hasattr(servername, 'servername'):
|
|
sni = servername.servername.decode('utf-8', errors='ignore')
|
|
if sni:
|
|
found_urls.add(sni)
|
|
except Exception:
|
|
pass
|
|
|
|
# 3. Raw Payload (Fallback with strict regex)
|
|
if pkt.haslayer(Raw): # Check whether the packet has a payload
|
|
payload = pkt[Raw].load.decode(errors='ignore')
|
|
|
|
# Search for hashes
|
|
found_hashes.update(hash_pattern.findall(payload))
|
|
|
|
# Search for URLs
|
|
found_urls.update(url_pattern.findall(payload))
|
|
|
|
# Store results in the database
|
|
save_urls_to_database(found_urls, raw_dataset_id)
|
|
save_hashes_to_database(found_hashes, raw_dataset_id)
|
|
|
|
print("Done analyzing.")
|
|
|
|
|
|
def save_urls_to_database(urls, raw_dataset_id):
|
|
"""
|
|
Stores extracted URLs in the database.
|
|
"""
|
|
for url in urls:
|
|
UrlRaw.objects.create(
|
|
raw_dataset_id=raw_dataset_id,
|
|
url=url
|
|
)
|
|
|
|
|
|
def save_hashes_to_database(hashes, raw_dataset_id):
|
|
"""
|
|
Stores extracted hashes in the database.
|
|
"""
|
|
for hash_value in hashes:
|
|
HashRaw.objects.create(
|
|
raw_dataset_id=raw_dataset_id,
|
|
hash_value=hash_value
|
|
)
|