mirror of
https://github.com/infinition/Bjorn.git
synced 2026-03-16 17:12:00 +00:00
Add RLUtils class for managing RL/AI dashboard endpoints
- Implemented methods for fetching AI stats, training history, and recent experiences. - Added functionality to set operation mode (MANUAL, AUTO, AI) with appropriate handling. - Included helper methods for querying the database and sending JSON responses. - Integrated model metadata extraction for visualization purposes.
This commit is contained in:
@@ -1,110 +1,85 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
odin_eye.py -- Network traffic analyzer and credential hunter for BJORN.
|
||||
Uses pyshark to capture and analyze packets in real-time.
|
||||
"""
|
||||
|
||||
import os
|
||||
import json
|
||||
try:
|
||||
import psutil
|
||||
except Exception:
|
||||
psutil = None
|
||||
import pyshark
|
||||
HAS_PYSHARK = True
|
||||
except ImportError:
|
||||
pyshark = None
|
||||
HAS_PYSHARK = False
|
||||
|
||||
import re
|
||||
import threading
|
||||
import time
|
||||
import logging
|
||||
from datetime import datetime
|
||||
|
||||
def _list_net_ifaces() -> list[str]:
|
||||
names = set()
|
||||
# 1) psutil si dispo
|
||||
if psutil:
|
||||
try:
|
||||
names.update(ifname for ifname in psutil.net_if_addrs().keys() if ifname != "lo")
|
||||
except Exception:
|
||||
pass
|
||||
# 2) fallback kernel
|
||||
try:
|
||||
for n in os.listdir("/sys/class/net"):
|
||||
if n and n != "lo":
|
||||
names.add(n)
|
||||
except Exception:
|
||||
pass
|
||||
out = ["auto"] + sorted(names)
|
||||
# sécurité: pas de doublons
|
||||
seen, unique = set(), []
|
||||
for x in out:
|
||||
if x not in seen:
|
||||
unique.append(x); seen.add(x)
|
||||
return unique
|
||||
from collections import defaultdict
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
from logger import Logger
|
||||
|
||||
# Hook appelée par le backend avant affichage UI / sync DB
|
||||
def compute_dynamic_b_args(base: dict) -> dict:
|
||||
"""
|
||||
Compute dynamic arguments at runtime.
|
||||
Called by the web interface to populate dropdowns, etc.
|
||||
"""
|
||||
d = dict(base or {})
|
||||
|
||||
# Example: Dynamic interface list
|
||||
if "interface" in d:
|
||||
import psutil
|
||||
interfaces = ["auto"]
|
||||
try:
|
||||
for ifname in psutil.net_if_addrs().keys():
|
||||
if ifname != "lo":
|
||||
interfaces.append(ifname)
|
||||
except:
|
||||
interfaces.extend(["wlan0", "eth0"])
|
||||
|
||||
d["interface"]["choices"] = interfaces
|
||||
|
||||
return d
|
||||
logger = Logger(name="odin_eye.py")
|
||||
|
||||
# --- MÉTADONNÉES UI SUPPLÉMENTAIRES -----------------------------------------
|
||||
# Exemples d’arguments (affichage frontend; aussi persisté en DB via sync_actions)
|
||||
b_examples = [
|
||||
{"interface": "auto", "filter": "http or ftp", "timeout": 120, "max_packets": 5000, "save_credentials": True},
|
||||
{"interface": "wlan0", "filter": "(http or smtp) and not broadcast", "timeout": 300, "max_packets": 10000},
|
||||
]
|
||||
|
||||
# Lien MD (peut être un chemin local servi par votre frontend, ou un http(s))
|
||||
# Exemple: un README markdown stocké dans votre repo
|
||||
b_docs_url = "docs/actions/OdinEye.md"
|
||||
|
||||
|
||||
# --- Métadonnées d'action (consommées par shared.generate_actions_json) -----
|
||||
# -------------------- Action metadata --------------------
|
||||
b_class = "OdinEye"
|
||||
b_module = "odin_eye" # nom du fichier sans .py
|
||||
b_enabled = 0
|
||||
b_module = "odin_eye"
|
||||
b_status = "odin_eye"
|
||||
b_port = None
|
||||
b_service = "[]"
|
||||
b_trigger = "on_start"
|
||||
b_parent = None
|
||||
b_action = "normal"
|
||||
b_priority = 30
|
||||
b_cooldown = 0
|
||||
b_rate_limit = None
|
||||
b_timeout = 600
|
||||
b_max_retries = 1
|
||||
b_stealth_level = 4 # Capturing is passive, but pyshark can be resource intensive
|
||||
b_risk_level = "low"
|
||||
b_enabled = 1
|
||||
b_tags = ["sniff", "pcap", "creds", "network"]
|
||||
b_category = "recon"
|
||||
b_name = "Odin Eye"
|
||||
b_description = (
|
||||
"Network traffic analyzer for capturing and analyzing data patterns and credentials.\n"
|
||||
"Requires: tshark (sudo apt install tshark) + pyshark (pip install pyshark)."
|
||||
)
|
||||
b_author = "Fabien / Cyberviking"
|
||||
b_version = "1.0.0"
|
||||
b_description = "Passive network analyzer that hunts for credentials and data patterns."
|
||||
b_author = "Bjorn Team"
|
||||
b_version = "2.0.1"
|
||||
b_icon = "OdinEye.png"
|
||||
|
||||
# Schéma d'arguments pour UI dynamique (clé == nom du flag sans '--')
|
||||
b_args = {
|
||||
"interface": {
|
||||
"type": "select", "label": "Network Interface",
|
||||
"choices": [], # <- Laisser vide: rempli dynamiquement par compute_dynamic_b_args(...)
|
||||
"type": "select",
|
||||
"label": "Network Interface",
|
||||
"choices": ["auto", "wlan0", "eth0"],
|
||||
"default": "auto",
|
||||
"help": "Interface à écouter. 'auto' tente de détecter l'interface par défaut." },
|
||||
"filter": {"type": "text", "label": "BPF Filter", "default": "(http or ftp or smtp or pop3 or imap or telnet) and not broadcast"},
|
||||
"output": {"type": "text", "label": "Output dir", "default": "/home/bjorn/Bjorn/data/output/packets"},
|
||||
"timeout": {"type": "number", "label": "Timeout (s)", "min": 10, "max": 36000, "step": 1, "default": 300},
|
||||
"max_packets": {"type": "number", "label": "Max packets", "min": 100, "max": 2000000, "step": 100, "default": 10000},
|
||||
"help": "Interface to listen on."
|
||||
},
|
||||
"filter": {
|
||||
"type": "text",
|
||||
"label": "BPF Filter",
|
||||
"default": "(http or ftp or smtp or pop3 or imap or telnet) and not broadcast"
|
||||
},
|
||||
"max_packets": {
|
||||
"type": "number",
|
||||
"label": "Max packets",
|
||||
"min": 100,
|
||||
"max": 100000,
|
||||
"step": 100,
|
||||
"default": 1000
|
||||
},
|
||||
"save_creds": {
|
||||
"type": "checkbox",
|
||||
"label": "Save Credentials",
|
||||
"default": True
|
||||
}
|
||||
}
|
||||
|
||||
# ----------------- Code d'analyse (ton code existant) -----------------------
|
||||
import os, json, pyshark, argparse, logging, re, threading, signal
|
||||
from datetime import datetime
|
||||
from collections import defaultdict
|
||||
|
||||
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
|
||||
|
||||
DEFAULT_OUTPUT_DIR = "/home/bjorn/Bjorn/data/output/packets"
|
||||
DEFAULT_SETTINGS_DIR = "/home/bjorn/.settings_bjorn"
|
||||
SETTINGS_FILE = os.path.join(DEFAULT_SETTINGS_DIR, "odin_eye_settings.json")
|
||||
DEFAULT_FILTER = "(http or ftp or smtp or pop3 or imap or telnet) and not broadcast"
|
||||
|
||||
CREDENTIAL_PATTERNS = {
|
||||
'http': {
|
||||
'username': [r'username=([^&]+)', r'user=([^&]+)', r'login=([^&]+)'],
|
||||
@@ -120,297 +95,153 @@ CREDENTIAL_PATTERNS = {
|
||||
}
|
||||
|
||||
class OdinEye:
|
||||
def __init__(self, interface, capture_filter=DEFAULT_FILTER, output_dir=DEFAULT_OUTPUT_DIR,
|
||||
timeout=300, max_packets=10000):
|
||||
self.interface = interface
|
||||
self.capture_filter = capture_filter
|
||||
self.output_dir = output_dir
|
||||
self.timeout = timeout
|
||||
self.max_packets = max_packets
|
||||
def __init__(self, shared_data):
|
||||
self.shared_data = shared_data
|
||||
self.capture = None
|
||||
self.stop_capture = threading.Event()
|
||||
|
||||
self.stop_event = threading.Event()
|
||||
self.statistics = defaultdict(int)
|
||||
self.credentials = []
|
||||
self.interesting_patterns = []
|
||||
|
||||
self.credentials: List[Dict[str, Any]] = []
|
||||
self.lock = threading.Lock()
|
||||
|
||||
def process_packet(self, packet):
|
||||
"""Analyze a single packet for patterns and credentials."""
|
||||
try:
|
||||
with self.lock:
|
||||
self.statistics['total_packets'] += 1
|
||||
if hasattr(packet, 'highest_layer'):
|
||||
self.statistics[packet.highest_layer] += 1
|
||||
|
||||
if hasattr(packet, 'tcp'):
|
||||
self.analyze_tcp_packet(packet)
|
||||
except Exception as e:
|
||||
logging.error(f"Error processing packet: {e}")
|
||||
# HTTP
|
||||
if hasattr(packet, 'http'):
|
||||
self._analyze_http(packet)
|
||||
# FTP
|
||||
elif hasattr(packet, 'ftp'):
|
||||
self._analyze_ftp(packet)
|
||||
# SMTP
|
||||
elif hasattr(packet, 'smtp'):
|
||||
self._analyze_smtp(packet)
|
||||
|
||||
# Payload generic check
|
||||
if hasattr(packet.tcp, 'payload'):
|
||||
self._analyze_payload(packet.tcp.payload)
|
||||
|
||||
def analyze_tcp_packet(self, packet):
|
||||
try:
|
||||
if hasattr(packet, 'http'):
|
||||
self.analyze_http_packet(packet)
|
||||
elif hasattr(packet, 'ftp'):
|
||||
self.analyze_ftp_packet(packet)
|
||||
elif hasattr(packet, 'smtp'):
|
||||
self.analyze_smtp_packet(packet)
|
||||
if hasattr(packet.tcp, 'payload'):
|
||||
self.analyze_payload(packet.tcp.payload)
|
||||
except Exception as e:
|
||||
logging.error(f"Error analyzing TCP packet: {e}")
|
||||
logger.debug(f"Packet processing error: {e}")
|
||||
|
||||
def analyze_http_packet(self, packet):
|
||||
try:
|
||||
if hasattr(packet.http, 'request_uri'):
|
||||
for field in ['username', 'password']:
|
||||
for pattern in CREDENTIAL_PATTERNS['http'][field]:
|
||||
matches = re.findall(pattern, packet.http.request_uri)
|
||||
if matches:
|
||||
with self.lock:
|
||||
self.credentials.append({
|
||||
'protocol': 'HTTP',
|
||||
'type': field,
|
||||
'value': matches[0],
|
||||
'timestamp': datetime.now().isoformat(),
|
||||
'source': packet.ip.src if hasattr(packet, 'ip') else None
|
||||
})
|
||||
except Exception as e:
|
||||
logging.error(f"Error analyzing HTTP packet: {e}")
|
||||
def _analyze_http(self, packet):
|
||||
if hasattr(packet.http, 'request_uri'):
|
||||
uri = packet.http.request_uri
|
||||
for field in ['username', 'password']:
|
||||
for pattern in CREDENTIAL_PATTERNS['http'][field]:
|
||||
m = re.findall(pattern, uri, re.I)
|
||||
if m:
|
||||
self._add_cred('HTTP', field, m[0], getattr(packet.ip, 'src', 'unknown'))
|
||||
|
||||
def analyze_ftp_packet(self, packet):
|
||||
try:
|
||||
if hasattr(packet.ftp, 'request_command'):
|
||||
cmd = packet.ftp.request_command.upper()
|
||||
if cmd in ['USER', 'PASS']:
|
||||
with self.lock:
|
||||
self.credentials.append({
|
||||
'protocol': 'FTP',
|
||||
'type': 'username' if cmd == 'USER' else 'password',
|
||||
'value': packet.ftp.request_arg,
|
||||
'timestamp': datetime.now().isoformat(),
|
||||
'source': packet.ip.src if hasattr(packet, 'ip') else None
|
||||
})
|
||||
except Exception as e:
|
||||
logging.error(f"Error analyzing FTP packet: {e}")
|
||||
def _analyze_ftp(self, packet):
|
||||
if hasattr(packet.ftp, 'request_command'):
|
||||
cmd = packet.ftp.request_command.upper()
|
||||
if cmd in ['USER', 'PASS']:
|
||||
field = 'username' if cmd == 'USER' else 'password'
|
||||
self._add_cred('FTP', field, packet.ftp.request_arg, getattr(packet.ip, 'src', 'unknown'))
|
||||
|
||||
def analyze_smtp_packet(self, packet):
|
||||
try:
|
||||
if hasattr(packet.smtp, 'command_line'):
|
||||
for pattern in CREDENTIAL_PATTERNS['smtp']['auth']:
|
||||
matches = re.findall(pattern, packet.smtp.command_line)
|
||||
if matches:
|
||||
with self.lock:
|
||||
self.credentials.append({
|
||||
'protocol': 'SMTP',
|
||||
'type': 'auth',
|
||||
'value': matches[0],
|
||||
'timestamp': datetime.now().isoformat(),
|
||||
'source': packet.ip.src if hasattr(packet, 'ip') else None
|
||||
})
|
||||
except Exception as e:
|
||||
logging.error(f"Error analyzing SMTP packet: {e}")
|
||||
def _analyze_smtp(self, packet):
|
||||
if hasattr(packet.smtp, 'command_line'):
|
||||
line = packet.smtp.command_line
|
||||
for pattern in CREDENTIAL_PATTERNS['smtp']['auth']:
|
||||
m = re.findall(pattern, line, re.I)
|
||||
if m:
|
||||
self._add_cred('SMTP', 'auth', m[0], getattr(packet.ip, 'src', 'unknown'))
|
||||
|
||||
def analyze_payload(self, payload):
|
||||
def _analyze_payload(self, payload):
|
||||
patterns = {
|
||||
'email': r'[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}',
|
||||
'credit_card': r'\b\d{4}[- ]?\d{4}[- ]?\d{4}[- ]?\d{4}\b',
|
||||
'ip_address': r'\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b'
|
||||
'credit_card': r'\b\d{4}[- ]?\d{4}[- ]?\d{4}[- ]?\d{4}\b'
|
||||
}
|
||||
for name, pattern in patterns.items():
|
||||
matches = re.findall(pattern, payload)
|
||||
if matches:
|
||||
with self.lock:
|
||||
self.interesting_patterns.append({
|
||||
'type': name,
|
||||
'value': matches[0],
|
||||
'timestamp': datetime.now().isoformat()
|
||||
})
|
||||
m = re.findall(pattern, payload)
|
||||
if m:
|
||||
self.shared_data.log_milestone(b_class, "PatternFound", f"{name} detected in traffic")
|
||||
|
||||
def _add_cred(self, proto, field, value, source):
|
||||
with self.lock:
|
||||
cred = {
|
||||
'protocol': proto,
|
||||
'type': field,
|
||||
'value': value,
|
||||
'timestamp': datetime.now().isoformat(),
|
||||
'source': source
|
||||
}
|
||||
if cred not in self.credentials:
|
||||
self.credentials.append(cred)
|
||||
logger.success(f"OdinEye: Credential found! [{proto}] {field}={value}")
|
||||
self.shared_data.log_milestone(b_class, "Credential", f"{proto} {field} captured")
|
||||
|
||||
def execute(self, ip, port, row, status_key) -> str:
|
||||
"""Standard entry point."""
|
||||
iface = getattr(self.shared_data, "odin_eye_interface", "auto")
|
||||
if iface == "auto":
|
||||
iface = None # pyshark handles None as default
|
||||
|
||||
bpf_filter = getattr(self.shared_data, "odin_eye_filter", b_args["filter"]["default"])
|
||||
max_pkts = int(getattr(self.shared_data, "odin_eye_max_packets", 1000))
|
||||
timeout = int(getattr(self.shared_data, "odin_eye_timeout", 300))
|
||||
output_dir = getattr(self.shared_data, "odin_eye_output", "/home/bjorn/Bjorn/data/output/packets")
|
||||
|
||||
logger.info(f"OdinEye: Starting capture on {iface or 'default'} (filter: {bpf_filter})")
|
||||
self.shared_data.log_milestone(b_class, "Startup", f"Sniffing on {iface or 'any'}")
|
||||
|
||||
def save_results(self):
|
||||
try:
|
||||
os.makedirs(self.output_dir, exist_ok=True)
|
||||
timestamp = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
|
||||
stats_file = os.path.join(self.output_dir, f"capture_stats_{timestamp}.json")
|
||||
with open(stats_file, 'w') as f:
|
||||
json.dump(dict(self.statistics), f, indent=4)
|
||||
if self.credentials:
|
||||
creds_file = os.path.join(self.output_dir, f"credentials_{timestamp}.json")
|
||||
with open(creds_file, 'w') as f:
|
||||
json.dump(self.credentials, f, indent=4)
|
||||
if self.interesting_patterns:
|
||||
patterns_file = os.path.join(self.output_dir, f"patterns_{timestamp}.json")
|
||||
with open(patterns_file, 'w') as f:
|
||||
json.dump(self.interesting_patterns, f, indent=4)
|
||||
logging.info(f"Results saved to {self.output_dir}")
|
||||
except Exception as e:
|
||||
logging.error(f"Failed to save results: {e}")
|
||||
|
||||
def execute(self):
|
||||
try:
|
||||
# Timeout thread (inchangé) ...
|
||||
if self.timeout and self.timeout > 0:
|
||||
def _stop_after():
|
||||
self.stop_capture.wait(self.timeout)
|
||||
self.stop_capture.set()
|
||||
threading.Thread(target=_stop_after, daemon=True).start()
|
||||
|
||||
logging.info(...)
|
||||
|
||||
self.capture = pyshark.LiveCapture(interface=self.interface, bpf_filter=self.capture_filter)
|
||||
|
||||
# Interruption douce — SKIP si on tourne en mode importlib (thread)
|
||||
if os.environ.get("BJORN_EMBEDDED") != "1":
|
||||
try:
|
||||
signal.signal(signal.SIGINT, self.handle_interrupt)
|
||||
signal.signal(signal.SIGTERM, self.handle_interrupt)
|
||||
except Exception:
|
||||
# Ex: ValueError si pas dans le main thread
|
||||
pass
|
||||
|
||||
self.capture = pyshark.LiveCapture(interface=iface, bpf_filter=bpf_filter)
|
||||
|
||||
start_time = time.time()
|
||||
packet_count = 0
|
||||
|
||||
# Use sniff_continuously for real-time processing
|
||||
for packet in self.capture.sniff_continuously():
|
||||
if self.stop_capture.is_set() or self.statistics['total_packets'] >= self.max_packets:
|
||||
if self.shared_data.orchestrator_should_exit:
|
||||
break
|
||||
|
||||
if time.time() - start_time > timeout:
|
||||
logger.info("OdinEye: Timeout reached.")
|
||||
break
|
||||
|
||||
packet_count += 1
|
||||
if packet_count >= max_pkts:
|
||||
logger.info("OdinEye: Max packets reached.")
|
||||
break
|
||||
|
||||
self.process_packet(packet)
|
||||
|
||||
# Periodic progress update (every 50 packets)
|
||||
if packet_count % 50 == 0:
|
||||
prog = int((packet_count / max_pkts) * 100)
|
||||
self.shared_data.bjorn_progress = f"{prog}%"
|
||||
self.shared_data.log_milestone(b_class, "Status", f"Captured {packet_count} packets")
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"Capture error: {e}")
|
||||
logger.error(f"Capture error: {e}")
|
||||
self.shared_data.log_milestone(b_class, "Error", str(e))
|
||||
return "failed"
|
||||
finally:
|
||||
self.cleanup()
|
||||
if self.capture:
|
||||
try: self.capture.close()
|
||||
except: pass
|
||||
|
||||
# Save results
|
||||
if self.credentials or self.statistics['total_packets'] > 0:
|
||||
os.makedirs(output_dir, exist_ok=True)
|
||||
ts = datetime.now().strftime("%Y%m%d_%H%M%S")
|
||||
with open(os.path.join(output_dir, f"odin_recon_{ts}.json"), 'w') as f:
|
||||
json.dump({
|
||||
"stats": dict(self.statistics),
|
||||
"credentials": self.credentials
|
||||
}, f, indent=4)
|
||||
self.shared_data.log_milestone(b_class, "Complete", f"Capture finished. {len(self.credentials)} creds found.")
|
||||
|
||||
def handle_interrupt(self, signum, frame):
|
||||
self.stop_capture.set()
|
||||
|
||||
def cleanup(self):
|
||||
if self.capture:
|
||||
self.capture.close()
|
||||
self.save_results()
|
||||
logging.info("Capture completed")
|
||||
|
||||
def save_settings(interface, capture_filter, output_dir, timeout, max_packets):
|
||||
try:
|
||||
os.makedirs(DEFAULT_SETTINGS_DIR, exist_ok=True)
|
||||
settings = {
|
||||
"interface": interface,
|
||||
"capture_filter": capture_filter,
|
||||
"output_dir": output_dir,
|
||||
"timeout": timeout,
|
||||
"max_packets": max_packets
|
||||
}
|
||||
with open(SETTINGS_FILE, 'w') as f:
|
||||
json.dump(settings, f)
|
||||
logging.info(f"Settings saved to {SETTINGS_FILE}")
|
||||
except Exception as e:
|
||||
logging.error(f"Failed to save settings: {e}")
|
||||
|
||||
def load_settings():
|
||||
if os.path.exists(SETTINGS_FILE):
|
||||
try:
|
||||
with open(SETTINGS_FILE, 'r') as f:
|
||||
return json.load(f)
|
||||
except Exception as e:
|
||||
logging.error(f"Failed to load settings: {e}")
|
||||
return {}
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="OdinEye: network traffic analyzer & credential hunter")
|
||||
parser.add_argument("-i", "--interface", required=False, help="Network interface to monitor")
|
||||
parser.add_argument("-f", "--filter", default=DEFAULT_FILTER, help="BPF capture filter")
|
||||
parser.add_argument("-o", "--output", default=DEFAULT_OUTPUT_DIR, help="Output directory")
|
||||
parser.add_argument("-t", "--timeout", type=int, default=300, help="Capture timeout in seconds")
|
||||
parser.add_argument("-m", "--max-packets", type=int, default=10000, help="Maximum packets to capture")
|
||||
args = parser.parse_args()
|
||||
|
||||
settings = load_settings()
|
||||
interface = args.interface or settings.get("interface")
|
||||
capture_filter = args.filter or settings.get("capture_filter", DEFAULT_FILTER)
|
||||
output_dir = args.output or settings.get("output_dir", DEFAULT_OUTPUT_DIR)
|
||||
timeout = args.timeout or settings.get("timeout", 300)
|
||||
max_packets = args.max_packets or settings.get("max_packets", 10000)
|
||||
|
||||
if not interface:
|
||||
logging.error("Interface is required. Use -i or set it in settings")
|
||||
return
|
||||
|
||||
save_settings(interface, capture_filter, output_dir, timeout, max_packets)
|
||||
analyzer = OdinEye(interface, capture_filter, output_dir, timeout, max_packets)
|
||||
analyzer.execute()
|
||||
return "success"
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
|
||||
|
||||
|
||||
"""
|
||||
# action_template.py
|
||||
# Example template for a Bjorn action with Neo launcher support
|
||||
|
||||
# UI Metadata
|
||||
b_class = "MyAction"
|
||||
b_module = "my_action"
|
||||
b_enabled = 1
|
||||
b_action = "normal" # normal, aggressive, stealth
|
||||
b_description = "Description of what this action does"
|
||||
|
||||
# Arguments schema for UI
|
||||
b_args = {
|
||||
"target": {
|
||||
"type": "text",
|
||||
"label": "Target IP/Host",
|
||||
"default": "192.168.1.1",
|
||||
"placeholder": "Enter target",
|
||||
"help": "The target to scan"
|
||||
},
|
||||
"port": {
|
||||
"type": "number",
|
||||
"label": "Port",
|
||||
"default": 80,
|
||||
"min": 1,
|
||||
"max": 65535
|
||||
},
|
||||
"protocol": {
|
||||
"type": "select",
|
||||
"label": "Protocol",
|
||||
"choices": ["tcp", "udp"],
|
||||
"default": "tcp"
|
||||
},
|
||||
"verbose": {
|
||||
"type": "checkbox",
|
||||
"label": "Verbose output",
|
||||
"default": False
|
||||
},
|
||||
"timeout": {
|
||||
"type": "slider",
|
||||
"label": "Timeout (seconds)",
|
||||
"min": 10,
|
||||
"max": 300,
|
||||
"step": 10,
|
||||
"default": 60
|
||||
}
|
||||
}
|
||||
|
||||
def compute_dynamic_b_args(base: dict) -> dict:
|
||||
# Compute dynamic values at runtime
|
||||
return base
|
||||
|
||||
import argparse
|
||||
import sys
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description=b_description)
|
||||
parser.add_argument('--target', default=b_args['target']['default'])
|
||||
parser.add_argument('--port', type=int, default=b_args['port']['default'])
|
||||
parser.add_argument('--protocol', choices=b_args['protocol']['choices'],
|
||||
default=b_args['protocol']['default'])
|
||||
parser.add_argument('--verbose', action='store_true')
|
||||
parser.add_argument('--timeout', type=int, default=b_args['timeout']['default'])
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
# Your action logic here
|
||||
print(f"Starting action with target: {args.target}")
|
||||
# ...
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
"""
|
||||
from init_shared import shared_data
|
||||
eye = OdinEye(shared_data)
|
||||
eye.execute("0.0.0.0", None, {}, "odin_eye")
|
||||
Reference in New Issue
Block a user