mirror of
https://github.com/CyberSecurityUP/NeuroSploit.git
synced 2026-07-08 12:28:29 +02:00
v3.5.0: orchestration chaining + rich REPL (rustyline, model arrow-select, persistent history) + model-aware /key
Harness: - Exploit-chaining round: after validation, chain confirmed findings into deeper impact (SSRF→metadata, SQLi→dump→reuse, IDOR→ATO, file-read→secrets→RCE), validate the new findings, merge. Wired into black-box and greybox. - Latest top models surfaced: claude-opus-4-8, gpt-5.1/gpt-5.1-codex, gemini-3-pro. REPL: - Real line editing via rustyline: ↑/↓ command-history recall, Ctrl-A/E/K, paste; Ctrl-C cancels the line, Ctrl-D exits. Command history persists to data/repl_history.txt. Graceful plain-stdin fallback when not a TTY. - /model with no arg → arrow-key multi-select (dialoguer); with arg accepts any provider:model names. - /key is model-aware: lists the providers your selected models need (set/missing) and prompts for the missing keys; /key <prov> <key> still works. - Run history persists to data/repl_runs.json and reloads across sessions (/runs lists past + current; /results /report /status by run number). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -24,12 +24,12 @@ pub fn providers() -> Vec<Provider> {
|
||||
vec![
|
||||
Provider { key: "anthropic", label: "Anthropic Claude", base_url: "https://api.anthropic.com/v1", env_key: "ANTHROPIC_API_KEY", kind: "cli",
|
||||
models: vec!["claude-opus-4-8", "claude-sonnet-4-6", "claude-haiku-4-5"] },
|
||||
Provider { key: "openai", label: "OpenAI", base_url: "https://api.openai.com/v1", env_key: "OPENAI_API_KEY", kind: "cli",
|
||||
models: vec!["gpt-5.1", "o4"] },
|
||||
Provider { key: "openai", label: "OpenAI (ChatGPT)", base_url: "https://api.openai.com/v1", env_key: "OPENAI_API_KEY", kind: "cli",
|
||||
models: vec!["gpt-5.1", "gpt-5.1-codex", "o4"] },
|
||||
Provider { key: "xai", label: "xAI Grok", base_url: "https://api.x.ai/v1", env_key: "XAI_API_KEY", kind: "cli",
|
||||
models: vec!["grok-4", "grok-4-fast"] },
|
||||
Provider { key: "gemini", label: "Google Gemini", base_url: "https://generativelanguage.googleapis.com/v1beta/openai", env_key: "GEMINI_API_KEY", kind: "cli",
|
||||
models: vec!["gemini-2.5-pro", "gemini-2.5-flash", "gemini-2.0-flash"] },
|
||||
models: vec!["gemini-3-pro", "gemini-2.5-pro", "gemini-2.5-flash"] },
|
||||
Provider { key: "nvidia_nim", label: "NVIDIA NIM", base_url: "https://integrate.api.nvidia.com/v1", env_key: "NVIDIA_NIM_API_KEY", kind: "api",
|
||||
models: vec!["nvidia/llama-3.3-nemotron-super-49b-v1", "deepseek-ai/deepseek-r1", "qwen/qwen2.5-coder-32b-instruct"] },
|
||||
Provider { key: "deepseek", label: "DeepSeek", base_url: "https://api.deepseek.com/v1", env_key: "DEEPSEEK_API_KEY", kind: "api",
|
||||
|
||||
@@ -195,7 +195,16 @@ pub async fn run(cfg: RunConfig, lib: &Library, pool: &ModelPool, tx: Sender<Str
|
||||
let _ = tx.send(format!("{} candidate finding(s) (deduped) — validating by {}-model vote", candidates.len(), cfg.vote_n)).await;
|
||||
|
||||
// ---- 4. Validate by N-model voting ---------------------------------
|
||||
let findings = validate(candidates, pool, VOTE_SYS, cfg.vote_n, &tx).await;
|
||||
let mut findings = validate(candidates, pool, VOTE_SYS, cfg.vote_n, &tx).await;
|
||||
|
||||
// ---- 5. Chain confirmed findings into deeper impact ----------------
|
||||
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;
|
||||
let _ = tx.send(format!("chaining added {} validated finding(s)", extra.len())).await;
|
||||
findings.extend(extra);
|
||||
findings = dedup_findings(findings);
|
||||
}
|
||||
finish(cfg, lib, recon, transcript, findings, selected, &mut rl, tx).await
|
||||
}
|
||||
|
||||
@@ -386,10 +395,49 @@ pub async fn run_greybox(cfg: RunConfig, lib: &Library, pool: &ModelPool, tx: Se
|
||||
let transcript = format!("{}\n{}", code_leads, 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 findings = validate(candidates, pool, VOTE_SYS, cfg.vote_n, &tx).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;
|
||||
let _ = tx.send(format!("chaining added {} validated finding(s)", extra.len())).await;
|
||||
findings.extend(extra);
|
||||
findings = dedup_findings(findings);
|
||||
}
|
||||
finish(cfg, lib, recon, transcript, findings, selected, &mut rl, tx).await
|
||||
}
|
||||
|
||||
const CHAIN_SYS: &str = "You are an exploit-chaining specialist. Given already-CONFIRMED findings, chain them into deeper impact — e.g. SSRF→cloud metadata creds, SQLi→DB dump→credential reuse, IDOR→account takeover, arbitrary file read→secrets→RCE, auth bypass→admin. Use your tools to actually carry the chain forward and PROVE the escalated impact. Report ONLY NEW findings beyond the inputs.";
|
||||
|
||||
/// One orchestration round: take the confirmed findings and try to chain them
|
||||
/// into higher-impact follow-ups, reusing the recon/auth context. Returns the
|
||||
/// (unvalidated) new candidate findings produced by chaining.
|
||||
async fn chain_round(pool: &ModelPool, target: &str, recon: &str, directives: &str,
|
||||
confirmed: &[Finding], tx: &Sender<String>) -> Vec<Finding> {
|
||||
if confirmed.is_empty() {
|
||||
return vec![];
|
||||
}
|
||||
let summary: String = confirmed.iter().take(20)
|
||||
.map(|f| format!("- [{}] {} @ {} ({})", f.severity, f.title, f.endpoint, f.cwe))
|
||||
.collect::<Vec<_>>().join("\n");
|
||||
let _ = tx.send(format!("chaining {} confirmed finding(s) for deeper impact…", confirmed.len())).await;
|
||||
let recon_ctx: String = recon.chars().take(2500).collect();
|
||||
let user = format!(
|
||||
"AUTHORIZED engagement on {target}.\n\n{directives}{react}{doctrine}\
|
||||
CONFIRMED FINDINGS TO CHAIN:\n{summary}\n\nRecon:\n{recon_ctx}\n\n\
|
||||
Chain these into deeper impact and PROVE it. Reply ONLY a JSON array of NEW findings \
|
||||
(may be []): {{id,title,severity,cwe,endpoint,payload,evidence,impact,remediation,confidence}}.",
|
||||
react = REACT_DOCTRINE, doctrine = tool_doctrine(pool.mcp_config.is_some()),
|
||||
);
|
||||
match pool.complete_routed(Task::Exploit, CHAIN_SYS, &user).await {
|
||||
Ok((m, text)) => {
|
||||
let f = extract_findings(&text, "chain");
|
||||
let _ = tx.send(format!("chain via {} → {} new candidate(s)", m.label(), f.len())).await;
|
||||
f
|
||||
}
|
||||
Err(e) => { let _ = tx.send(format!("chaining failed: {e}")).await; vec![] }
|
||||
}
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------- shared
|
||||
|
||||
const SELECT_SYS: &str = "You are a penetration-test orchestrator. Given recon of a target and a catalog of specialist agents, choose ONLY the agents whose preconditions clearly match the target's attack surface. Be selective. Reply with a JSON array of agent names (strings) drawn exactly from the catalog. No prose.";
|
||||
|
||||
Reference in New Issue
Block a user