Initial build: geo-scout geo-targeted network scanner
masscan port discovery → nmap fingerprinting → probe engine → pattern/version matching Dynamic YAML inputs: countries.yaml (country codes + CIDR resolution) and targets.yaml (fingerprint probes) RIR delegated stats for authoritative country CIDR data (no external API) Supports tcp_banner, http, https, rtsp, udp probe types Version extraction and comparison operators (<=, >=, ==, etc.) SQLite results per engagement, CSV/JSON export format-spec/ docs for generating input files externally
This commit is contained in:
@@ -0,0 +1,117 @@
|
||||
import json
|
||||
import sqlite3
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
|
||||
RESULTS_DIR = Path(__file__).parent.parent / "results"
|
||||
|
||||
|
||||
def get_db(engagement: str) -> sqlite3.Connection:
|
||||
RESULTS_DIR.mkdir(parents=True, exist_ok=True)
|
||||
db_path = RESULTS_DIR / f"{engagement}.db"
|
||||
conn = sqlite3.connect(str(db_path))
|
||||
conn.row_factory = sqlite3.Row
|
||||
_init(conn)
|
||||
return conn
|
||||
|
||||
|
||||
def _init(conn: sqlite3.Connection) -> None:
|
||||
conn.executescript("""
|
||||
CREATE TABLE IF NOT EXISTS runs (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
started_at TEXT NOT NULL,
|
||||
finished_at TEXT,
|
||||
countries TEXT,
|
||||
targets TEXT,
|
||||
total_hosts INTEGER DEFAULT 0,
|
||||
total_matches INTEGER DEFAULT 0
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS hits (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
run_id INTEGER NOT NULL,
|
||||
ip TEXT NOT NULL,
|
||||
port INTEGER NOT NULL,
|
||||
country_code TEXT,
|
||||
target_name TEXT NOT NULL,
|
||||
tags TEXT,
|
||||
probe_type TEXT,
|
||||
confidence TEXT,
|
||||
version TEXT,
|
||||
nmap_service TEXT,
|
||||
nmap_banner TEXT,
|
||||
found_at TEXT NOT NULL,
|
||||
FOREIGN KEY (run_id) REFERENCES runs(id)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_hits_ip ON hits(ip);
|
||||
CREATE INDEX IF NOT EXISTS idx_hits_target ON hits(target_name);
|
||||
CREATE INDEX IF NOT EXISTS idx_hits_country ON hits(country_code);
|
||||
CREATE INDEX IF NOT EXISTS idx_hits_run ON hits(run_id);
|
||||
""")
|
||||
conn.commit()
|
||||
|
||||
|
||||
def start_run(conn: sqlite3.Connection, countries: list, targets: list) -> int:
|
||||
cur = conn.execute(
|
||||
"INSERT INTO runs (started_at, countries, targets) VALUES (?, ?, ?)",
|
||||
(datetime.utcnow().isoformat(), json.dumps(countries), json.dumps(targets)),
|
||||
)
|
||||
conn.commit()
|
||||
return cur.lastrowid
|
||||
|
||||
|
||||
def finish_run(conn: sqlite3.Connection, run_id: int, total_hosts: int, total_matches: int) -> None:
|
||||
conn.execute(
|
||||
"UPDATE runs SET finished_at=?, total_hosts=?, total_matches=? WHERE id=?",
|
||||
(datetime.utcnow().isoformat(), total_hosts, total_matches, run_id),
|
||||
)
|
||||
conn.commit()
|
||||
|
||||
|
||||
def insert_hit(conn: sqlite3.Connection, run_id: int, hit: dict) -> None:
|
||||
conn.execute(
|
||||
"""INSERT INTO hits
|
||||
(run_id, ip, port, country_code, target_name, tags, probe_type,
|
||||
confidence, version, nmap_service, nmap_banner, found_at)
|
||||
VALUES (?,?,?,?,?,?,?,?,?,?,?,?)""",
|
||||
(
|
||||
run_id,
|
||||
hit["ip"],
|
||||
hit["port"],
|
||||
hit.get("country_code", ""),
|
||||
hit["target_name"],
|
||||
json.dumps(hit.get("tags", [])),
|
||||
hit.get("probe_type", ""),
|
||||
hit.get("confidence", ""),
|
||||
hit.get("version", ""),
|
||||
hit.get("nmap_service", ""),
|
||||
hit.get("nmap_banner", ""),
|
||||
datetime.utcnow().isoformat(),
|
||||
),
|
||||
)
|
||||
conn.commit()
|
||||
|
||||
|
||||
def export_json(conn: sqlite3.Connection, run_id: int | None = None) -> list[dict]:
|
||||
if run_id:
|
||||
rows = conn.execute("SELECT * FROM hits WHERE run_id=?", (run_id,)).fetchall()
|
||||
else:
|
||||
rows = conn.execute("SELECT * FROM hits").fetchall()
|
||||
return [dict(r) for r in rows]
|
||||
|
||||
|
||||
def export_csv_lines(conn: sqlite3.Connection, run_id: int | None = None) -> list[str]:
|
||||
header = "ip,port,country_code,target_name,confidence,version,nmap_service,nmap_banner,found_at"
|
||||
if run_id:
|
||||
rows = conn.execute("SELECT * FROM hits WHERE run_id=?", (run_id,)).fetchall()
|
||||
else:
|
||||
rows = conn.execute("SELECT * FROM hits ORDER BY found_at").fetchall()
|
||||
lines = [header]
|
||||
for r in rows:
|
||||
lines.append(",".join([
|
||||
str(r["ip"]), str(r["port"]), r["country_code"] or "",
|
||||
r["target_name"], r["confidence"] or "", r["version"] or "",
|
||||
r["nmap_service"] or "", r["nmap_banner"] or "", r["found_at"],
|
||||
]))
|
||||
return lines
|
||||
Reference in New Issue
Block a user