Autarch Will Control The Internet
This commit is contained in:
283
modules/wireshark.py
Normal file
283
modules/wireshark.py
Normal file
@@ -0,0 +1,283 @@
|
||||
"""
|
||||
AUTARCH Wireshark Module
|
||||
Packet capture and analysis (scapy + optional tshark)
|
||||
|
||||
Live capture, PCAP analysis, protocol/conversation/DNS/HTTP analysis,
|
||||
credential detection.
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
# Module metadata
|
||||
DESCRIPTION = "Packet capture & analysis (scapy)"
|
||||
AUTHOR = "darkHal"
|
||||
VERSION = "1.0"
|
||||
CATEGORY = "analyze"
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).parent.parent))
|
||||
from core.banner import Colors, clear_screen, display_banner
|
||||
from core.wireshark import get_wireshark_manager
|
||||
|
||||
|
||||
class PacketAnalyzer:
|
||||
"""Packet capture and analysis tools."""
|
||||
|
||||
def __init__(self):
|
||||
self.mgr = get_wireshark_manager()
|
||||
|
||||
def print_status(self, message: str, status: str = "info"):
|
||||
colors = {"info": Colors.CYAN, "success": Colors.GREEN, "warning": Colors.YELLOW, "error": Colors.RED}
|
||||
symbols = {"info": "*", "success": "+", "warning": "!", "error": "X"}
|
||||
print(f"{colors.get(status, Colors.WHITE)}[{symbols.get(status, '*')}] {message}{Colors.RESET}")
|
||||
|
||||
def show_menu(self):
|
||||
while True:
|
||||
clear_screen()
|
||||
display_banner()
|
||||
print(f"\n{Colors.BOLD}Wireshark / Packet Analysis{Colors.RESET}")
|
||||
|
||||
# Status
|
||||
status = self.mgr.get_status()
|
||||
engine = []
|
||||
if status['scapy']:
|
||||
engine.append(f'{Colors.GREEN}scapy{Colors.RESET}')
|
||||
else:
|
||||
engine.append(f'{Colors.RED}scapy (missing){Colors.RESET}')
|
||||
if status['tshark']:
|
||||
engine.append(f'{Colors.GREEN}tshark{Colors.RESET}')
|
||||
else:
|
||||
engine.append(f'{Colors.YELLOW}tshark (not found){Colors.RESET}')
|
||||
print(f" Engine: {' + '.join(engine)}")
|
||||
if status['can_capture']:
|
||||
print(f" Live capture: {Colors.GREEN}available{Colors.RESET}")
|
||||
else:
|
||||
print(f" Live capture: {Colors.YELLOW}needs root{Colors.RESET}")
|
||||
|
||||
print(f"\n {Colors.CYAN}[1]{Colors.RESET} List Interfaces")
|
||||
print(f" {Colors.CYAN}[2]{Colors.RESET} Start Live Capture")
|
||||
print(f" {Colors.CYAN}[3]{Colors.RESET} Open PCAP File")
|
||||
print(f" {Colors.CYAN}[4]{Colors.RESET} Protocol Analysis")
|
||||
print(f" {Colors.CYAN}[5]{Colors.RESET} Conversation Analysis")
|
||||
print(f" {Colors.CYAN}[6]{Colors.RESET} DNS Query Analysis")
|
||||
print(f" {Colors.CYAN}[7]{Colors.RESET} HTTP Traffic Analysis")
|
||||
print(f" {Colors.CYAN}[8]{Colors.RESET} Credential Detection")
|
||||
print(f" {Colors.CYAN}[9]{Colors.RESET} Export Results")
|
||||
print(f" {Colors.CYAN}[0]{Colors.RESET} Back")
|
||||
|
||||
choice = input(f"\n{Colors.WHITE}Select option: {Colors.RESET}").strip()
|
||||
|
||||
if choice == '0':
|
||||
break
|
||||
elif choice == '1':
|
||||
self.list_interfaces()
|
||||
elif choice == '2':
|
||||
self.start_capture()
|
||||
elif choice == '3':
|
||||
self.open_pcap()
|
||||
elif choice == '4':
|
||||
self.protocol_analysis()
|
||||
elif choice == '5':
|
||||
self.conversation_analysis()
|
||||
elif choice == '6':
|
||||
self.dns_analysis()
|
||||
elif choice == '7':
|
||||
self.http_analysis()
|
||||
elif choice == '8':
|
||||
self.credential_detection()
|
||||
elif choice == '9':
|
||||
self.export_results()
|
||||
|
||||
def list_interfaces(self):
|
||||
"""List network interfaces."""
|
||||
print(f"\n{Colors.BOLD}Network Interfaces{Colors.RESET}")
|
||||
interfaces = self.mgr.list_interfaces()
|
||||
if not interfaces:
|
||||
self.print_status("No interfaces found", "error")
|
||||
else:
|
||||
for i, iface in enumerate(interfaces, 1):
|
||||
desc = f" ({iface['description']})" if iface.get('description') else ''
|
||||
print(f" {Colors.CYAN}{i}.{Colors.RESET} {iface['name']}{desc}")
|
||||
input(f"\n{Colors.WHITE}Press Enter to continue...{Colors.RESET}")
|
||||
|
||||
def start_capture(self):
|
||||
"""Start a live packet capture."""
|
||||
print(f"\n{Colors.BOLD}Live Capture{Colors.RESET}")
|
||||
|
||||
if not self.mgr.can_capture:
|
||||
self.print_status("Root privileges required for live capture", "error")
|
||||
input(f"\n{Colors.WHITE}Press Enter to continue...{Colors.RESET}")
|
||||
return
|
||||
|
||||
# Show interfaces
|
||||
interfaces = self.mgr.list_interfaces()
|
||||
for i, iface in enumerate(interfaces, 1):
|
||||
print(f" {i}. {iface['name']}")
|
||||
|
||||
iface_input = input(f"\n{Colors.WHITE}Interface (name or number, Enter for default): {Colors.RESET}").strip()
|
||||
interface = None
|
||||
if iface_input:
|
||||
try:
|
||||
idx = int(iface_input) - 1
|
||||
if 0 <= idx < len(interfaces):
|
||||
interface = interfaces[idx]['name']
|
||||
except ValueError:
|
||||
interface = iface_input
|
||||
|
||||
bpf = input(f"{Colors.WHITE}BPF filter (e.g., 'tcp port 80', Enter for all): {Colors.RESET}").strip() or None
|
||||
duration_str = input(f"{Colors.WHITE}Duration in seconds (default 30): {Colors.RESET}").strip()
|
||||
duration = int(duration_str) if duration_str.isdigit() else 30
|
||||
|
||||
self.print_status(f"Starting capture on {interface or 'default'} for {duration}s...", "info")
|
||||
|
||||
result = self.mgr.start_capture(interface=interface, bpf_filter=bpf, duration=duration)
|
||||
if 'error' in result:
|
||||
self.print_status(result['error'], "error")
|
||||
input(f"\n{Colors.WHITE}Press Enter to continue...{Colors.RESET}")
|
||||
return
|
||||
|
||||
self.print_status(f"Capturing... Output: {result.get('file', '')}", "info")
|
||||
|
||||
# Wait for capture to complete
|
||||
import time
|
||||
try:
|
||||
while self.mgr._capture_running:
|
||||
stats = self.mgr.get_capture_stats()
|
||||
print(f"\r Packets: {stats.get('packet_count', 0)}", end='', flush=True)
|
||||
time.sleep(1)
|
||||
except KeyboardInterrupt:
|
||||
self.mgr.stop_capture()
|
||||
|
||||
stats = self.mgr.get_capture_stats()
|
||||
print()
|
||||
self.print_status(f"Capture complete: {stats.get('packet_count', 0)} packets", "success")
|
||||
if stats.get('output_file'):
|
||||
self.print_status(f"Saved to: {stats['output_file']}", "info")
|
||||
|
||||
input(f"\n{Colors.WHITE}Press Enter to continue...{Colors.RESET}")
|
||||
|
||||
def open_pcap(self):
|
||||
"""Open and load a PCAP file."""
|
||||
print(f"\n{Colors.BOLD}Open PCAP File{Colors.RESET}")
|
||||
filepath = input(f"{Colors.WHITE}PCAP file path: {Colors.RESET}").strip()
|
||||
if not filepath:
|
||||
return
|
||||
|
||||
self.print_status(f"Loading {filepath}...", "info")
|
||||
result = self.mgr.read_pcap(filepath)
|
||||
|
||||
if 'error' in result:
|
||||
self.print_status(result['error'], "error")
|
||||
else:
|
||||
self.print_status(f"Loaded {result['total_packets']} packets from {result['file']}", "success")
|
||||
# Show first few packets
|
||||
for pkt in result['packets'][:20]:
|
||||
print(f" {pkt.get('src','?'):>15} -> {pkt.get('dst','?'):<15} {pkt.get('protocol',''):>8} {pkt.get('info','')}")
|
||||
if result['total_packets'] > 20:
|
||||
print(f" ... and {result['total_packets'] - 20} more packets")
|
||||
|
||||
input(f"\n{Colors.WHITE}Press Enter to continue...{Colors.RESET}")
|
||||
|
||||
def protocol_analysis(self):
|
||||
"""Show protocol distribution."""
|
||||
print(f"\n{Colors.BOLD}Protocol Analysis{Colors.RESET}")
|
||||
result = self.mgr.get_protocol_hierarchy()
|
||||
|
||||
if result['total'] == 0:
|
||||
self.print_status("No packets loaded. Open a PCAP or run a capture first.", "warning")
|
||||
else:
|
||||
print(f" Total packets: {result['total']}\n")
|
||||
for proto, data in result['protocols'].items():
|
||||
bar_len = int(data['percent'] / 2)
|
||||
bar = '█' * bar_len
|
||||
print(f" {proto:<12} {data['count']:>6} {data['percent']:>5.1f}% {Colors.CYAN}{bar}{Colors.RESET}")
|
||||
|
||||
input(f"\n{Colors.WHITE}Press Enter to continue...{Colors.RESET}")
|
||||
|
||||
def conversation_analysis(self):
|
||||
"""Show IP conversations."""
|
||||
print(f"\n{Colors.BOLD}Conversation Analysis{Colors.RESET}")
|
||||
convos = self.mgr.extract_conversations()
|
||||
|
||||
if not convos:
|
||||
self.print_status("No packets loaded.", "warning")
|
||||
else:
|
||||
print(f" {'Source':<20} {'Destination':<20} {'Packets':>8} {'Bytes':>10} {'Protocols'}")
|
||||
print(f" {'─'*20} {'─'*20} {'─'*8} {'─'*10} {'─'*20}")
|
||||
for c in convos[:30]:
|
||||
protos = ', '.join(c['protocols'][:3])
|
||||
print(f" {c['src']:<20} {c['dst']:<20} {c['packets']:>8} {c['bytes']:>10} {protos}")
|
||||
|
||||
input(f"\n{Colors.WHITE}Press Enter to continue...{Colors.RESET}")
|
||||
|
||||
def dns_analysis(self):
|
||||
"""Show DNS queries."""
|
||||
print(f"\n{Colors.BOLD}DNS Query Analysis{Colors.RESET}")
|
||||
queries = self.mgr.extract_dns_queries()
|
||||
|
||||
if not queries:
|
||||
self.print_status("No DNS queries found.", "warning")
|
||||
else:
|
||||
print(f" {'Query':<40} {'Type':<6} {'Count':>6} {'Response'}")
|
||||
print(f" {'─'*40} {'─'*6} {'─'*6} {'─'*30}")
|
||||
for q in queries[:40]:
|
||||
resp = q.get('response', '')[:30]
|
||||
print(f" {q['query']:<40} {q['type']:<6} {q['count']:>6} {resp}")
|
||||
|
||||
input(f"\n{Colors.WHITE}Press Enter to continue...{Colors.RESET}")
|
||||
|
||||
def http_analysis(self):
|
||||
"""Show HTTP requests."""
|
||||
print(f"\n{Colors.BOLD}HTTP Traffic Analysis{Colors.RESET}")
|
||||
requests = self.mgr.extract_http_requests()
|
||||
|
||||
if not requests:
|
||||
self.print_status("No HTTP requests found.", "warning")
|
||||
else:
|
||||
for r in requests[:30]:
|
||||
method = r.get('method', '?')
|
||||
host = r.get('host', '')
|
||||
path = r.get('path', '')[:60]
|
||||
src = r.get('src', '')
|
||||
color = Colors.GREEN if method == 'GET' else Colors.YELLOW
|
||||
print(f" {color}{method:<7}{Colors.RESET} {host}{path} from {src}")
|
||||
|
||||
input(f"\n{Colors.WHITE}Press Enter to continue...{Colors.RESET}")
|
||||
|
||||
def credential_detection(self):
|
||||
"""Detect plaintext credentials."""
|
||||
print(f"\n{Colors.BOLD}Credential Detection{Colors.RESET}")
|
||||
creds = self.mgr.extract_credentials()
|
||||
|
||||
if not creds:
|
||||
self.print_status("No plaintext credentials detected.", "info")
|
||||
else:
|
||||
self.print_status(f"Found {len(creds)} credential artifacts!", "warning")
|
||||
for c in creds:
|
||||
print(f" {Colors.RED}[{c['protocol']}]{Colors.RESET} {c['type']}: {c['value']} ({c['src']} -> {c['dst']})")
|
||||
|
||||
input(f"\n{Colors.WHITE}Press Enter to continue...{Colors.RESET}")
|
||||
|
||||
def export_results(self):
|
||||
"""Export packets."""
|
||||
print(f"\n{Colors.BOLD}Export Results{Colors.RESET}")
|
||||
print(f" {Colors.CYAN}[1]{Colors.RESET} Export as JSON")
|
||||
print(f" {Colors.CYAN}[2]{Colors.RESET} Export as CSV")
|
||||
|
||||
choice = input(f"\n{Colors.WHITE}Select format: {Colors.RESET}").strip()
|
||||
fmt = 'csv' if choice == '2' else 'json'
|
||||
|
||||
result = self.mgr.export_packets(fmt=fmt)
|
||||
if 'error' in result:
|
||||
self.print_status(result['error'], "error")
|
||||
else:
|
||||
self.print_status(f"Exported {result['count']} packets to {result['filepath']}", "success")
|
||||
|
||||
input(f"\n{Colors.WHITE}Press Enter to continue...{Colors.RESET}")
|
||||
|
||||
|
||||
def run():
|
||||
"""Module entry point."""
|
||||
analyzer = PacketAnalyzer()
|
||||
analyzer.show_menu()
|
||||
Reference in New Issue
Block a user