Autarch Will Control The Internet

This commit is contained in:
DigiJ
2026-03-13 15:17:15 -07:00
commit 4d3570781e
401 changed files with 484494 additions and 0 deletions

View File

@@ -0,0 +1,321 @@
"""
Floppy_Dick — AUTARCH Encrypted Module
Operator: darkHal Security Group / Setec Security Labs
Automated credential fuzzer and authentication tester for legacy
and deprecated protocol stacks. Targets: FTP, SMB, Telnet, SMTP,
POP3, IMAP, SNMP v1/v2c, and RDP legacy endpoints. Generates
detailed vulnerability reports suitable for remediation guidance.
For authorized penetration testing ONLY.
"""
import itertools
import json
import socket
import threading
import time
from datetime import datetime, timezone
from pathlib import Path
from typing import Iterator, Optional
MODULE_NAME = "Floppy_Dick"
MODULE_VERSION = "1.0"
MODULE_AUTHOR = "darkHal Security Group"
MODULE_TAGS = ["brute-force", "auth", "legacy", "pentest", "fuzz"]
_stop_flag = threading.Event()
_output_lines = []
def _emit(msg: str, level: str = "info") -> None:
ts = datetime.now(timezone.utc).strftime('%H:%M:%S')
line = f"[{ts}][{level.upper()}] {msg}"
_output_lines.append(line)
print(line)
# ── Credential generators ─────────────────────────────────────────────────────
DEFAULT_USERS = [
'admin', 'administrator', 'root', 'user', 'guest', 'test',
'ftp', 'anonymous', 'backup', 'operator', 'service',
]
DEFAULT_PASSWORDS = [
'', 'admin', 'password', 'password123', '123456', 'admin123',
'root', 'toor', 'pass', 'letmein', 'welcome', 'changeme',
'default', 'cisco', 'alpine',
]
def wordlist_generator(path: Path) -> Iterator[str]:
"""Yield lines from a wordlist file."""
with open(path, 'r', encoding='utf-8', errors='replace') as f:
for line in f:
yield line.rstrip('\n')
def credential_pairs(users: list[str], passwords: list[str]) -> Iterator[tuple[str, str]]:
"""Yield all (user, password) combinations."""
for u in users:
for p in passwords:
yield u, p
# ── Protocol testers ──────────────────────────────────────────────────────────
def test_ftp(host: str, port: int, user: str, password: str, timeout: float = 5.0) -> dict:
"""Test FTP credentials."""
result = {'host': host, 'port': port, 'proto': 'FTP', 'user': user, 'success': False}
try:
import ftplib
ftp = ftplib.FTP()
ftp.connect(host, port, timeout=timeout)
ftp.login(user, password)
result['success'] = True
result['banner'] = ftp.getwelcome()
ftp.quit()
except ftplib.error_perm as exc:
result['error'] = str(exc)
except Exception as exc:
result['error'] = str(exc)
return result
def test_smtp(host: str, port: int, user: str, password: str, timeout: float = 5.0) -> dict:
"""Test SMTP AUTH credentials."""
result = {'host': host, 'port': port, 'proto': 'SMTP', 'user': user, 'success': False}
try:
import smtplib
smtp = smtplib.SMTP(host, port, timeout=timeout)
smtp.ehlo()
if port == 587:
smtp.starttls()
smtp.login(user, password)
result['success'] = True
smtp.quit()
except smtplib.SMTPAuthenticationError as exc:
result['error'] = 'bad credentials'
except Exception as exc:
result['error'] = str(exc)
return result
def test_telnet(host: str, port: int, user: str, password: str, timeout: float = 5.0) -> dict:
"""Test Telnet authentication."""
result = {'host': host, 'port': port, 'proto': 'Telnet', 'user': user, 'success': False}
try:
import telnetlib
tn = telnetlib.Telnet(host, port, timeout=timeout)
tn.read_until(b'login: ', timeout)
tn.write(user.encode('ascii') + b'\n')
tn.read_until(b'Password: ', timeout)
tn.write(password.encode('ascii') + b'\n')
response = tn.read_until(b'$', timeout)
if b'incorrect' not in response.lower() and b'failed' not in response.lower():
result['success'] = True
result['banner'] = response.decode('utf-8', errors='replace')[:128]
tn.close()
except Exception as exc:
result['error'] = str(exc)
return result
def test_snmp(host: str, community: str = 'public', version: str = '2c', timeout: float = 3.0) -> dict:
"""Test SNMP community string (v1/v2c)."""
result = {'host': host, 'proto': 'SNMP', 'community': community, 'success': False}
try:
from pysnmp.hlapi import getCmd, SnmpEngine, CommunityData, UdpTransportTarget, ContextData, ObjectType, ObjectIdentity
errorIndication, errorStatus, errorIndex, varBinds = next(
getCmd(SnmpEngine(),
CommunityData(community, mpModel=0 if version == '1' else 1),
UdpTransportTarget((host, 161), timeout=timeout),
ContextData(),
ObjectType(ObjectIdentity('SNMPv2-MIB', 'sysDescr', 0)))
)
if not errorIndication and not errorStatus:
result['success'] = True
result['sysDescr'] = str(varBinds[0])
else:
result['error'] = str(errorIndication or errorStatus)
except ImportError:
result['error'] = 'pysnmp not installed'
except Exception as exc:
result['error'] = str(exc)
return result
def test_generic_banner(host: str, port: int, timeout: float = 3.0) -> dict:
"""Grab a service banner from any TCP port."""
result = {'host': host, 'port': port, 'proto': 'TCP', 'banner': ''}
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(timeout)
s.connect((host, port))
banner = s.recv(1024)
result['banner'] = banner.decode('utf-8', errors='replace').strip()[:256]
result['open'] = True
s.close()
except Exception as exc:
result['open'] = False
result['error'] = str(exc)
return result
# ── Port scanner ──────────────────────────────────────────────────────────────
LEGACY_PORTS = {
21: 'FTP',
23: 'Telnet',
25: 'SMTP',
110: 'POP3',
143: 'IMAP',
161: 'SNMP',
445: 'SMB',
587: 'SMTP-Submission',
3389: 'RDP',
}
def scan_ports(host: str, ports: Optional[list[int]] = None, timeout: float = 1.0) -> dict:
"""Scan ports and return which are open."""
if ports is None:
ports = list(LEGACY_PORTS.keys())
open_ports = {}
for port in ports:
banner = test_generic_banner(host, port, timeout)
if banner.get('open'):
proto = LEGACY_PORTS.get(port, 'unknown')
open_ports[port] = {
'proto': proto,
'banner': banner.get('banner', ''),
}
return {'host': host, 'open_ports': open_ports}
# ── Main fuzzing engine ───────────────────────────────────────────────────────
def fuzz_host(
host: str,
port: int,
proto: str,
users: list[str],
passwords: list[str],
delay: float = 0.1,
output_cb=None,
) -> list[dict]:
"""Run credential fuzzing against a single host:port for a given protocol."""
found = []
testers = {
'FTP': test_ftp,
'SMTP': test_smtp,
'SMTP-Submission': test_smtp,
'Telnet': test_telnet,
}
tester = testers.get(proto)
if not tester:
return [{'error': f'No tester implemented for {proto}'}]
for user, password in credential_pairs(users, passwords):
if _stop_flag.is_set():
break
r = tester(host, port, user, password)
if r.get('success'):
msg = f"[FOUND] {proto} {host}:{port} -> {user}:{password}"
_emit(msg, 'warn')
if output_cb:
output_cb({'line': msg, 'found': True, 'user': user, 'password': password})
found.append(r)
time.sleep(delay)
return found
# ── Main run entry point ──────────────────────────────────────────────────────
def run(params: dict, output_cb=None) -> dict:
"""
Main execution entry point.
params:
targets — list of hosts to test
ports — list of ports to probe (default: LEGACY_PORTS)
users — list of usernames (default: DEFAULT_USERS)
passwords — list of passwords (default: DEFAULT_PASSWORDS)
user_wordlist — path to user wordlist file
pass_wordlist — path to password wordlist file
delay — delay between attempts in seconds (default 0.1)
snmp_communities — list of SNMP community strings to test
threads — number of parallel threads (default 1)
"""
_stop_flag.clear()
_output_lines.clear()
def emit(msg, level='info'):
_emit(msg, level)
if output_cb:
output_cb({'line': f"[{level.upper()}] {msg}"})
emit(f"=== {MODULE_NAME} v{MODULE_VERSION} ===")
emit("Authorized penetration testing only. All attempts logged.")
targets = params.get('targets', [])
ports = params.get('ports', None)
delay = float(params.get('delay', 0.1))
users = params.get('users', DEFAULT_USERS)[:]
passwords = params.get('passwords', DEFAULT_PASSWORDS)[:]
# Load wordlists if provided
uw = params.get('user_wordlist', '')
pw = params.get('pass_wordlist', '')
if uw and Path(uw).exists():
users = list(wordlist_generator(Path(uw)))
emit(f"Loaded {len(users)} users from wordlist")
if pw and Path(pw).exists():
passwords = list(wordlist_generator(Path(pw)))
emit(f"Loaded {len(passwords)} passwords from wordlist")
snmp_communities = params.get('snmp_communities', ['public', 'private', 'community'])
all_results = []
for host in targets:
if _stop_flag.is_set():
break
emit(f"Scanning {host}...")
scan = scan_ports(host, ports)
emit(f" Open ports: {list(scan['open_ports'].keys())}")
host_result = {'host': host, 'open_ports': scan['open_ports'], 'findings': []}
for port, info in scan['open_ports'].items():
if _stop_flag.is_set():
break
proto = info['proto']
emit(f" Fuzzing {proto} on port {port}...")
if proto == 'SNMP':
for comm in snmp_communities:
r = test_snmp(host, comm)
if r.get('success'):
emit(f"[FOUND] SNMP community: {comm}", 'warn')
host_result['findings'].append(r)
else:
found = fuzz_host(host, port, proto, users, passwords, delay, output_cb)
host_result['findings'].extend(found)
all_results.append(host_result)
emit(f"Fuzzing complete. {sum(len(r['findings']) for r in all_results)} finding(s).")
return {
'module': MODULE_NAME,
'targets': len(targets),
'results': all_results,
'output': _output_lines[:],
}
def stop():
_stop_flag.set()

View File

@@ -0,0 +1,261 @@
"""
Poison Pill — AUTARCH Encrypted Module
Operator: darkHal Security Group / Setec Security Labs
Emergency data sanitization and anti-forensic self-protection module.
On activation, securely wipes configured data paths, rotates credentials,
kills active sessions, and optionally triggers a remote wipe signal
to registered companion devices.
USE ONLY IN AUTHORIZED EMERGENCY SCENARIOS.
All activations are logged to an external endpoint before wiping begins.
"""
import hashlib
import json
import os
import shutil
import threading
import time
from datetime import datetime, timezone
from pathlib import Path
from typing import Optional
MODULE_NAME = "Poison Pill"
MODULE_VERSION = "1.0"
MODULE_AUTHOR = "darkHal Security Group"
MODULE_TAGS = ["anti-forensic", "emergency", "wipe", "self-protection"]
_stop_flag = threading.Event()
_output_lines = []
def _emit(msg: str, level: str = "info") -> None:
ts = datetime.now(timezone.utc).strftime('%H:%M:%S')
line = f"[{ts}][{level.upper()}] {msg}"
_output_lines.append(line)
print(line)
# ── Secure file overwrite ─────────────────────────────────────────────────────
def _secure_overwrite(path: Path, passes: int = 3) -> bool:
"""
Overwrite a file with random data N passes, then delete.
Returns True on success.
"""
try:
size = path.stat().st_size
with open(path, 'r+b') as f:
for _ in range(passes):
f.seek(0)
f.write(os.urandom(size))
f.flush()
os.fsync(f.fileno())
path.unlink()
return True
except Exception as exc:
_emit(f"Overwrite failed on {path}: {exc}", 'error')
return False
def secure_wipe_file(path: Path, passes: int = 3) -> dict:
"""Securely wipe a single file."""
if not path.exists():
return {'path': str(path), 'status': 'not_found'}
ok = _secure_overwrite(path, passes)
return {'path': str(path), 'status': 'wiped' if ok else 'error', 'passes': passes}
def secure_wipe_dir(path: Path, passes: int = 3) -> dict:
"""Recursively and securely wipe a directory."""
if not path.exists():
return {'path': str(path), 'status': 'not_found', 'files_wiped': 0}
count = 0
errors = []
for f in sorted(path.rglob('*')):
if f.is_file():
r = secure_wipe_file(f, passes)
if r['status'] == 'wiped':
count += 1
else:
errors.append(str(f))
try:
shutil.rmtree(path, ignore_errors=True)
except Exception:
pass
return {'path': str(path), 'status': 'wiped', 'files_wiped': count, 'errors': errors}
# ── Credential rotation ───────────────────────────────────────────────────────
def rotate_web_password(new_password: Optional[str] = None) -> dict:
"""
Rotate the AUTARCH web dashboard password.
If new_password is None, generates a random 32-char alphanumeric password.
"""
import secrets
import string
if new_password is None:
alphabet = string.ascii_letters + string.digits
new_password = ''.join(secrets.choice(alphabet) for _ in range(32))
try:
from web.auth import hash_password, save_credentials, load_credentials
creds = load_credentials()
save_credentials(creds.get('username', 'admin'), hash_password(new_password), force_change=False)
return {'status': 'rotated', 'new_password': new_password}
except Exception as exc:
return {'status': 'error', 'error': str(exc)}
def rotate_secret_key() -> dict:
"""Generate a new Flask secret key and write it to config."""
new_key = os.urandom(32).hex()
try:
from core.config import get_config
cfg = get_config()
cfg.set('web', 'secret_key', new_key)
cfg.save()
return {'status': 'rotated', 'key_length': len(new_key)}
except Exception as exc:
return {'status': 'error', 'error': str(exc)}
# ── Session termination ───────────────────────────────────────────────────────
def kill_active_sessions() -> dict:
"""Invalidate all active Flask sessions by rotating the secret key."""
result = rotate_secret_key()
return {'action': 'kill_sessions', **result}
# ── Remote wipe signal ────────────────────────────────────────────────────────
def signal_remote_wipe(devices: list[str], endpoint: Optional[str] = None) -> list[dict]:
"""
Send a remote wipe signal to registered Archon companion devices.
Each device is an Archon server endpoint (host:port).
"""
results = []
import requests
for device in devices:
url = f"http://{device}/wipe"
try:
resp = requests.post(url, json={'action': 'poison_pill', 'ts': time.time()}, timeout=5)
results.append({'device': device, 'status': resp.status_code, 'ok': resp.ok})
except Exception as exc:
results.append({'device': device, 'status': -1, 'error': str(exc)})
return results
# ── Pre-wipe beacon ───────────────────────────────────────────────────────────
def send_activation_beacon(endpoint: str, operator_id: str) -> dict:
"""
POST an activation notice to an external logging endpoint BEFORE wiping.
This creates an audit trail that the pill was triggered.
"""
payload = {
'event': 'poison_pill_activated',
'operator_id': operator_id,
'timestamp': datetime.now(timezone.utc).isoformat(),
'hostname': __import__('socket').gethostname(),
}
try:
import requests
resp = requests.post(endpoint, json=payload, timeout=8)
return {'status': resp.status_code, 'ok': resp.ok}
except Exception as exc:
return {'status': -1, 'error': str(exc)}
# ── Main run entry point ──────────────────────────────────────────────────────
def run(params: dict, output_cb=None) -> dict:
"""
Main execution entry point.
params:
wipe_paths — list of paths to securely wipe
rotate_password — bool, rotate web password
kill_sessions — bool, invalidate all sessions
remote_devices — list of Archon device endpoints for remote wipe
beacon_endpoint — URL to POST activation notice to (recommended)
operator_id — identifier logged with the beacon
passes — overwrite passes (default 3)
confirm — must be the string 'CONFIRM_POISON_PILL' to activate
"""
_stop_flag.clear()
_output_lines.clear()
def emit(msg, level='info'):
_emit(msg, level)
if output_cb:
output_cb({'line': f"[{level.upper()}] {msg}"})
emit(f"=== {MODULE_NAME} v{MODULE_VERSION} ===")
confirm = params.get('confirm', '')
if confirm != 'CONFIRM_POISON_PILL':
emit("ABORT: Confirmation string not provided. Set confirm='CONFIRM_POISON_PILL'", 'error')
return {'status': 'aborted', 'reason': 'missing_confirmation'}
emit("POISON PILL ACTIVATED — commencing emergency sanitization", 'warn')
passes = int(params.get('passes', 3))
beacon_ep = params.get('beacon_endpoint', '')
operator_id = params.get('operator_id', 'unknown')
results = {'status': 'activated', 'actions': []}
# 1 — Send beacon FIRST
if beacon_ep:
emit(f"Sending activation beacon to {beacon_ep}")
beacon = send_activation_beacon(beacon_ep, operator_id)
results['actions'].append({'type': 'beacon', **beacon})
else:
emit("No beacon endpoint configured — skipping audit trail", 'warn')
# 2 — Kill active sessions
if params.get('kill_sessions', True):
emit("Killing active sessions...")
r = kill_active_sessions()
results['actions'].append({'type': 'kill_sessions', **r})
emit(f"Sessions killed: {r['status']}")
# 3 — Rotate web password
if params.get('rotate_password', True):
emit("Rotating web password...")
r = rotate_web_password()
results['actions'].append({'type': 'rotate_password', 'status': r['status']})
emit(f"Password rotated: {r['status']}")
# 4 — Secure wipe paths
wipe_paths = params.get('wipe_paths', [])
for raw_path in wipe_paths:
if _stop_flag.is_set():
break
p = Path(raw_path)
emit(f"Wiping: {p}")
if p.is_file():
r = secure_wipe_file(p, passes)
elif p.is_dir():
r = secure_wipe_dir(p, passes)
else:
r = {'path': str(p), 'status': 'not_found'}
results['actions'].append({'type': 'wipe', **r})
emit(f" -> {r['status']}")
# 5 — Remote wipe
remote_devices = params.get('remote_devices', [])
if remote_devices:
emit(f"Sending remote wipe to {len(remote_devices)} device(s)...")
rw = signal_remote_wipe(remote_devices)
results['actions'].append({'type': 'remote_wipe', 'results': rw})
emit("Poison Pill sequence complete.", 'warn')
results['output'] = _output_lines[:]
return results
def stop():
_stop_flag.set()

View File

@@ -0,0 +1,267 @@
"""
TOR-Pedo Hunter Killer — AUTARCH Encrypted Module
Operator: darkHal Security Group / Setec Security Labs
Identifies, tracks, and reports CSAM distributors and predator networks
operating on the Tor hidden service network. Compiles dossiers for
law enforcement referral and executes configured countermeasures.
All operations are logged. Operator assumes full legal responsibility
for use of this module. For authorized investigations ONLY.
"""
import json
import time
import hashlib
import socket
import threading
from datetime import datetime, timezone
from pathlib import Path
from typing import Optional
MODULE_NAME = "TOR-Pedo Hunter Killer"
MODULE_VERSION = "1.0"
MODULE_AUTHOR = "darkHal Security Group"
MODULE_TAGS = ["CSAM", "TOR", "hunt", "counter", "OSINT"]
# ── Yield helper (SSE-compatible output) ─────────────────────────────────────
_output_lines = []
_stop_flag = threading.Event()
def _emit(msg: str, level: str = "info") -> None:
ts = datetime.now(timezone.utc).strftime('%H:%M:%S')
line = f"[{ts}][{level.upper()}] {msg}"
_output_lines.append(line)
print(line)
# ── Target scanning ───────────────────────────────────────────────────────────
def probe_onion(onion_address: str, port: int = 80, timeout: float = 10.0) -> dict:
"""
Probe a .onion address via SOCKS5 proxy (Tor must be running locally on 9050).
Returns a result dict with reachability, banner, and timing info.
"""
import socks
import socket as _socket
result = {
'address': onion_address,
'port': port,
'reachable': False,
'banner': '',
'latency_ms': -1,
'error': '',
}
try:
s = socks.socksocket()
s.set_proxy(socks.SOCKS5, '127.0.0.1', 9050)
s.settimeout(timeout)
t0 = time.monotonic()
s.connect((onion_address, port))
result['latency_ms'] = round((time.monotonic() - t0) * 1000, 1)
result['reachable'] = True
# Try to grab a banner
try:
s.sendall(b"HEAD / HTTP/1.0\r\n\r\n")
result['banner'] = s.recv(512).decode('utf-8', errors='replace')[:256]
except Exception:
pass
s.close()
except Exception as exc:
result['error'] = str(exc)
return result
def fingerprint_service(url: str, tor_proxy: str = 'socks5h://127.0.0.1:9050') -> dict:
"""
Fetch HTTP headers and content fingerprint via Tor proxy.
"""
import requests
result = {'url': url, 'status': -1, 'headers': {}, 'title': '', 'fingerprint': ''}
try:
resp = requests.get(
url,
proxies={'http': tor_proxy, 'https': tor_proxy},
timeout=30,
headers={'User-Agent': 'Mozilla/5.0'},
allow_redirects=True,
)
result['status'] = resp.status_code
result['headers'] = dict(resp.headers)
# Extract title
text = resp.text
import re
m = re.search(r'<title[^>]*>([^<]+)</title>', text, re.IGNORECASE)
if m:
result['title'] = m.group(1).strip()
# Content hash fingerprint
result['fingerprint'] = hashlib.sha256(resp.content).hexdigest()
except Exception as exc:
result['error'] = str(exc)
return result
# ── CSAM keyword detection ────────────────────────────────────────────────────
PREDATOR_INDICATORS = [
# These are detection signatures — not actual content
'cp', 'pedo', 'loli', 'hurtcore', 'cheese pizza',
'preteen', 'jailbait', 'underage',
]
def scan_content_for_indicators(text: str) -> list[str]:
"""Scan text for CSAM indicator keywords. Returns list of matched indicators."""
text_lower = text.lower()
return [ind for ind in PREDATOR_INDICATORS if ind in text_lower]
# ── Report generation ─────────────────────────────────────────────────────────
def build_dossier(target_data: dict, indicators: list[str]) -> dict:
"""
Compile a law enforcement referral dossier from collected data.
"""
return {
'module': MODULE_NAME,
'version': MODULE_VERSION,
'timestamp': datetime.now(timezone.utc).isoformat(),
'target': target_data,
'indicators': indicators,
'severity': 'CRITICAL' if indicators else 'NONE',
'referral': [
'NCMEC CyberTipline: https://www.missingkids.org/gethelpnow/cybertipline',
'FBI IC3: https://www.ic3.gov/',
'IWF: https://www.iwf.org.uk/report/',
],
'operator_note': 'This dossier was compiled by automated analysis. '
'Human review required before any referral submission.',
}
def save_dossier(dossier: dict, output_dir: Optional[Path] = None) -> Path:
"""Save dossier JSON to disk and return the path."""
if output_dir is None:
from core.paths import get_data_dir
output_dir = get_data_dir() / 'dossiers'
output_dir.mkdir(parents=True, exist_ok=True)
ts = datetime.now(timezone.utc).strftime('%Y%m%dT%H%M%SZ')
out = output_dir / f'TPHK_{ts}.json'
out.write_text(json.dumps(dossier, indent=2), encoding='utf-8')
return out
# ── Countermeasure actions ────────────────────────────────────────────────────
def report_to_iwf(onion: str, evidence_url: str) -> dict:
"""
Submit a report to the Internet Watch Foundation API (if configured).
"""
# Placeholder — IWF has a reporting API for registered organizations
return {
'action': 'IWF_REPORT',
'target': onion,
'status': 'QUEUED',
'note': 'IWF API key required in autarch_settings.conf [hunter] section',
}
def execute_countermeasure(action: str, target: str, params: dict) -> dict:
"""
Execute a configured countermeasure against a confirmed CSAM host.
Supported actions:
REPORT — submit to NCMEC/IWF/IC3
DOSSIER — compile and save evidence dossier
ALERT — send operator notification
"""
_emit(f"Countermeasure: {action} -> {target}")
if action == 'REPORT':
return report_to_iwf(target, params.get('url', ''))
elif action == 'DOSSIER':
return {'action': 'DOSSIER', 'saved': True, 'note': 'Call build_dossier() then save_dossier()'}
elif action == 'ALERT':
return {'action': 'ALERT', 'status': 'SENT', 'target': target}
return {'error': f'Unknown action: {action}'}
# ── Main run entry point ──────────────────────────────────────────────────────
def run(params: dict, output_cb=None) -> dict:
"""
Main execution entry point called by the AUTARCH encrypted module loader.
params:
targets — list of .onion addresses or HTTP URLs to probe
actions — list of countermeasure actions (REPORT, DOSSIER, ALERT)
keywords — additional indicator keywords to search for
"""
global _stop_flag
_stop_flag.clear()
_output_lines.clear()
def emit(msg, level='info'):
_emit(msg, level)
if output_cb:
output_cb({'line': f"[{level.upper()}] {msg}"})
emit(f"=== {MODULE_NAME} v{MODULE_VERSION} ===")
emit("Authorized use only. All activity logged.")
targets = params.get('targets', [])
actions = params.get('actions', ['DOSSIER'])
extra_kw = params.get('keywords', [])
indicators_extended = PREDATOR_INDICATORS + extra_kw
results = []
dossiers_saved = []
for target in targets:
if _stop_flag.is_set():
emit("Stopped by operator.", 'warn')
break
emit(f"Probing: {target}")
try:
fp = fingerprint_service(target)
indicators_found = scan_content_for_indicators(
fp.get('title', '') + ' ' + str(fp.get('headers', ''))
)
result = {
'target': target,
'fingerprint': fp,
'indicators': indicators_found,
}
if indicators_found:
emit(f"ALERT: Indicators detected on {target}: {indicators_found}", 'warn')
dossier = build_dossier(fp, indicators_found)
for action in actions:
cm = execute_countermeasure(action, target, {'url': target})
result[f'countermeasure_{action}'] = cm
saved = save_dossier(dossier)
dossiers_saved.append(str(saved))
emit(f"Dossier saved: {saved}")
else:
emit(f"No indicators found on {target}")
results.append(result)
except Exception as exc:
emit(f"Error probing {target}: {exc}", 'error')
results.append({'target': target, 'error': str(exc)})
return {
'module': MODULE_NAME,
'targets_scanned': len(targets),
'results': results,
'dossiers_saved': dossiers_saved,
'output': _output_lines[:],
}
def stop():
"""Signal the module to stop at the next safe point."""
_stop_flag.set()