feat: DECNET-PROBER standalone JARM fingerprinting service
Add active TLS probing via JARM to identify C2 frameworks (Cobalt Strike, Sliver, Metasploit) by their TLS server implementation quirks. Runs as a detached host-level process — no container dependency. - decnet/prober/jarm.py: pure-stdlib JARM implementation (10 crafted probes) - decnet/prober/worker.py: standalone async worker with RFC 5424 + JSON output - CLI: `decnet probe --targets ip:port` and `--probe-targets` on deploy - Ingester: JARM bounty extraction (fingerprint type) - 68 new tests covering JARM logic and bounty extraction
This commit is contained in:
@@ -120,6 +120,8 @@ def deploy(
|
||||
config_file: Optional[str] = typer.Option(None, "--config", "-c", help="Path to INI config file"),
|
||||
api: bool = typer.Option(False, "--api", help="Start the FastAPI backend to ingest and serve logs"),
|
||||
api_port: int = typer.Option(8000, "--api-port", help="Port for the backend API"),
|
||||
probe_targets: Optional[str] = typer.Option(None, "--probe-targets", help="Comma-separated ip:port pairs for JARM active probing (e.g. 10.0.0.1:443,10.0.0.2:8443)"),
|
||||
probe_interval: int = typer.Option(300, "--probe-interval", help="Seconds between JARM probe cycles (default: 300)"),
|
||||
) -> None:
|
||||
"""Deploy deckies to the LAN."""
|
||||
import os
|
||||
@@ -296,6 +298,43 @@ def deploy(
|
||||
except (FileNotFoundError, subprocess.SubprocessError):
|
||||
console.print("[red]Failed to start API. Ensure 'uvicorn' is installed in the current environment.[/]")
|
||||
|
||||
if probe_targets and not dry_run:
|
||||
import subprocess # nosec B404
|
||||
import sys
|
||||
console.print(f"[bold cyan]Starting DECNET-PROBER[/] → targets: {probe_targets}")
|
||||
try:
|
||||
_prober_args = [
|
||||
sys.executable, "-m", "decnet.cli", "probe",
|
||||
"--targets", probe_targets,
|
||||
"--interval", str(probe_interval),
|
||||
]
|
||||
if effective_log_file:
|
||||
_prober_args.extend(["--log-file", str(effective_log_file)])
|
||||
subprocess.Popen( # nosec B603
|
||||
_prober_args,
|
||||
stdin=subprocess.DEVNULL,
|
||||
stdout=subprocess.DEVNULL,
|
||||
stderr=subprocess.STDOUT,
|
||||
start_new_session=True,
|
||||
)
|
||||
except (FileNotFoundError, subprocess.SubprocessError):
|
||||
console.print("[red]Failed to start DECNET-PROBER.[/]")
|
||||
|
||||
|
||||
@app.command()
|
||||
def probe(
|
||||
targets: str = typer.Option(..., "--targets", "-t", help="Comma-separated ip:port pairs to JARM fingerprint"),
|
||||
log_file: str = typer.Option(DECNET_INGEST_LOG_FILE, "--log-file", "-f", help="Path for RFC 5424 syslog + .json output"),
|
||||
interval: int = typer.Option(300, "--interval", "-i", help="Seconds between probe cycles (default: 300)"),
|
||||
timeout: float = typer.Option(5.0, "--timeout", help="Per-probe TCP timeout in seconds"),
|
||||
) -> None:
|
||||
"""Run JARM active fingerprinting against target hosts."""
|
||||
import asyncio
|
||||
from decnet.prober import prober_worker
|
||||
log.info("probe command invoked targets=%s interval=%d", targets, interval)
|
||||
console.print(f"[bold cyan]DECNET-PROBER starting[/] → {targets}")
|
||||
asyncio.run(prober_worker(log_file, targets, interval=interval, timeout=timeout))
|
||||
|
||||
|
||||
@app.command()
|
||||
def collect(
|
||||
|
||||
Reference in New Issue
Block a user