diff --git a/agents_md/infra/ad_acl_privesc.md b/agents_md/infra/ad_acl_privesc.md new file mode 100644 index 0000000..f41dfe1 --- /dev/null +++ b/agents_md/infra/ad_acl_privesc.md @@ -0,0 +1,35 @@ +# AD ACL / DACL Abuse Agent + +## User Prompt +You are testing **{target}** (a host/infrastructure target) for dangerous Active Directory ACLs. + +**Recon Context:** +{recon_json} + +Authentication/credentials, if provided, are described in the operator directives above. + +**METHODOLOGY:** + +### 1. Map +- Collect with bloodhound-python/SharpHound; find GenericAll/WriteDACL/ForceChangePassword edges + +### 2. Confirm +- Demonstrate one safe, reversible control step (e.g. shadow-cred / targeted password reset in a lab) proving the path + +### 3. Report Format +For each CONFIRMED finding: +``` +FINDING: +- Title: AD ACL / DACL Abuse on [host] +- Severity: High +- CWE: CWE-269 +- Endpoint: [host/service] +- Vector: [how] +- Payload: [command/PoC] +- Evidence: [raw tool output proving it] +- Impact: Domain privilege escalation +- Remediation: Tighten ACLs; tiered admin model +``` + +## System Prompt +You are an infrastructure pentest specialist for dangerous Active Directory ACLs. AUTHORIZED engagement. Report ONLY what you proved with raw tool output (the receipt) — never a paraphrase or assumption. If you lack access/observation to confirm, say so and gather more first. Stay in scope; never run destructive or DoS actions. Credits: Joas A Santos & Red Team Leaders. diff --git a/agents_md/infra/ad_asreproasting.md b/agents_md/infra/ad_asreproasting.md new file mode 100644 index 0000000..a16f8db --- /dev/null +++ b/agents_md/infra/ad_asreproasting.md @@ -0,0 +1,35 @@ +# AD AS-REP Roasting Agent + +## User Prompt +You are testing **{target}** (a host/infrastructure target) for accounts with Kerberos pre-auth disabled. + +**Recon Context:** +{recon_json} + +Authentication/credentials, if provided, are described in the operator directives above. + +**METHODOLOGY:** + +### 1. Enumerate +- impacket GetNPUsers / `netexec ldap {target} --asreproast out.txt` for DONT_REQ_PREAUTH accounts + +### 2. Crack & confirm +- Crack the AS-REP (hashcat -m 18200); confirm a recovered password + +### 3. Report Format +For each CONFIRMED finding: +``` +FINDING: +- Title: AD AS-REP Roasting on [host] +- Severity: High +- CWE: CWE-522 +- Endpoint: [host/service] +- Vector: [how] +- Payload: [command/PoC] +- Evidence: [raw tool output proving it] +- Impact: Account compromise +- Remediation: Require Kerberos pre-auth; strong passwords +``` + +## System Prompt +You are an infrastructure pentest specialist for accounts with Kerberos pre-auth disabled. AUTHORIZED engagement. Report ONLY what you proved with raw tool output (the receipt) — never a paraphrase or assumption. If you lack access/observation to confirm, say so and gather more first. Stay in scope; never run destructive or DoS actions. Credits: Joas A Santos & Red Team Leaders. diff --git a/agents_md/infra/ad_dcsync.md b/agents_md/infra/ad_dcsync.md new file mode 100644 index 0000000..a8e1458 --- /dev/null +++ b/agents_md/infra/ad_dcsync.md @@ -0,0 +1,35 @@ +# AD DCSync Exposure Agent + +## User Prompt +You are testing **{target}** (a host/infrastructure target) for replication rights enabling DCSync. + +**Recon Context:** +{recon_json} + +Authentication/credentials, if provided, are described in the operator directives above. + +**METHODOLOGY:** + +### 1. Check rights +- Identify principals with DS-Replication-Get-Changes(-All) via BloodHound/ACL review + +### 2. Confirm +- With authorized creds, prove replication right (e.g. impacket secretsdump -just-dc-user for a single test account) + +### 3. Report Format +For each CONFIRMED finding: +``` +FINDING: +- Title: AD DCSync Exposure on [host] +- Severity: Critical +- CWE: CWE-269 +- Endpoint: [host/service] +- Vector: [how] +- Payload: [command/PoC] +- Evidence: [raw tool output proving it] +- Impact: Full domain credential compromise +- Remediation: Remove replication rights from non-DC principals +``` + +## System Prompt +You are an infrastructure pentest specialist for replication rights enabling DCSync. AUTHORIZED engagement. Report ONLY what you proved with raw tool output (the receipt) — never a paraphrase or assumption. If you lack access/observation to confirm, say so and gather more first. Stay in scope; never run destructive or DoS actions. Credits: Joas A Santos & Red Team Leaders. diff --git a/agents_md/infra/ad_default_creds.md b/agents_md/infra/ad_default_creds.md new file mode 100644 index 0000000..b8b8980 --- /dev/null +++ b/agents_md/infra/ad_default_creds.md @@ -0,0 +1,35 @@ +# AD/Host Default & Reused Credentials Agent + +## User Prompt +You are testing **{target}** (a host/infrastructure target) for default or reused credentials across the domain. + +**Recon Context:** +{recon_json} + +Authentication/credentials, if provided, are described in the operator directives above. + +**METHODOLOGY:** + +### 1. Spray (authorized, throttled) +- With supplied account list, `netexec smb {target} -u users -p pass --continue-on-success` within ROE + +### 2. Confirm +- Show a successful authentication that should not have worked (reused/default cred) + +### 3. Report Format +For each CONFIRMED finding: +``` +FINDING: +- Title: AD/Host Default & Reused Credentials on [host] +- Severity: High +- CWE: CWE-798 +- Endpoint: [host/service] +- Vector: [how] +- Payload: [command/PoC] +- Evidence: [raw tool output proving it] +- Impact: Lateral movement, domain access +- Remediation: Rotate defaults; enforce unique strong passwords; lockout +``` + +## System Prompt +You are an infrastructure pentest specialist for default or reused credentials across the domain. AUTHORIZED engagement. Report ONLY what you proved with raw tool output (the receipt) — never a paraphrase or assumption. If you lack access/observation to confirm, say so and gather more first. Stay in scope; never run destructive or DoS actions. Credits: Joas A Santos & Red Team Leaders. diff --git a/agents_md/infra/ad_kerberoasting.md b/agents_md/infra/ad_kerberoasting.md new file mode 100644 index 0000000..9627c09 --- /dev/null +++ b/agents_md/infra/ad_kerberoasting.md @@ -0,0 +1,35 @@ +# AD Kerberoasting Agent + +## User Prompt +You are testing **{target}** (a host/infrastructure target) for service accounts with crackable SPNs. + +**Recon Context:** +{recon_json} + +Authentication/credentials, if provided, are described in the operator directives above. + +**METHODOLOGY:** + +### 1. Request +- `netexec ldap {target} -u -p --kerberoasting out.txt` or impacket GetUserSPNs + +### 2. Crack & confirm +- Crack the TGS hash offline (hashcat -m 13100); confirm a recovered service-account password + +### 3. Report Format +For each CONFIRMED finding: +``` +FINDING: +- Title: AD Kerberoasting on [host] +- Severity: High +- CWE: CWE-522 +- Endpoint: [host/service] +- Vector: [how] +- Payload: [command/PoC] +- Evidence: [raw tool output proving it] +- Impact: Service-account compromise, lateral movement +- Remediation: Strong/long service-account passwords; gMSA +``` + +## System Prompt +You are an infrastructure pentest specialist for service accounts with crackable SPNs. AUTHORIZED engagement. Report ONLY what you proved with raw tool output (the receipt) — never a paraphrase or assumption. If you lack access/observation to confirm, say so and gather more first. Stay in scope; never run destructive or DoS actions. Credits: Joas A Santos & Red Team Leaders. diff --git a/agents_md/infra/infra_port_service_scan.md b/agents_md/infra/infra_port_service_scan.md new file mode 100644 index 0000000..270802d --- /dev/null +++ b/agents_md/infra/infra_port_service_scan.md @@ -0,0 +1,37 @@ +# Host Port & Service Scan Agent + +## User Prompt +You are testing **{target}** (a host/infrastructure target) for open ports and service/version discovery. + +**Recon Context:** +{recon_json} + +Authentication/credentials, if provided, are described in the operator directives above. + +**METHODOLOGY:** + +### 1. Scan +- `rustscan -a {target} -- -sV` if present, else `nmap -sV -sC -Pn {target}` +- Identify open TCP/UDP ports, service banners and versions + +### 2. Triage +- Flag risky services (SMB, RDP, SSH, WinRM, LDAP, databases) and outdated versions +- Correlate versions to known CVEs for downstream agents + +### 3. Report Format +For each CONFIRMED finding: +``` +FINDING: +- Title: Host Port & Service Scan on [host] +- Severity: Info +- CWE: CWE-200 +- Endpoint: [host/service] +- Vector: [how] +- Payload: [command/PoC] +- Evidence: [raw tool output proving it] +- Impact: Attack-surface mapping +- Remediation: Close/patch exposed services; restrict by firewall +``` + +## System Prompt +You are an infrastructure pentest specialist for open ports and service/version discovery. AUTHORIZED engagement. Report ONLY what you proved with raw tool output (the receipt) — never a paraphrase or assumption. If you lack access/observation to confirm, say so and gather more first. Stay in scope; never run destructive or DoS actions. Credits: Joas A Santos & Red Team Leaders. diff --git a/agents_md/infra/infra_smb_enum.md b/agents_md/infra/infra_smb_enum.md new file mode 100644 index 0000000..0d61b57 --- /dev/null +++ b/agents_md/infra/infra_smb_enum.md @@ -0,0 +1,36 @@ +# SMB/NetBIOS Enumeration Agent + +## User Prompt +You are testing **{target}** (a host/infrastructure target) for SMB shares, sessions and misconfigurations. + +**Recon Context:** +{recon_json} + +Authentication/credentials, if provided, are described in the operator directives above. + +**METHODOLOGY:** + +### 1. Enumerate +- `netexec smb {target}` / `crackmapexec smb {target}` for hosts, signing, null sessions +- `smbclient -L //{target}/ -N` to list shares; check anonymous read/write + +### 2. Assess +- Flag SMB signing disabled (relay risk), guest/anonymous access, writable shares + +### 3. Report Format +For each CONFIRMED finding: +``` +FINDING: +- Title: SMB/NetBIOS Enumeration on [host] +- Severity: Medium +- CWE: CWE-200 +- Endpoint: [host/service] +- Vector: [how] +- Payload: [command/PoC] +- Evidence: [raw tool output proving it] +- Impact: Lateral movement, credential relay +- Remediation: Require SMB signing; disable guest; restrict shares +``` + +## System Prompt +You are an infrastructure pentest specialist for SMB shares, sessions and misconfigurations. AUTHORIZED engagement. Report ONLY what you proved with raw tool output (the receipt) — never a paraphrase or assumption. If you lack access/observation to confirm, say so and gather more first. Stay in scope; never run destructive or DoS actions. Credits: Joas A Santos & Red Team Leaders. diff --git a/agents_md/infra/linux_cron_writable.md b/agents_md/infra/linux_cron_writable.md new file mode 100644 index 0000000..ff42941 --- /dev/null +++ b/agents_md/infra/linux_cron_writable.md @@ -0,0 +1,35 @@ +# Writable Cron / Service Abuse Agent + +## User Prompt +You are testing **{target}** (a host/infrastructure target) for world-writable cron jobs or unit files. + +**Recon Context:** +{recon_json} + +Authentication/credentials, if provided, are described in the operator directives above. + +**METHODOLOGY:** + +### 1. Find +- Inspect /etc/cron*, systemd units, and scripts they call for writable paths + +### 2. Confirm +- Plant a benign marker that the privileged job executes, proving control + +### 3. Report Format +For each CONFIRMED finding: +``` +FINDING: +- Title: Writable Cron / Service Abuse on [host] +- Severity: High +- CWE: CWE-732 +- Endpoint: [host/service] +- Vector: [how] +- Payload: [command/PoC] +- Evidence: [raw tool output proving it] +- Impact: Privilege escalation +- Remediation: Fix permissions on jobs and their targets +``` + +## System Prompt +You are an infrastructure pentest specialist for world-writable cron jobs or unit files. AUTHORIZED engagement. Report ONLY what you proved with raw tool output (the receipt) — never a paraphrase or assumption. If you lack access/observation to confirm, say so and gather more first. Stay in scope; never run destructive or DoS actions. Credits: Joas A Santos & Red Team Leaders. diff --git a/agents_md/infra/linux_priv_esc.md b/agents_md/infra/linux_priv_esc.md new file mode 100644 index 0000000..596670d --- /dev/null +++ b/agents_md/infra/linux_priv_esc.md @@ -0,0 +1,36 @@ +# Linux Privilege Escalation Agent + +## User Prompt +You are testing **{target}** (a host/infrastructure target) for local privilege-escalation paths on a Linux host. + +**Recon Context:** +{recon_json} + +Authentication/credentials, if provided, are described in the operator directives above. + +**METHODOLOGY:** + +### 1. Enumerate (authenticated via SSH) +- Run linpeas/`sudo -l`, SUID/SGID (`find / -perm -4000`), cron, capabilities, writable PATH +- Check kernel version for known local exploits + +### 2. Confirm +- Demonstrate an actual escalation to root (or a clear, reachable path) with command output + +### 3. Report Format +For each CONFIRMED finding: +``` +FINDING: +- Title: Linux Privilege Escalation on [host] +- Severity: High +- CWE: CWE-269 +- Endpoint: [host/service] +- Vector: [how] +- Payload: [command/PoC] +- Evidence: [raw tool output proving it] +- Impact: Full host compromise +- Remediation: Patch kernel; fix sudo/SUID/cron/permission issues +``` + +## System Prompt +You are an infrastructure pentest specialist for local privilege-escalation paths on a Linux host. AUTHORIZED engagement. Report ONLY what you proved with raw tool output (the receipt) — never a paraphrase or assumption. If you lack access/observation to confirm, say so and gather more first. Stay in scope; never run destructive or DoS actions. Credits: Joas A Santos & Red Team Leaders. diff --git a/agents_md/infra/linux_ssh_weak_auth.md b/agents_md/infra/linux_ssh_weak_auth.md new file mode 100644 index 0000000..1b21c51 --- /dev/null +++ b/agents_md/infra/linux_ssh_weak_auth.md @@ -0,0 +1,36 @@ +# SSH Weak Authentication Agent + +## User Prompt +You are testing **{target}** (a host/infrastructure target) for weak/guessable SSH credentials or misconfig. + +**Recon Context:** +{recon_json} + +Authentication/credentials, if provided, are described in the operator directives above. + +**METHODOLOGY:** + +### 1. Assess +- Check allowed auth methods; test provided creds with `ssh`/`sshpass` +- Only test supplied credentials — never brute force out of scope + +### 2. Confirm +- Show authenticated shell access with the credentials, capturing the session banner + +### 3. Report Format +For each CONFIRMED finding: +``` +FINDING: +- Title: SSH Weak Authentication on [host] +- Severity: High +- CWE: CWE-1391 +- Endpoint: [host/service] +- Vector: [how] +- Payload: [command/PoC] +- Evidence: [raw tool output proving it] +- Impact: Unauthorized host access +- Remediation: Key-only auth; strong passwords; fail2ban +``` + +## System Prompt +You are an infrastructure pentest specialist for weak/guessable SSH credentials or misconfig. AUTHORIZED engagement. Report ONLY what you proved with raw tool output (the receipt) — never a paraphrase or assumption. If you lack access/observation to confirm, say so and gather more first. Stay in scope; never run destructive or DoS actions. Credits: Joas A Santos & Red Team Leaders. diff --git a/agents_md/infra/linux_sudo_misconfig.md b/agents_md/infra/linux_sudo_misconfig.md new file mode 100644 index 0000000..75b468e --- /dev/null +++ b/agents_md/infra/linux_sudo_misconfig.md @@ -0,0 +1,35 @@ +# Linux Sudo Misconfiguration Agent + +## User Prompt +You are testing **{target}** (a host/infrastructure target) for exploitable sudo rules. + +**Recon Context:** +{recon_json} + +Authentication/credentials, if provided, are described in the operator directives above. + +**METHODOLOGY:** + +### 1. Enumerate +- `sudo -l`; look for NOPASSWD binaries and GTFObins-exploitable entries + +### 2. Confirm +- Escalate via a permitted binary and show `id`=root output + +### 3. Report Format +For each CONFIRMED finding: +``` +FINDING: +- Title: Linux Sudo Misconfiguration on [host] +- Severity: High +- CWE: CWE-250 +- Endpoint: [host/service] +- Vector: [how] +- Payload: [command/PoC] +- Evidence: [raw tool output proving it] +- Impact: Privilege escalation to root +- Remediation: Restrict sudo to least privilege; avoid shell-capable binaries +``` + +## System Prompt +You are an infrastructure pentest specialist for exploitable sudo rules. AUTHORIZED engagement. Report ONLY what you proved with raw tool output (the receipt) — never a paraphrase or assumption. If you lack access/observation to confirm, say so and gather more first. Stay in scope; never run destructive or DoS actions. Credits: Joas A Santos & Red Team Leaders. diff --git a/agents_md/infra/windows_priv_esc.md b/agents_md/infra/windows_priv_esc.md new file mode 100644 index 0000000..d4b5d4d --- /dev/null +++ b/agents_md/infra/windows_priv_esc.md @@ -0,0 +1,35 @@ +# Windows Privilege Escalation Agent + +## User Prompt +You are testing **{target}** (a host/infrastructure target) for local privilege escalation on a Windows host. + +**Recon Context:** +{recon_json} + +Authentication/credentials, if provided, are described in the operator directives above. + +**METHODOLOGY:** + +### 1. Enumerate (authenticated) +- Run winPEAS/`whoami /priv`; check unquoted service paths, weak service perms, AlwaysInstallElevated, token privileges (SeImpersonate) + +### 2. Confirm +- Demonstrate escalation to SYSTEM/admin with command output (e.g. via a Potato technique where applicable) + +### 3. Report Format +For each CONFIRMED finding: +``` +FINDING: +- Title: Windows Privilege Escalation on [host] +- Severity: High +- CWE: CWE-269 +- Endpoint: [host/service] +- Vector: [how] +- Payload: [command/PoC] +- Evidence: [raw tool output proving it] +- Impact: Full host compromise +- Remediation: Patch; fix service perms; remove dangerous privileges +``` + +## System Prompt +You are an infrastructure pentest specialist for local privilege escalation on a Windows host. AUTHORIZED engagement. Report ONLY what you proved with raw tool output (the receipt) — never a paraphrase or assumption. If you lack access/observation to confirm, say so and gather more first. Stay in scope; never run destructive or DoS actions. Credits: Joas A Santos & Red Team Leaders. diff --git a/agents_md/infra/windows_smb_signing.md b/agents_md/infra/windows_smb_signing.md new file mode 100644 index 0000000..0fdcd85 --- /dev/null +++ b/agents_md/infra/windows_smb_signing.md @@ -0,0 +1,35 @@ +# SMB Signing & Relay Exposure Agent + +## User Prompt +You are testing **{target}** (a host/infrastructure target) for SMB signing not required (NTLM relay risk). + +**Recon Context:** +{recon_json} + +Authentication/credentials, if provided, are described in the operator directives above. + +**METHODOLOGY:** + +### 1. Detect +- `netexec smb {target}` — note `signing:False` + +### 2. Assess +- Explain the NTLM-relay exposure; confirm a coercible auth path only if in scope + +### 3. Report Format +For each CONFIRMED finding: +``` +FINDING: +- Title: SMB Signing & Relay Exposure on [host] +- Severity: Medium +- CWE: CWE-294 +- Endpoint: [host/service] +- Vector: [how] +- Payload: [command/PoC] +- Evidence: [raw tool output proving it] +- Impact: Credential relay, lateral movement +- Remediation: Enforce SMB signing; disable NTLM where possible +``` + +## System Prompt +You are an infrastructure pentest specialist for SMB signing not required (NTLM relay risk). AUTHORIZED engagement. Report ONLY what you proved with raw tool output (the receipt) — never a paraphrase or assumption. If you lack access/observation to confirm, say so and gather more first. Stay in scope; never run destructive or DoS actions. Credits: Joas A Santos & Red Team Leaders. diff --git a/agents_md/infra/windows_winrm_access.md b/agents_md/infra/windows_winrm_access.md new file mode 100644 index 0000000..3a386c0 --- /dev/null +++ b/agents_md/infra/windows_winrm_access.md @@ -0,0 +1,35 @@ +# WinRM Authenticated Access Agent + +## User Prompt +You are testing **{target}** (a host/infrastructure target) for remote management access via WinRM. + +**Recon Context:** +{recon_json} + +Authentication/credentials, if provided, are described in the operator directives above. + +**METHODOLOGY:** + +### 1. Connect +- `evil-winrm -i {target} -u -p ` (or -H ) with supplied creds/hash + +### 2. Confirm +- Show an authenticated remote shell and the host context (`whoami`, hostname) + +### 3. Report Format +For each CONFIRMED finding: +``` +FINDING: +- Title: WinRM Authenticated Access on [host] +- Severity: Medium +- CWE: CWE-287 +- Endpoint: [host/service] +- Vector: [how] +- Payload: [command/PoC] +- Evidence: [raw tool output proving it] +- Impact: Remote host control +- Remediation: Restrict WinRM; strong creds; network segmentation +``` + +## System Prompt +You are an infrastructure pentest specialist for remote management access via WinRM. AUTHORIZED engagement. Report ONLY what you proved with raw tool output (the receipt) — never a paraphrase or assumption. If you lack access/observation to confirm, say so and gather more first. Stay in scope; never run destructive or DoS actions. Credits: Joas A Santos & Red Team Leaders. diff --git a/neurosploit-rs/app/src/main.rs b/neurosploit-rs/app/src/main.rs index f4affd7..0b3cf65 100644 --- a/neurosploit-rs/app/src/main.rs +++ b/neurosploit-rs/app/src/main.rs @@ -130,6 +130,29 @@ enum Cmd { #[arg(long)] mcp: bool, }, + /// Infra/host: scan an IP/host and run Linux/Windows/AD agents. SSH/Windows + /// credentials come from --creds (creds.yaml ssh:/windows: blocks). + Host { + /// Target host or IP. + target: String, + #[arg(long = "model")] + models: Vec, + /// Credentials YAML (ssh / windows / ad blocks). + #[arg(long)] + creds: Option, + #[arg(long)] + focus: Option, + #[arg(long, default_value_t = 0)] + max_agents: usize, + #[arg(long, default_value_t = 3)] + vote_n: usize, + #[arg(long)] + offline: bool, + #[arg(long)] + subscription: bool, + #[arg(short, long)] + verbose: bool, + }, /// Show agent library counts. Agents, /// List providers and models. @@ -178,8 +201,8 @@ async fn main() -> anyhow::Result<()> { Cmd::Agents => { let lib = agents::load(&base); println!( - "{{\"vulns\":{},\"recon\":{},\"code\":{},\"meta\":{},\"total\":{}}}", - lib.vulns.len(), lib.recon.len(), lib.code.len(), lib.meta.len(), lib.total() + "{{\"vulns\":{},\"recon\":{},\"code\":{},\"infra\":{},\"meta\":{},\"total\":{}}}", + lib.vulns.len(), lib.recon.len(), lib.code.len(), lib.infra.len(), lib.meta.len(), lib.total() ); } Cmd::Models => { @@ -251,6 +274,21 @@ async fn main() -> anyhow::Result<()> { let mode = if repo.is_some() { Mode::Grey } else { Mode::Black }; tui::run(&base, cfg, mcp, mode).await?; } + Cmd::Host { target, models, creds, focus, max_agents, vote_n, offline, subscription, verbose } => { + let mut cfg = RunConfig::new(&target); + cfg.max_agents = max_agents; + cfg.vote_n = vote_n; + cfg.offline = offline; + cfg.subscription = subscription; + cfg.verbose = verbose; + cfg.instructions = focus; + if !models.is_empty() { + cfg.models = models; + } + apply_creds(&mut cfg, creds.as_deref()).await; + let out = run_mode(&base, cfg, false, Mode::Host).await?; + print_findings(&out); + } } Ok(()) } @@ -274,6 +312,13 @@ pub(crate) async fn apply_creds(cfg: &mut RunConfig, path: Option<&str>) { if cfg.auth.is_none() { cfg.auth = c.auth_header(); } + // Host credentials (SSH / Windows-AD) → tell the agents how to authenticate + // to the host so they can run on-host enumeration / privesc / AD checks. + if let Some(hi) = c.host_instruction() { + let base = cfg.instructions.clone().unwrap_or_default(); + cfg.instructions = Some(format!("{hi}\n{base}")); + println!(" [*] host credentials loaded (SSH/Windows-AD)"); + } // No direct material but a login flow → perform it now. if cfg.auth.is_none() { if let Some(login) = &c.login { @@ -296,7 +341,7 @@ pub(crate) async fn apply_creds(cfg: &mut RunConfig, path: Option<&str>) { } #[derive(Clone, Copy, PartialEq)] -pub(crate) enum Mode { Black, White, Grey } +pub(crate) enum Mode { Black, White, Grey, Host } pub(crate) async fn run_greybox_engagement(base: &Path, cfg: RunConfig, mcp: bool) -> anyhow::Result { run_mode(base, cfg, mcp, Mode::Grey).await @@ -327,7 +372,7 @@ async fn run_mode(base: &Path, mut cfg: RunConfig, mcp: bool, mode: Mode) -> any println!(" │ repo : {}", cfg.repo.clone().unwrap_or_default()); } println!(" └─ mode : {}{}{}", - match mode { Mode::White => "white-box", Mode::Grey => "greybox", Mode::Black => "black-box" }, + match mode { Mode::White => "white-box", Mode::Grey => "greybox", Mode::Host => "host/infra", Mode::Black => "black-box" }, if cfg.subscription { " · subscription" } else { " · api" }, if mcp { " · mcp" } else { "" }); @@ -376,6 +421,7 @@ async fn run_mode(base: &Path, mut cfg: RunConfig, mcp: bool, mode: Mode) -> any let out = match mode { Mode::White => harness::run_whitebox(cfg, &lib, &pool, tx).await, Mode::Grey => harness::run_greybox(cfg, &lib, &pool, tx).await, + Mode::Host => harness::run_host(cfg, &lib, &pool, tx).await, Mode::Black => harness::run(cfg, &lib, &pool, tx).await, }; out diff --git a/neurosploit-rs/app/src/repl.rs b/neurosploit-rs/app/src/repl.rs index 60c280c..050a861 100644 --- a/neurosploit-rs/app/src/repl.rs +++ b/neurosploit-rs/app/src/repl.rs @@ -131,8 +131,6 @@ impl Default for Session { } } -const PROMPT: &str = "\x1b[35mneurosploit›\x1b[0m "; - /// Line reader: full rustyline editing (Tab-complete, history, multiline) when /// interactive, plain stdin when piped. enum Reader { @@ -157,9 +155,10 @@ impl Reader { /// Returns None to exit (EOF / Ctrl-D), Some(line) otherwise. Ctrl-C cancels /// the current line (returns an empty string) instead of exiting. - fn read(&mut self) -> Option { + /// `prompt` is the dynamic context bar + prompt to show. + fn read(&mut self, prompt: &str) -> Option { match self { - Reader::Rl(ed, hist) => match ed.readline(PROMPT) { + Reader::Rl(ed, hist) => match ed.readline(prompt) { Ok(l) => { // Join multiline input: a trailing `\` continued the line. let l = l.replace("\\\n", " ").replace('\n', " "); @@ -174,7 +173,7 @@ impl Reader { }, Reader::Plain(stdin) => { use std::io::Write; - print!("{PROMPT}"); + print!("{prompt}"); std::io::stdout().flush().ok(); let mut s = String::new(); match stdin.read_line(&mut s) { @@ -209,7 +208,7 @@ pub async fn repl(base: &Path) -> anyhow::Result<()> { show(&s); loop { - let Some(line) = reader.read() else { println!("\n bye."); break }; + let Some(line) = reader.read(&context_prompt(&s)) else { println!("\n bye."); break }; let line = line.trim(); if line.is_empty() { continue; @@ -590,5 +589,26 @@ fn parse_range(r: &str) -> Option<(usize, usize)> { } } +/// Context/status bar shown above the prompt — model · cwd · mode/target, +/// e.g. "claude-opus-4-8 · /opt/projeto · black-box▸target". +fn context_prompt(s: &Session) -> String { + let model = s.models.first().map(|m| m.split(':').next_back().unwrap_or(m)).unwrap_or("?"); + let auth = if s.subscription { "sub" } else { "api" }; + let cwd = std::env::current_dir().ok() + .map(|p| p.display().to_string()) + .unwrap_or_else(|| ".".into()); + let mode = match (&s.repo, &s.target) { + (Some(_), Some(_)) => "greybox", + (Some(_), None) => "white-box", + (None, Some(_)) => "black-box", + _ => "idle", + }; + let tgt = s.target.clone().or_else(|| s.repo.clone()).unwrap_or_default(); + let tgt = if tgt.is_empty() { String::new() } else { format!("▸{}", tgt.replace("https://", "").replace("http://", "")) }; + format!( + "\x1b[2m{model} {auth} · {cwd} · {mode}{tgt}\x1b[0m\n\x1b[35mneurosploit›\x1b[0m " + ) +} + fn onoff(b: bool) -> &'static str { if b { "on" } else { "off" } } fn trunc(s: &str, n: usize) -> String { if s.len() <= n { s.to_string() } else { format!("{}…", &s[..n.saturating_sub(1)]) } } diff --git a/neurosploit-rs/app/src/tui.rs b/neurosploit-rs/app/src/tui.rs index 8395926..97b1315 100644 --- a/neurosploit-rs/app/src/tui.rs +++ b/neurosploit-rs/app/src/tui.rs @@ -148,7 +148,7 @@ pub async fn run(base: &Path, mut cfg: RunConfig, mcp: bool, mode: Mode) -> anyh let (tx, mut rx) = tokio::sync::mpsc::channel::(512); let models = cfg.models.join(", "); - let mode_s = match mode { Mode::White => "white-box", Mode::Grey => "greybox", Mode::Black => "black-box" }; + let mode_s = match mode { Mode::White => "white-box", Mode::Grey => "greybox", Mode::Host => "host/infra", Mode::Black => "black-box" }; let target_s = cfg.target.clone(); // ---- terminal setup FIRST: on a non-TTY this errors before we spawn any @@ -162,6 +162,7 @@ pub async fn run(base: &Path, mut cfg: RunConfig, mcp: bool, mode: Mode) -> anyh match mode { Mode::White => harness::run_whitebox(cfg, &lib, &pool, tx).await, Mode::Grey => harness::run_greybox(cfg, &lib, &pool, tx).await, + Mode::Host => harness::run_host(cfg, &lib, &pool, tx).await, Mode::Black => harness::run(cfg, &lib, &pool, tx).await, } }); diff --git a/neurosploit-rs/crates/harness/src/agents.rs b/neurosploit-rs/crates/harness/src/agents.rs index 8b0f0a3..824fa4a 100644 --- a/neurosploit-rs/crates/harness/src/agents.rs +++ b/neurosploit-rs/crates/harness/src/agents.rs @@ -23,11 +23,12 @@ pub struct Library { pub meta: Vec, pub recon: Vec, pub code: Vec, + pub infra: Vec, } impl Library { pub fn total(&self) -> usize { - self.vulns.len() + self.meta.len() + self.recon.len() + self.code.len() + self.vulns.len() + self.meta.len() + self.recon.len() + self.code.len() + self.infra.len() } } @@ -39,6 +40,7 @@ pub fn load(base: &Path) -> Library { meta: load_dir(&root.join("meta"), "meta"), recon: load_dir(&root.join("recon"), "recon"), code: load_dir(&root.join("code"), "code"), + infra: load_dir(&root.join("infra"), "infra"), } } diff --git a/neurosploit-rs/crates/harness/src/creds.rs b/neurosploit-rs/crates/harness/src/creds.rs index cbe095f..789cf49 100644 --- a/neurosploit-rs/crates/harness/src/creds.rs +++ b/neurosploit-rs/crates/harness/src/creds.rs @@ -31,12 +31,34 @@ pub struct Login { pub success: String, } +/// SSH credentials for Linux host testing. +#[derive(Default, Debug, Clone)] +pub struct Ssh { + pub host: String, + pub port: String, // default 22 + pub user: String, + pub password: String, + pub key: String, // path to a private key +} + +/// Windows / Active Directory credentials. +#[derive(Default, Debug, Clone)] +pub struct Win { + pub host: String, + pub user: String, + pub password: String, + pub domain: String, + pub hash: String, // NTLM hash for pass-the-hash (LM:NT or NT) +} + #[derive(Default, Debug, Clone)] pub struct Creds { pub jwt: Option, pub header: Option, pub cookie: Option, pub login: Option, + pub ssh: Option, + pub win: Option, } impl Creds { @@ -44,8 +66,10 @@ impl Creds { let text = std::fs::read_to_string(path).ok()?; let mut c = Creds::default(); let mut login = Login { method: "POST".into(), ..Default::default() }; - let mut in_login = false; - let mut have_login = false; + let mut ssh = Ssh { port: "22".into(), ..Default::default() }; + let mut win = Win::default(); + let (mut have_login, mut have_ssh, mut have_win) = (false, false, false); + let mut block = ""; // "", "login", "ssh", "windows" for raw in text.lines() { let line = raw.split('#').next().unwrap_or(""); if line.trim().is_empty() { @@ -56,25 +80,49 @@ impl Creds { Some((k, v)) => (k.trim().to_string(), unquote(v.trim())), None => continue, }; - if k == "login" && v.is_empty() { - in_login = true; - have_login = true; + // Enter a nested block (header line with empty value). + if v.is_empty() && !indented { + block = match k.as_str() { + "login" => { have_login = true; "login" } + "ssh" => { have_ssh = true; "ssh" } + "windows" | "win" | "ad" => { have_win = true; "windows" } + _ => "", + }; continue; } - if in_login && indented { - match k.as_str() { - "url" => login.url = v, - "method" => login.method = v.to_uppercase(), - "username_field" => login.username_field = v, - "password_field" => login.password_field = v, - "username" | "user" => login.username = v, - "password" | "pass" => login.password = v, - "success" => login.success = v, + if indented { + match block { + "login" => match k.as_str() { + "url" => login.url = v, + "method" => login.method = v.to_uppercase(), + "username_field" => login.username_field = v, + "password_field" => login.password_field = v, + "username" | "user" => login.username = v, + "password" | "pass" => login.password = v, + "success" => login.success = v, + _ => {} + }, + "ssh" => match k.as_str() { + "host" | "ip" => ssh.host = v, + "port" => ssh.port = v, + "user" | "username" => ssh.user = v, + "password" | "pass" => ssh.password = v, + "key" | "keyfile" | "identity" => ssh.key = v, + _ => {} + }, + "windows" => match k.as_str() { + "host" | "ip" => win.host = v, + "user" | "username" => win.user = v, + "password" | "pass" => win.password = v, + "domain" => win.domain = v, + "hash" | "ntlm" => win.hash = v, + _ => {} + }, _ => {} } continue; } - in_login = false; + block = ""; match k.as_str() { "jwt" | "token" => c.jwt = Some(v), "header" => c.header = Some(v), @@ -82,15 +130,37 @@ impl Creds { _ => {} } } - if have_login && !login.url.is_empty() { - c.login = Some(login); - } - if c.jwt.is_none() && c.header.is_none() && c.cookie.is_none() && c.login.is_none() { + if have_login && !login.url.is_empty() { c.login = Some(login); } + if have_ssh && !ssh.host.is_empty() { c.ssh = Some(ssh); } + if have_win && !win.host.is_empty() { c.win = Some(win); } + if c.jwt.is_none() && c.header.is_none() && c.cookie.is_none() + && c.login.is_none() && c.ssh.is_none() && c.win.is_none() { return None; } Some(c) } + /// A directive describing the host credentials available to the agents, so + /// they can authenticate to Linux (SSH) / Windows (AD) hosts. + pub fn host_instruction(&self) -> Option { + let mut s = String::new(); + if let Some(h) = &self.ssh { + let auth = if !h.key.is_empty() { format!("private key {}", h.key) } else { "password (provided)".into() }; + s.push_str(&format!( + "SSH ACCESS (Linux): host {}:{} as user '{}' via {}. Use `ssh`/`sshpass` to run \ + enumeration and privilege-escalation checks on the host.\n", + h.host, h.port, h.user, auth)); + } + if let Some(w) = &self.win { + let auth = if !w.hash.is_empty() { "NTLM hash (pass-the-hash)".to_string() } else { "password".into() }; + s.push_str(&format!( + "WINDOWS/AD ACCESS: host {} domain '{}' as user '{}' via {}. Use tools like \ + crackmapexec/netexec, impacket, evil-winrm, bloodhound-python for host and AD checks.\n", + w.host, if w.domain.is_empty() { "(workgroup)" } else { &w.domain }, w.user, auth)); + } + if s.is_empty() { None } else { Some(s) } + } + /// The auth material to send with each request, as a header line. pub fn auth_header(&self) -> Option { if let Some(h) = &self.header { diff --git a/neurosploit-rs/crates/harness/src/lib.rs b/neurosploit-rs/crates/harness/src/lib.rs index b7bf928..42e0f15 100644 --- a/neurosploit-rs/crates/harness/src/lib.rs +++ b/neurosploit-rs/crates/harness/src/lib.rs @@ -24,7 +24,7 @@ pub use models::{ cli_binary_for, ensure_playwright_mcp, installed_cli_backends, mcp_supported, provider_for, providers, write_mcp_config, ChatClient, ModelRef, Provider, }; -pub use pipeline::{run_greybox, run_whitebox, RunOutput}; +pub use pipeline::{run_greybox, run_host, run_whitebox, RunOutput}; pub use pipeline::run; pub use pool::{ModelPool, Task}; pub use types::{Finding, RunConfig}; diff --git a/neurosploit-rs/crates/harness/src/pipeline.rs b/neurosploit-rs/crates/harness/src/pipeline.rs index c3d1dbb..b766e40 100644 --- a/neurosploit-rs/crates/harness/src/pipeline.rs +++ b/neurosploit-rs/crates/harness/src/pipeline.rs @@ -864,3 +864,98 @@ fn collect_repo_context(root: &Path, max_files: usize, max_bytes: usize) -> Stri } out } + +const HOST_RECON_SYS: &str = "You are an infrastructure recon specialist on an AUTHORIZED engagement against a HOST/IP. Actively scan with rustscan/nmap (and netexec/smbclient where relevant) to map open ports, services, versions and auth surfaces. Use any provided SSH/Windows credentials to enumerate from inside. Do not ask permission; proceed. Reply with a compact JSON object (host, os, ports, services, auth, ad). No prose."; + +const HOST_TOOLING: &str = "TOOLING (best on Kali): nmap/rustscan (ports), netexec/crackmapexec + smbclient (SMB/AD), ssh/sshpass + linpeas (Linux), evil-winrm + winPEAS + impacket (Windows), bloodhound-python/SharpHound (AD), hashcat (offline cracking). Use only supplied credentials; never brute force or run destructive/DoS actions.\n\n"; + +/// Infrastructure engagement: scan/enumerate an IP/host and run Linux/Windows/AD +/// agents. Mirrors the web pipeline but selects from the `infra` agent set. +pub async fn run_host(cfg: RunConfig, lib: &Library, pool: &ModelPool, tx: Sender) -> RunOutput { + pool.set_progress(tx.clone()); + let _ = tx.send(format!("HOST · target: {} · {} infra agents · models: {}", cfg.target, lib.infra.len(), + pool.candidates.iter().map(|m| m.label()).collect::>().join(", "))).await; + + let recon = if cfg.offline { + "{}".to_string() + } else { + let user = format!("{}{}Target host: {}", operator_directives(&cfg), HOST_TOOLING, cfg.target); + match pool.complete_routed(Task::Recon, "recon", HOST_RECON_SYS, &user).await { + Ok((m, t)) => { let _ = tx.send(format!("recon complete via {}", m.label())).await; t } + Err(e) => { let _ = tx.send(format!("recon failed ({e})")).await; "{}".to_string() } + } + }; + + let mut rl = cfg.rl_path.as_ref().map(|p| RlState::load(Path::new(p))).unwrap_or_default(); + let mut ranked: Vec = lib.infra.clone(); + ranked.sort_by(|a, b| rl.weight(&b.name).partial_cmp(&rl.weight(&a.name)).unwrap_or(std::cmp::Ordering::Equal)); + let cap = if cfg.max_agents > 0 { cfg.max_agents.min(ranked.len()) } else { ranked.len() }; + let focus = cfg.instructions.clone().unwrap_or_default(); + + if cfg.offline { + let selected: Vec = ranked.into_iter().take(cap).collect(); + let _ = tx.send(format!("offline: selected {} infra agent(s); no live testing", selected.len())).await; + let artifacts = persist(&cfg, &recon, "", &[]); + return RunOutput { target: cfg.target.clone(), workdir: cfg.workdir.clone().unwrap_or_default(), findings: vec![], + agents_ran: selected.iter().map(|a| a.name.clone()).collect(), candidates: 0, recon, artifacts }; + } + + let chosen = select_agents(pool, &recon, &focus, &ranked, &tx).await; + let selected: Vec = if !chosen.is_empty() { + let sel: Vec = ranked.iter().filter(|a| chosen.iter().any(|c| c == &a.name)).cloned().collect(); + if sel.is_empty() { ranked.iter().take(cap).cloned().collect() } else { sel.into_iter().take(cap).collect() } + } else { + ranked.iter().take(cap).cloned().collect() + }; + let selected: Vec = { let mut seen = std::collections::HashSet::new(); + selected.into_iter().filter(|a| seen.insert(a.name.clone())).collect() }; + let _ = tx.send(format!("selected {} infra agent(s): {}", selected.len(), + selected.iter().map(|a| a.name.clone()).collect::>().join(", "))).await; + + let target = cfg.target.clone(); + let verbose = cfg.verbose; + let directives = operator_directives(&cfg); + let recon_ctx: String = recon.chars().take(3000).collect(); + let raw: Vec<(String, String, Vec)> = stream::iter(selected.iter().cloned()) + .map(|ag| { + let target = target.clone(); + let recon = recon_ctx.clone(); + let directives = directives.clone(); + let txc = tx.clone(); + async move { + if pool.is_cancelled() { return (ag.name.clone(), String::new(), vec![]); } + if verbose { + let _ = txc.send(format!(" ▶ launching agent: {} ({})", ag.name, ag.title.replace(" Agent", ""))).await; + } + let user = format!( + "AUTHORIZED host engagement on {target}. Proceed and PROVE each issue with raw tool output.\n\n{directives}{tooling}{react}{body}\n\nReply ONLY a JSON array of confirmed findings (may be []): {{id,title,severity,cwe,endpoint,payload,evidence,impact,remediation,confidence}}.", + target = target, directives = directives, tooling = HOST_TOOLING, react = REACT_DOCTRINE, + body = ag.user.replace("{target}", &target).replace("{recon_json}", &recon), + ); + match pool.complete_routed(Task::Exploit, &ag.name, &ag.system, &user).await { + Ok((m, text)) => { + let f = extract_findings(&text, &ag.name); + let _ = txc.send(format!("test {} via {} → {} candidate(s)", ag.name, m.label(), f.len())).await; + for c in &f { let _ = txc.send(format!("finding: [{}] {} @ {}", c.severity, c.title, c.endpoint)).await; } + (ag.name.clone(), text, f) + } + Err(e) => { let _ = txc.send(format!("test {} failed: {e}", ag.name)).await; + (ag.name.clone(), format!("ERROR: {e}"), vec![]) } + } + } + }) + .buffer_unordered(cfg.concurrency) + .collect::>().await; + + let transcript = transcript_of(&raw); + let candidates = dedup_findings(raw.iter().flat_map(|(_, _, f)| f.clone()).collect()); + let _ = tx.send(format!("{} candidate finding(s) (deduped) — validating", candidates.len())).await; + let mut findings = validate(candidates, pool, VOTE_SYS, cfg.vote_n, &tx).await; + let chained = chain_round(pool, &cfg.target, &recon, &operator_directives(&cfg), &findings, &tx).await; + if !chained.is_empty() { + let extra = validate(dedup_findings(chained), pool, VOTE_SYS, cfg.vote_n, &tx).await; + findings.extend(extra); + findings = dedup_findings(findings); + } + finish(cfg, lib, recon, transcript, findings, selected, &mut rl, tx).await +} diff --git a/neurosploit-rs/creds.example.yaml b/neurosploit-rs/creds.example.yaml index 14ffd91..66c8e70 100644 --- a/neurosploit-rs/creds.example.yaml +++ b/neurosploit-rs/creds.example.yaml @@ -20,3 +20,18 @@ login: username: admin password: password success: Logout # text that appears on a successful login + +# --- infra/host credentials (used by `neurosploit host --creds creds.yaml`) --- +ssh: + host: 10.0.0.5 + port: 22 + user: ubuntu + password: s3cret # or: + key: /home/op/id_ed25519 + +windows: # also used for Active Directory + host: 10.0.0.10 + domain: CORP + user: jdoe + password: Winter2026! # or pass-the-hash: + hash: aad3b435b51404eeaad3b435b51404ee:NThashhere diff --git a/scripts/build_infra_agents.py b/scripts/build_infra_agents.py new file mode 100644 index 0000000..2f2362d --- /dev/null +++ b/scripts/build_infra_agents.py @@ -0,0 +1,117 @@ +#!/usr/bin/env python3 +""" +NeuroSploit v3.5.1 — infrastructure host agents (Linux / Windows / Active Directory). +Writes agents_md/infra/*.md. Credits: Joas A Santos & Red Team Leaders. +""" +import os +ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +OUT = os.path.join(ROOT, "agents_md", "infra") + + +def render(a): + L = [f"# {a['title']} Agent\n", "## User Prompt", + f"You are testing **{{target}}** (a host/infrastructure target) for {a['for']}.\n", + "**Recon Context:**\n{recon_json}\n", + "Authentication/credentials, if provided, are described in the operator directives above.\n", + "**METHODOLOGY:**\n"] + for i, (s, bs) in enumerate(a["steps"], 1): + L.append(f"### {i}. {s}") + L += [f"- {b}" for b in bs] + L.append("") + n = len(a["steps"]) + 1 + L += [f"### {n}. Report Format", "For each CONFIRMED finding:", "```", "FINDING:", + f"- Title: {a['title']} on [host]", f"- Severity: {a['sev']}", f"- CWE: {a['cwe']}", + "- Endpoint: [host/service]", "- Vector: [how]", "- Payload: [command/PoC]", + "- Evidence: [raw tool output proving it]", f"- Impact: {a['impact']}", + f"- Remediation: {a['fix']}", "```\n", + "## System Prompt", a["system"]] + return "\n".join(L) + "\n" + + +def A(name, title, vc, cwe, sev, steps, fix, impact): + return {"name": name, "title": title, "for": vc, "sev": sev, "cwe": cwe, "impact": impact, + "fix": fix, "steps": steps, + "system": f"You are an infrastructure pentest specialist for {vc}. AUTHORIZED engagement. " + "Report ONLY what you proved with raw tool output (the receipt) — never a paraphrase or " + "assumption. If you lack access/observation to confirm, say so and gather more first. " + "Stay in scope; never run destructive or DoS actions. Credits: Joas A Santos & Red Team Leaders."} + + +INFRA = [ + # ---- recon / network ---- + A("infra_port_service_scan", "Host Port & Service Scan", "open ports and service/version discovery", "CWE-200", "Info", + [("Scan", ["`rustscan -a {target} -- -sV` if present, else `nmap -sV -sC -Pn {target}`", + "Identify open TCP/UDP ports, service banners and versions"]), + ("Triage", ["Flag risky services (SMB, RDP, SSH, WinRM, LDAP, databases) and outdated versions", + "Correlate versions to known CVEs for downstream agents"])], + "Close/patch exposed services; restrict by firewall", "Attack-surface mapping"), + A("infra_smb_enum", "SMB/NetBIOS Enumeration", "SMB shares, sessions and misconfigurations", "CWE-200", "Medium", + [("Enumerate", ["`netexec smb {target}` / `crackmapexec smb {target}` for hosts, signing, null sessions", + "`smbclient -L //{target}/ -N` to list shares; check anonymous read/write"]), + ("Assess", ["Flag SMB signing disabled (relay risk), guest/anonymous access, writable shares"])], + "Require SMB signing; disable guest; restrict shares", "Lateral movement, credential relay"), + # ---- linux ---- + A("linux_priv_esc", "Linux Privilege Escalation", "local privilege-escalation paths on a Linux host", "CWE-269", "High", + [("Enumerate (authenticated via SSH)", ["Run linpeas/`sudo -l`, SUID/SGID (`find / -perm -4000`), cron, capabilities, writable PATH", + "Check kernel version for known local exploits"]), + ("Confirm", ["Demonstrate an actual escalation to root (or a clear, reachable path) with command output"])], + "Patch kernel; fix sudo/SUID/cron/permission issues", "Full host compromise"), + A("linux_ssh_weak_auth", "SSH Weak Authentication", "weak/guessable SSH credentials or misconfig", "CWE-1391", "High", + [("Assess", ["Check allowed auth methods; test provided creds with `ssh`/`sshpass`", + "Only test supplied credentials — never brute force out of scope"]), + ("Confirm", ["Show authenticated shell access with the credentials, capturing the session banner"])], + "Key-only auth; strong passwords; fail2ban", "Unauthorized host access"), + A("linux_sudo_misconfig", "Linux Sudo Misconfiguration", "exploitable sudo rules", "CWE-250", "High", + [("Enumerate", ["`sudo -l`; look for NOPASSWD binaries and GTFObins-exploitable entries"]), + ("Confirm", ["Escalate via a permitted binary and show `id`=root output"])], + "Restrict sudo to least privilege; avoid shell-capable binaries", "Privilege escalation to root"), + A("linux_cron_writable", "Writable Cron / Service Abuse", "world-writable cron jobs or unit files", "CWE-732", "High", + [("Find", ["Inspect /etc/cron*, systemd units, and scripts they call for writable paths"]), + ("Confirm", ["Plant a benign marker that the privileged job executes, proving control"])], + "Fix permissions on jobs and their targets", "Privilege escalation"), + # ---- windows ---- + A("windows_priv_esc", "Windows Privilege Escalation", "local privilege escalation on a Windows host", "CWE-269", "High", + [("Enumerate (authenticated)", ["Run winPEAS/`whoami /priv`; check unquoted service paths, weak service perms, AlwaysInstallElevated, token privileges (SeImpersonate)"]), + ("Confirm", ["Demonstrate escalation to SYSTEM/admin with command output (e.g. via a Potato technique where applicable)"])], + "Patch; fix service perms; remove dangerous privileges", "Full host compromise"), + A("windows_smb_signing", "SMB Signing & Relay Exposure", "SMB signing not required (NTLM relay risk)", "CWE-294", "Medium", + [("Detect", ["`netexec smb {target}` — note `signing:False`"]), + ("Assess", ["Explain the NTLM-relay exposure; confirm a coercible auth path only if in scope"])], + "Enforce SMB signing; disable NTLM where possible", "Credential relay, lateral movement"), + A("windows_winrm_access", "WinRM Authenticated Access", "remote management access via WinRM", "CWE-287", "Medium", + [("Connect", ["`evil-winrm -i {target} -u -p ` (or -H ) with supplied creds/hash"]), + ("Confirm", ["Show an authenticated remote shell and the host context (`whoami`, hostname)"])], + "Restrict WinRM; strong creds; network segmentation", "Remote host control"), + # ---- active directory ---- + A("ad_kerberoasting", "AD Kerberoasting", "service accounts with crackable SPNs", "CWE-522", "High", + [("Request", ["`netexec ldap {target} -u -p --kerberoasting out.txt` or impacket GetUserSPNs"]), + ("Crack & confirm", ["Crack the TGS hash offline (hashcat -m 13100); confirm a recovered service-account password"])], + "Strong/long service-account passwords; gMSA", "Service-account compromise, lateral movement"), + A("ad_asreproasting", "AD AS-REP Roasting", "accounts with Kerberos pre-auth disabled", "CWE-522", "High", + [("Enumerate", ["impacket GetNPUsers / `netexec ldap {target} --asreproast out.txt` for DONT_REQ_PREAUTH accounts"]), + ("Crack & confirm", ["Crack the AS-REP (hashcat -m 18200); confirm a recovered password"])], + "Require Kerberos pre-auth; strong passwords", "Account compromise"), + A("ad_acl_privesc", "AD ACL / DACL Abuse", "dangerous Active Directory ACLs", "CWE-269", "High", + [("Map", ["Collect with bloodhound-python/SharpHound; find GenericAll/WriteDACL/ForceChangePassword edges"]), + ("Confirm", ["Demonstrate one safe, reversible control step (e.g. shadow-cred / targeted password reset in a lab) proving the path"])], + "Tighten ACLs; tiered admin model", "Domain privilege escalation"), + A("ad_dcsync", "AD DCSync Exposure", "replication rights enabling DCSync", "CWE-269", "Critical", + [("Check rights", ["Identify principals with DS-Replication-Get-Changes(-All) via BloodHound/ACL review"]), + ("Confirm", ["With authorized creds, prove replication right (e.g. impacket secretsdump -just-dc-user for a single test account)"])], + "Remove replication rights from non-DC principals", "Full domain credential compromise"), + A("ad_default_creds", "AD/Host Default & Reused Credentials", "default or reused credentials across the domain", "CWE-798", "High", + [("Spray (authorized, throttled)", ["With supplied account list, `netexec smb {target} -u users -p pass --continue-on-success` within ROE"]), + ("Confirm", ["Show a successful authentication that should not have worked (reused/default cred)"])], + "Rotate defaults; enforce unique strong passwords; lockout", "Lateral movement, domain access"), +] + + +def main(): + os.makedirs(OUT, exist_ok=True) + for a in INFRA: + open(os.path.join(OUT, a["name"] + ".md"), "w").write(render(a)) + print(f"wrote {len(INFRA)} infra agents to {OUT}") + + +if __name__ == "__main__": + main()