mirror of
https://github.com/CyberSecurityUP/NeuroSploit.git
synced 2026-07-07 11:57:54 +02:00
v3.5.1: live findings + /finding + Ctrl+O/expand + 3-way /stop (soft validate) + report URL + structured Typst + IIS/CMS/CVE agents
REPL interactivity & findings: - Live findings registered during a run: /results shows them accumulating; /finding opens a selection menu with FULL details (PoC, command, evidence, CVSS, OWASP/CWE, remediation). Past runs too. - /expand (and Ctrl+O) dump the last full, untruncated commands. - Findings colored by severity in the feed (not all-yellow); confirmed vote = green. Stop & report: - CRITICAL: /stop no longer kills validation. New SOFT stop (pool.soft) halts launching new agents but lets in-flight + VALIDATION finish — so confirmed findings are kept. /stop now asks 3 ways: [1] validate then report, [2] report raw (no validation), [3] discard. - Report file:// URL printed on completion/stop. Report: - Typst report restructured: executive summary, a Vulnerability Summary TABLE (#, vuln, severity, CVSS, OWASP/CWE), and per-finding sections with criticality, CVSS, OWASP/CWE, description/impact, PoC, evidence, remediation. owasp passed through. Agents: +14 app-stack/CVE (IIS tilde/WebDAV/ViewState/debug/handler-bypass, CMS fingerprint + WordPress/Joomla/Drupal/default-admin, app-server consoles, exposed VCS, known-CVE & outdated-component exploitation) → 343 total. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -102,3 +102,5 @@ neurosploit-rs/runs/
|
|||||||
v34_gui.png
|
v34_gui.png
|
||||||
data/repl_runs.json
|
data/repl_runs.json
|
||||||
data/repl_history.txt
|
data/repl_history.txt
|
||||||
|
.neurosploit/
|
||||||
|
/tmp/*
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
# App-Server Console Exposure Agent
|
||||||
|
|
||||||
|
## User Prompt
|
||||||
|
You are testing **{target}** for exposed Tomcat/JBoss/Jenkins/Actuator consoles.
|
||||||
|
|
||||||
|
**Recon Context:**
|
||||||
|
{recon_json}
|
||||||
|
|
||||||
|
**METHODOLOGY:**
|
||||||
|
|
||||||
|
### 1. Discover
|
||||||
|
- Probe `/manager/html`, `/jmx-console`, `/jenkins`, `/actuator`, `/console`, `/admin`
|
||||||
|
|
||||||
|
### 2. Assess
|
||||||
|
- Test default/weak creds (in scope); check unauth-exposed management endpoints
|
||||||
|
|
||||||
|
### 3. Confirm
|
||||||
|
- Demonstrate a management action / deploy / info-leak proving exposure (→ often RCE)
|
||||||
|
|
||||||
|
### 4. Report Format
|
||||||
|
For each CONFIRMED finding:
|
||||||
|
```
|
||||||
|
FINDING:
|
||||||
|
- Title: App-Server Console Exposure at [endpoint]
|
||||||
|
- Severity: High
|
||||||
|
- CWE: CWE-1188
|
||||||
|
- Endpoint: [full URL]
|
||||||
|
- Vector: [what/where]
|
||||||
|
- Payload: [exact payload/command]
|
||||||
|
- Evidence: [raw tool output proving it]
|
||||||
|
- Impact: Remote code execution / takeover
|
||||||
|
- Remediation: Authenticate & network-restrict consoles; remove defaults
|
||||||
|
```
|
||||||
|
|
||||||
|
## System Prompt
|
||||||
|
You are a specialist in exposed Tomcat/JBoss/Jenkins/Actuator consoles. AUTHORIZED engagement. Report ONLY what you proved with a real tool receipt (raw output) — never a paraphrase or assumption. Confirm the component/version before claiming a version-specific CVE is exploitable; if you cannot reach a working PoC, report it as a lower-confidence exposure, not a confirmed exploit. No destructive/DoS actions. Credits: Joas A Santos and Red Team Leaders.
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
# ASP.NET Debug/Trace Exposure Agent
|
||||||
|
|
||||||
|
## User Prompt
|
||||||
|
You are testing **{target}** for debug/trace enabled in production ASP.NET.
|
||||||
|
|
||||||
|
**Recon Context:**
|
||||||
|
{recon_json}
|
||||||
|
|
||||||
|
**METHODOLOGY:**
|
||||||
|
|
||||||
|
### 1. Probe
|
||||||
|
- Request `trace.axd`; send `DEBUG` verb; check `<compilation debug=...>` leakage via errors
|
||||||
|
|
||||||
|
### 2. Assess
|
||||||
|
- Harvest request/session data, stack traces, app internals from trace output
|
||||||
|
|
||||||
|
### 3. Confirm
|
||||||
|
- Show sensitive runtime data exposed
|
||||||
|
|
||||||
|
### 4. Report Format
|
||||||
|
For each CONFIRMED finding:
|
||||||
|
```
|
||||||
|
FINDING:
|
||||||
|
- Title: ASP.NET Debug/Trace Exposure at [endpoint]
|
||||||
|
- Severity: Medium
|
||||||
|
- CWE: CWE-489
|
||||||
|
- Endpoint: [full URL]
|
||||||
|
- Vector: [what/where]
|
||||||
|
- Payload: [exact payload/command]
|
||||||
|
- Evidence: [raw tool output proving it]
|
||||||
|
- Impact: Information disclosure
|
||||||
|
- Remediation: Disable debug/trace; custom errors
|
||||||
|
```
|
||||||
|
|
||||||
|
## System Prompt
|
||||||
|
You are a specialist in debug/trace enabled in production ASP.NET. AUTHORIZED engagement. Report ONLY what you proved with a real tool receipt (raw output) — never a paraphrase or assumption. Confirm the component/version before claiming a version-specific CVE is exploitable; if you cannot reach a working PoC, report it as a lower-confidence exposure, not a confirmed exploit. No destructive/DoS actions. Credits: Joas A Santos and Red Team Leaders.
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
# ASP.NET ViewState Deserialization Agent
|
||||||
|
|
||||||
|
## User Prompt
|
||||||
|
You are testing **{target}** for unprotected/known-key __VIEWSTATE deserialization.
|
||||||
|
|
||||||
|
**Recon Context:**
|
||||||
|
{recon_json}
|
||||||
|
|
||||||
|
**METHODOLOGY:**
|
||||||
|
|
||||||
|
### 1. Inspect
|
||||||
|
- Capture __VIEWSTATE; check if MAC is disabled (enableViewStateMac=false) or a known/leaked machineKey is in play
|
||||||
|
|
||||||
|
### 2. Weaponize
|
||||||
|
- With a known/guessed machineKey, craft a ysoserial.net ViewState gadget
|
||||||
|
|
||||||
|
### 3. Confirm
|
||||||
|
- Prove code execution via OOB callback or command output tied to a unique marker
|
||||||
|
|
||||||
|
### 4. Report Format
|
||||||
|
For each CONFIRMED finding:
|
||||||
|
```
|
||||||
|
FINDING:
|
||||||
|
- Title: ASP.NET ViewState Deserialization at [endpoint]
|
||||||
|
- Severity: Critical
|
||||||
|
- CWE: CWE-502
|
||||||
|
- Endpoint: [full URL]
|
||||||
|
- Vector: [what/where]
|
||||||
|
- Payload: [exact payload/command]
|
||||||
|
- Evidence: [raw tool output proving it]
|
||||||
|
- Impact: Remote code execution
|
||||||
|
- Remediation: Enable ViewState MAC; rotate machineKey; patch
|
||||||
|
```
|
||||||
|
|
||||||
|
## System Prompt
|
||||||
|
You are a specialist in unprotected/known-key __VIEWSTATE deserialization. AUTHORIZED engagement. Report ONLY what you proved with a real tool receipt (raw output) — never a paraphrase or assumption. Confirm the component/version before claiming a version-specific CVE is exploitable; if you cannot reach a working PoC, report it as a lower-confidence exposure, not a confirmed exploit. No destructive/DoS actions. Credits: Joas A Santos and Red Team Leaders.
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
# CMS Admin Panel & Default Creds Agent
|
||||||
|
|
||||||
|
## User Prompt
|
||||||
|
You are testing **{target}** for exposed CMS admin with weak/default credentials.
|
||||||
|
|
||||||
|
**Recon Context:**
|
||||||
|
{recon_json}
|
||||||
|
|
||||||
|
**METHODOLOGY:**
|
||||||
|
|
||||||
|
### 1. Locate
|
||||||
|
- Find admin (`/wp-admin`, `/administrator`, `/user/login`, `/admin`)
|
||||||
|
|
||||||
|
### 2. Test (in scope)
|
||||||
|
- Try supplied/default credentials; respect lockout/ROE — no out-of-scope brute force
|
||||||
|
|
||||||
|
### 3. Confirm
|
||||||
|
- Show authenticated admin access
|
||||||
|
|
||||||
|
### 4. Report Format
|
||||||
|
For each CONFIRMED finding:
|
||||||
|
```
|
||||||
|
FINDING:
|
||||||
|
- Title: CMS Admin Panel & Default Creds at [endpoint]
|
||||||
|
- Severity: High
|
||||||
|
- CWE: CWE-1392
|
||||||
|
- Endpoint: [full URL]
|
||||||
|
- Vector: [what/where]
|
||||||
|
- Payload: [exact payload/command]
|
||||||
|
- Evidence: [raw tool output proving it]
|
||||||
|
- Impact: Full CMS compromise
|
||||||
|
- Remediation: Remove defaults; strong creds + MFA; restrict admin
|
||||||
|
```
|
||||||
|
|
||||||
|
## System Prompt
|
||||||
|
You are a specialist in exposed CMS admin with weak/default credentials. AUTHORIZED engagement. Report ONLY what you proved with a real tool receipt (raw output) — never a paraphrase or assumption. Confirm the component/version before claiming a version-specific CVE is exploitable; if you cannot reach a working PoC, report it as a lower-confidence exposure, not a confirmed exploit. No destructive/DoS actions. Credits: Joas A Santos and Red Team Leaders.
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
# CMS Fingerprint & Version Agent
|
||||||
|
|
||||||
|
## User Prompt
|
||||||
|
You are testing **{target}** for CMS identification and version disclosure.
|
||||||
|
|
||||||
|
**Recon Context:**
|
||||||
|
{recon_json}
|
||||||
|
|
||||||
|
**METHODOLOGY:**
|
||||||
|
|
||||||
|
### 1. Identify
|
||||||
|
- Detect CMS via meta generator, paths (`/wp-`, `/sites/`, `/administrator/`), headers, favicon hash
|
||||||
|
- Run whatweb/wpscan-style detection without auth
|
||||||
|
|
||||||
|
### 2. Version
|
||||||
|
- Pin exact version from readme/changelog/asset hashes
|
||||||
|
|
||||||
|
### 3. Map
|
||||||
|
- List plugins/themes/modules and their versions for CVE correlation
|
||||||
|
|
||||||
|
### 4. Report Format
|
||||||
|
For each CONFIRMED finding:
|
||||||
|
```
|
||||||
|
FINDING:
|
||||||
|
- Title: CMS Fingerprint & Version at [endpoint]
|
||||||
|
- Severity: Info
|
||||||
|
- CWE: CWE-200
|
||||||
|
- Endpoint: [full URL]
|
||||||
|
- Vector: [what/where]
|
||||||
|
- Payload: [exact payload/command]
|
||||||
|
- Evidence: [raw tool output proving it]
|
||||||
|
- Impact: Targeted exploitation surface
|
||||||
|
- Remediation: Hide version/generator; keep components updated
|
||||||
|
```
|
||||||
|
|
||||||
|
## System Prompt
|
||||||
|
You are a specialist in CMS identification and version disclosure. AUTHORIZED engagement. Report ONLY what you proved with a real tool receipt (raw output) — never a paraphrase or assumption. Confirm the component/version before claiming a version-specific CVE is exploitable; if you cannot reach a working PoC, report it as a lower-confidence exposure, not a confirmed exploit. No destructive/DoS actions. Credits: Joas A Santos and Red Team Leaders.
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
# Known-CVE Exploitation Specialist Agent
|
||||||
|
|
||||||
|
## User Prompt
|
||||||
|
You are testing **{target}** for exploiting known CVEs for the detected stack.
|
||||||
|
|
||||||
|
**Recon Context:**
|
||||||
|
{recon_json}
|
||||||
|
|
||||||
|
**METHODOLOGY:**
|
||||||
|
|
||||||
|
### 1. Identify versions
|
||||||
|
- From recon, list each component + exact version (server, framework, CMS, plugins, libs)
|
||||||
|
|
||||||
|
### 2. Map to CVEs
|
||||||
|
- Match versions to known CVEs; prioritise unauth RCE/SQLi/auth-bypass; note CVE id + CVSS
|
||||||
|
- Prefer issues with a reliable, non-destructive PoC
|
||||||
|
|
||||||
|
### 3. Reproduce safely
|
||||||
|
- Run a benign PoC (e.g. a version/echo check or OOB callback) to confirm the CVE is actually present and exploitable — never a destructive payload
|
||||||
|
|
||||||
|
### 4. Confirm
|
||||||
|
- Report the CVE only when the PoC produced concrete proof (output/OOB); otherwise report it as 'potentially vulnerable (version match, unconfirmed)'
|
||||||
|
|
||||||
|
### 5. Report Format
|
||||||
|
For each CONFIRMED finding:
|
||||||
|
```
|
||||||
|
FINDING:
|
||||||
|
- Title: Known-CVE Exploitation Specialist at [endpoint]
|
||||||
|
- Severity: Critical
|
||||||
|
- CWE: CWE-1395
|
||||||
|
- Endpoint: [full URL]
|
||||||
|
- Vector: [what/where]
|
||||||
|
- Payload: [exact payload/command]
|
||||||
|
- Evidence: [raw tool output proving it]
|
||||||
|
- Impact: Depends on CVE — up to full compromise
|
||||||
|
- Remediation: Patch/upgrade the affected components; apply vendor advisories
|
||||||
|
```
|
||||||
|
|
||||||
|
## System Prompt
|
||||||
|
You are a specialist in exploiting known CVEs for the detected stack. AUTHORIZED engagement. Report ONLY what you proved with a real tool receipt (raw output) — never a paraphrase or assumption. Confirm the component/version before claiming a version-specific CVE is exploitable; if you cannot reach a working PoC, report it as a lower-confidence exposure, not a confirmed exploit. No destructive/DoS actions. Credits: Joas A Santos and Red Team Leaders.
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
# Drupal Security Audit Agent
|
||||||
|
|
||||||
|
## User Prompt
|
||||||
|
You are testing **{target}** for Drupal core/module weaknesses (e.g. Drupalgeddon class).
|
||||||
|
|
||||||
|
**Recon Context:**
|
||||||
|
{recon_json}
|
||||||
|
|
||||||
|
**METHODOLOGY:**
|
||||||
|
|
||||||
|
### 1. Enumerate
|
||||||
|
- Version (CHANGELOG, headers), enabled modules
|
||||||
|
|
||||||
|
### 2. Correlate CVEs
|
||||||
|
- Map to known Drupal RCE/SQLi (e.g. SA-CORE highly-critical classes)
|
||||||
|
|
||||||
|
### 3. Confirm
|
||||||
|
- Reproduce with an OOB/output proof where applicable
|
||||||
|
|
||||||
|
### 4. Report Format
|
||||||
|
For each CONFIRMED finding:
|
||||||
|
```
|
||||||
|
FINDING:
|
||||||
|
- Title: Drupal Security Audit at [endpoint]
|
||||||
|
- Severity: Critical
|
||||||
|
- CWE: CWE-1395
|
||||||
|
- Endpoint: [full URL]
|
||||||
|
- Vector: [what/where]
|
||||||
|
- Payload: [exact payload/command]
|
||||||
|
- Evidence: [raw tool output proving it]
|
||||||
|
- Impact: Remote code execution
|
||||||
|
- Remediation: Patch core/modules promptly
|
||||||
|
```
|
||||||
|
|
||||||
|
## System Prompt
|
||||||
|
You are a specialist in Drupal core/module weaknesses (e.g. Drupalgeddon class). AUTHORIZED engagement. Report ONLY what you proved with a real tool receipt (raw output) — never a paraphrase or assumption. Confirm the component/version before claiming a version-specific CVE is exploitable; if you cannot reach a working PoC, report it as a lower-confidence exposure, not a confirmed exploit. No destructive/DoS actions. Credits: Joas A Santos and Red Team Leaders.
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
# Exposed VCS / Build Artifacts Agent
|
||||||
|
|
||||||
|
## User Prompt
|
||||||
|
You are testing **{target}** for exposed .git/.svn/CI artifacts on the app host.
|
||||||
|
|
||||||
|
**Recon Context:**
|
||||||
|
{recon_json}
|
||||||
|
|
||||||
|
**METHODOLOGY:**
|
||||||
|
|
||||||
|
### 1. Probe
|
||||||
|
- Request `/.git/HEAD`, `/.svn/entries`, `/.env`, build/CI artifact paths
|
||||||
|
|
||||||
|
### 2. Recover
|
||||||
|
- Dump source (git-dumper) / read secrets
|
||||||
|
|
||||||
|
### 3. Confirm
|
||||||
|
- Show recovered source or live secret
|
||||||
|
|
||||||
|
### 4. Report Format
|
||||||
|
For each CONFIRMED finding:
|
||||||
|
```
|
||||||
|
FINDING:
|
||||||
|
- Title: Exposed VCS / Build Artifacts at [endpoint]
|
||||||
|
- Severity: High
|
||||||
|
- CWE: CWE-527
|
||||||
|
- Endpoint: [full URL]
|
||||||
|
- Vector: [what/where]
|
||||||
|
- Payload: [exact payload/command]
|
||||||
|
- Evidence: [raw tool output proving it]
|
||||||
|
- Impact: Source/secret disclosure → RCE
|
||||||
|
- Remediation: Block VCS/dotfiles from web; rotate secrets
|
||||||
|
```
|
||||||
|
|
||||||
|
## System Prompt
|
||||||
|
You are a specialist in exposed .git/.svn/CI artifacts on the app host. AUTHORIZED engagement. Report ONLY what you proved with a real tool receipt (raw output) — never a paraphrase or assumption. Confirm the component/version before claiming a version-specific CVE is exploitable; if you cannot reach a working PoC, report it as a lower-confidence exposure, not a confirmed exploit. No destructive/DoS actions. Credits: Joas A Santos and Red Team Leaders.
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
# IIS Handler/Extension Bypass Agent
|
||||||
|
|
||||||
|
## User Prompt
|
||||||
|
You are testing **{target}** for auth or filter bypass via IIS handler quirks.
|
||||||
|
|
||||||
|
**Recon Context:**
|
||||||
|
{recon_json}
|
||||||
|
|
||||||
|
**METHODOLOGY:**
|
||||||
|
|
||||||
|
### 1. Probe
|
||||||
|
- Test path/extension tricks: `;.asp`, `::$DATA`, trailing dot, `%20`, case, `/admin/.`/`..%2f`
|
||||||
|
|
||||||
|
### 2. Bypass
|
||||||
|
- Reach a protected handler/endpoint via a normalization or handler-mapping quirk
|
||||||
|
|
||||||
|
### 3. Confirm
|
||||||
|
- Show access to a resource that should be blocked
|
||||||
|
|
||||||
|
### 4. Report Format
|
||||||
|
For each CONFIRMED finding:
|
||||||
|
```
|
||||||
|
FINDING:
|
||||||
|
- Title: IIS Handler/Extension Bypass at [endpoint]
|
||||||
|
- Severity: High
|
||||||
|
- CWE: CWE-288
|
||||||
|
- Endpoint: [full URL]
|
||||||
|
- Vector: [what/where]
|
||||||
|
- Payload: [exact payload/command]
|
||||||
|
- Evidence: [raw tool output proving it]
|
||||||
|
- Impact: Auth/control bypass
|
||||||
|
- Remediation: Consistent normalization; patch; tighten ACLs
|
||||||
|
```
|
||||||
|
|
||||||
|
## System Prompt
|
||||||
|
You are a specialist in auth or filter bypass via IIS handler quirks. AUTHORIZED engagement. Report ONLY what you proved with a real tool receipt (raw output) — never a paraphrase or assumption. Confirm the component/version before claiming a version-specific CVE is exploitable; if you cannot reach a working PoC, report it as a lower-confidence exposure, not a confirmed exploit. No destructive/DoS actions. Credits: Joas A Santos and Red Team Leaders.
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
# IIS Tilde (~) Short-Name Enumeration Agent
|
||||||
|
|
||||||
|
## User Prompt
|
||||||
|
You are testing **{target}** for IIS 8.3 short-name disclosure.
|
||||||
|
|
||||||
|
**Recon Context:**
|
||||||
|
{recon_json}
|
||||||
|
|
||||||
|
**METHODOLOGY:**
|
||||||
|
|
||||||
|
### 1. Detect
|
||||||
|
- Probe `GET /*~1*/.aspx` style requests; a 404-vs-error differential reveals 8.3 short names
|
||||||
|
- Confirm IIS version from Server header
|
||||||
|
|
||||||
|
### 2. Enumerate
|
||||||
|
- Brute the short names char by char to reveal hidden files/dirs
|
||||||
|
|
||||||
|
### 3. Confirm
|
||||||
|
- Show recovered short names mapping to real sensitive files
|
||||||
|
|
||||||
|
### 4. Report Format
|
||||||
|
For each CONFIRMED finding:
|
||||||
|
```
|
||||||
|
FINDING:
|
||||||
|
- Title: IIS Tilde (~) Short-Name Enumeration at [endpoint]
|
||||||
|
- Severity: Medium
|
||||||
|
- CWE: CWE-200
|
||||||
|
- Endpoint: [full URL]
|
||||||
|
- Vector: [what/where]
|
||||||
|
- Payload: [exact payload/command]
|
||||||
|
- Evidence: [raw tool output proving it]
|
||||||
|
- Impact: Discovery of hidden files/backups/configs
|
||||||
|
- Remediation: Disable 8.3 name creation; patch IIS
|
||||||
|
```
|
||||||
|
|
||||||
|
## System Prompt
|
||||||
|
You are a specialist in IIS 8.3 short-name disclosure. AUTHORIZED engagement. Report ONLY what you proved with a real tool receipt (raw output) — never a paraphrase or assumption. Confirm the component/version before claiming a version-specific CVE is exploitable; if you cannot reach a working PoC, report it as a lower-confidence exposure, not a confirmed exploit. No destructive/DoS actions. Credits: Joas A Santos and Red Team Leaders.
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
# IIS WebDAV Misconfiguration Agent
|
||||||
|
|
||||||
|
## User Prompt
|
||||||
|
You are testing **{target}** for exposed/unsafe WebDAV on IIS.
|
||||||
|
|
||||||
|
**Recon Context:**
|
||||||
|
{recon_json}
|
||||||
|
|
||||||
|
**METHODOLOGY:**
|
||||||
|
|
||||||
|
### 1. Detect
|
||||||
|
- `OPTIONS /` — look for DAV header / PUT/MOVE/COPY allowed
|
||||||
|
|
||||||
|
### 2. Test write
|
||||||
|
- Attempt PUT of a benign file; if blocked, try `.txt`→MOVE→`.asp` trick
|
||||||
|
|
||||||
|
### 3. Confirm
|
||||||
|
- Show an uploaded file is served (and if executable → RCE)
|
||||||
|
|
||||||
|
### 4. Report Format
|
||||||
|
For each CONFIRMED finding:
|
||||||
|
```
|
||||||
|
FINDING:
|
||||||
|
- Title: IIS WebDAV Misconfiguration at [endpoint]
|
||||||
|
- Severity: High
|
||||||
|
- CWE: CWE-650
|
||||||
|
- Endpoint: [full URL]
|
||||||
|
- Vector: [what/where]
|
||||||
|
- Payload: [exact payload/command]
|
||||||
|
- Evidence: [raw tool output proving it]
|
||||||
|
- Impact: Arbitrary upload, potential RCE
|
||||||
|
- Remediation: Disable WebDAV or restrict methods/authn
|
||||||
|
```
|
||||||
|
|
||||||
|
## System Prompt
|
||||||
|
You are a specialist in exposed/unsafe WebDAV on IIS. AUTHORIZED engagement. Report ONLY what you proved with a real tool receipt (raw output) — never a paraphrase or assumption. Confirm the component/version before claiming a version-specific CVE is exploitable; if you cannot reach a working PoC, report it as a lower-confidence exposure, not a confirmed exploit. No destructive/DoS actions. Credits: Joas A Santos and Red Team Leaders.
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
# Joomla Security Audit Agent
|
||||||
|
|
||||||
|
## User Prompt
|
||||||
|
You are testing **{target}** for Joomla core/extension weaknesses.
|
||||||
|
|
||||||
|
**Recon Context:**
|
||||||
|
{recon_json}
|
||||||
|
|
||||||
|
**METHODOLOGY:**
|
||||||
|
|
||||||
|
### 1. Enumerate
|
||||||
|
- Version (`administrator/manifests/files/joomla.xml`), components/extensions + versions
|
||||||
|
|
||||||
|
### 2. Correlate CVEs
|
||||||
|
- Map to known Joomla/extension CVEs (SQLi, LFI, object injection)
|
||||||
|
|
||||||
|
### 3. Confirm
|
||||||
|
- Reproduce one with proof
|
||||||
|
|
||||||
|
### 4. Report Format
|
||||||
|
For each CONFIRMED finding:
|
||||||
|
```
|
||||||
|
FINDING:
|
||||||
|
- Title: Joomla Security Audit at [endpoint]
|
||||||
|
- Severity: High
|
||||||
|
- CWE: CWE-1395
|
||||||
|
- Endpoint: [full URL]
|
||||||
|
- Vector: [what/where]
|
||||||
|
- Payload: [exact payload/command]
|
||||||
|
- Evidence: [raw tool output proving it]
|
||||||
|
- Impact: Site takeover / data breach
|
||||||
|
- Remediation: Update core/extensions; harden admin
|
||||||
|
```
|
||||||
|
|
||||||
|
## System Prompt
|
||||||
|
You are a specialist in Joomla core/extension weaknesses. AUTHORIZED engagement. Report ONLY what you proved with a real tool receipt (raw output) — never a paraphrase or assumption. Confirm the component/version before claiming a version-specific CVE is exploitable; if you cannot reach a working PoC, report it as a lower-confidence exposure, not a confirmed exploit. No destructive/DoS actions. Credits: Joas A Santos and Red Team Leaders.
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
# Outdated Component CVE Specialist Agent
|
||||||
|
|
||||||
|
## User Prompt
|
||||||
|
You are testing **{target}** for outdated front-end/back-end components with known CVEs.
|
||||||
|
|
||||||
|
**Recon Context:**
|
||||||
|
{recon_json}
|
||||||
|
|
||||||
|
**METHODOLOGY:**
|
||||||
|
|
||||||
|
### 1. Inventory
|
||||||
|
- Extract JS libs (jQuery, Angular, etc.), server modules, framework versions from responses/JS/headers
|
||||||
|
|
||||||
|
### 2. Correlate
|
||||||
|
- Map each to known CVEs; flag the exploitable, reachable ones
|
||||||
|
|
||||||
|
### 3. Confirm
|
||||||
|
- Prove exploitability where a safe PoC exists; else report as version-based exposure
|
||||||
|
|
||||||
|
### 4. Report Format
|
||||||
|
For each CONFIRMED finding:
|
||||||
|
```
|
||||||
|
FINDING:
|
||||||
|
- Title: Outdated Component CVE Specialist at [endpoint]
|
||||||
|
- Severity: High
|
||||||
|
- CWE: CWE-1104
|
||||||
|
- Endpoint: [full URL]
|
||||||
|
- Vector: [what/where]
|
||||||
|
- Payload: [exact payload/command]
|
||||||
|
- Evidence: [raw tool output proving it]
|
||||||
|
- Impact: Varies — XSS/RCE/info-leak
|
||||||
|
- Remediation: Upgrade components; dependency scanning in CI
|
||||||
|
```
|
||||||
|
|
||||||
|
## System Prompt
|
||||||
|
You are a specialist in outdated front-end/back-end components with known CVEs. AUTHORIZED engagement. Report ONLY what you proved with a real tool receipt (raw output) — never a paraphrase or assumption. Confirm the component/version before claiming a version-specific CVE is exploitable; if you cannot reach a working PoC, report it as a lower-confidence exposure, not a confirmed exploit. No destructive/DoS actions. Credits: Joas A Santos and Red Team Leaders.
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
# WordPress Security Audit Agent
|
||||||
|
|
||||||
|
## User Prompt
|
||||||
|
You are testing **{target}** for WordPress core/plugin/theme weaknesses.
|
||||||
|
|
||||||
|
**Recon Context:**
|
||||||
|
{recon_json}
|
||||||
|
|
||||||
|
**METHODOLOGY:**
|
||||||
|
|
||||||
|
### 1. Enumerate
|
||||||
|
- Users (`/?author=`, REST `/wp-json/wp/v2/users`), plugins/themes + versions, `xmlrpc.php`
|
||||||
|
|
||||||
|
### 2. Correlate CVEs
|
||||||
|
- Map plugin/theme versions to known vulns (arbitrary upload, SQLi, auth bypass, LFI)
|
||||||
|
|
||||||
|
### 3. Confirm
|
||||||
|
- Reproduce one concrete issue (e.g. unauth arbitrary file upload) with proof
|
||||||
|
|
||||||
|
### 4. Report Format
|
||||||
|
For each CONFIRMED finding:
|
||||||
|
```
|
||||||
|
FINDING:
|
||||||
|
- Title: WordPress Security Audit at [endpoint]
|
||||||
|
- Severity: High
|
||||||
|
- CWE: CWE-1395
|
||||||
|
- Endpoint: [full URL]
|
||||||
|
- Vector: [what/where]
|
||||||
|
- Payload: [exact payload/command]
|
||||||
|
- Evidence: [raw tool output proving it]
|
||||||
|
- Impact: Site takeover / RCE
|
||||||
|
- Remediation: Update core/plugins/themes; harden; disable xmlrpc
|
||||||
|
```
|
||||||
|
|
||||||
|
## System Prompt
|
||||||
|
You are a specialist in WordPress core/plugin/theme weaknesses. AUTHORIZED engagement. Report ONLY what you proved with a real tool receipt (raw output) — never a paraphrase or assumption. Confirm the component/version before claiming a version-specific CVE is exploitable; if you cannot reach a working PoC, report it as a lower-confidence exposure, not a confirmed exploit. No destructive/DoS actions. Credits: Joas A Santos and Red Team Leaders.
|
||||||
@@ -22,3 +22,10 @@ run
|
|||||||
/results
|
/results
|
||||||
/report
|
/report
|
||||||
/exit
|
/exit
|
||||||
|
/run
|
||||||
|
/status
|
||||||
|
/help
|
||||||
|
/results
|
||||||
|
/stop
|
||||||
|
/results
|
||||||
|
/exit
|
||||||
|
|||||||
@@ -19,5 +19,12 @@
|
|||||||
"target": "http://testasp.vulnweb.com/",
|
"target": "http://testasp.vulnweb.com/",
|
||||||
"workdir": "",
|
"workdir": "",
|
||||||
"findings": []
|
"findings": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 4,
|
||||||
|
"mode": "black-box",
|
||||||
|
"target": "http://testasp.vulnweb.com/",
|
||||||
|
"workdir": "/opt/NeurosploitFinal/runs/ns-1782353097-testasp_vulnweb_com",
|
||||||
|
"findings": []
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -359,6 +359,7 @@ pub(crate) struct Spawned {
|
|||||||
pub task: tokio::task::JoinHandle<RunOutput>,
|
pub task: tokio::task::JoinHandle<RunOutput>,
|
||||||
pub rx: tokio::sync::mpsc::Receiver<String>,
|
pub rx: tokio::sync::mpsc::Receiver<String>,
|
||||||
pub cancel: std::sync::Arc<std::sync::atomic::AtomicBool>,
|
pub cancel: std::sync::Arc<std::sync::atomic::AtomicBool>,
|
||||||
|
pub soft: std::sync::Arc<std::sync::atomic::AtomicBool>,
|
||||||
pub workdir: PathBuf,
|
pub workdir: PathBuf,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -408,6 +409,7 @@ pub(crate) fn spawn_engagement(base: &Path, mut cfg: RunConfig, mcp: bool, mode:
|
|||||||
let refs: Vec<ModelRef> = cfg.models.iter().map(|s| ModelRef::parse(s)).collect();
|
let refs: Vec<ModelRef> = cfg.models.iter().map(|s| ModelRef::parse(s)).collect();
|
||||||
let pool = ModelPool::with_auth(refs, cfg.concurrency, cfg.subscription, mcp_config);
|
let pool = ModelPool::with_auth(refs, cfg.concurrency, cfg.subscription, mcp_config);
|
||||||
let cancel = pool.cancel_handle();
|
let cancel = pool.cancel_handle();
|
||||||
|
let soft = pool.soft_handle();
|
||||||
let (tx, rx) = tokio::sync::mpsc::channel::<String>(256);
|
let (tx, rx) = tokio::sync::mpsc::channel::<String>(256);
|
||||||
let task = tokio::spawn(async move {
|
let task = tokio::spawn(async move {
|
||||||
match mode {
|
match mode {
|
||||||
@@ -417,7 +419,25 @@ pub(crate) fn spawn_engagement(base: &Path, mut cfg: RunConfig, mcp: bool, mode:
|
|||||||
Mode::Black => harness::run(cfg, &lib, &pool, tx).await,
|
Mode::Black => harness::run(cfg, &lib, &pool, tx).await,
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
Spawned { task, rx, cancel, workdir }
|
Spawned { task, rx, cancel, soft, workdir }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Absolute file:// URL of a run's report (PDF if present, else HTML).
|
||||||
|
pub(crate) fn report_url(workdir: &Path) -> String {
|
||||||
|
let pdf = workdir.join("report.pdf");
|
||||||
|
let f = if pdf.is_file() { pdf } else { workdir.join("report.html") };
|
||||||
|
let abs = f.canonicalize().unwrap_or(f);
|
||||||
|
format!("file://{}", abs.display())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Generate a report directly from raw (unvalidated) findings — used by the REPL
|
||||||
|
/// when the user chooses "report without validating" on /stop.
|
||||||
|
pub(crate) fn report_raw(target: &str, findings: &[harness::types::Finding], workdir: &Path) {
|
||||||
|
let mut fs = findings.to_vec();
|
||||||
|
harness::attack_graph::enrich(&mut fs);
|
||||||
|
std::fs::write(workdir.join("findings.json"), serde_json::to_string_pretty(&fs).unwrap_or_default()).ok();
|
||||||
|
let _ = harness::report::typst_report(target, &fs, workdir);
|
||||||
|
write_status(workdir, "stopped-raw", &format!("\"findings\":{}", fs.len()));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generate the report + final status for a finished run, ensuring the workdir
|
/// Generate the report + final status for a finished run, ensuring the workdir
|
||||||
@@ -433,7 +453,7 @@ pub(crate) fn finalize_run(mut out: RunOutput, workdir: &Path) -> RunOutput {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn run_mode(base: &Path, cfg: RunConfig, mcp: bool, mode: Mode) -> anyhow::Result<RunOutput> {
|
async fn run_mode(base: &Path, cfg: RunConfig, mcp: bool, mode: Mode) -> anyhow::Result<RunOutput> {
|
||||||
let Spawned { mut task, mut rx, cancel, workdir } = spawn_engagement(base, cfg, mcp, mode);
|
let Spawned { mut task, mut rx, cancel, workdir, .. } = spawn_engagement(base, cfg, mcp, mode);
|
||||||
let printer = tokio::spawn(async move {
|
let printer = tokio::spawn(async move {
|
||||||
while let Some(line) = rx.recv().await { render_line(&line); }
|
while let Some(line) = rx.recv().await { render_line(&line); }
|
||||||
});
|
});
|
||||||
@@ -465,7 +485,8 @@ async fn run_mode(base: &Path, cfg: RunConfig, mcp: bool, mode: Mode) -> anyhow:
|
|||||||
}
|
}
|
||||||
|
|
||||||
let out = finalize_run(out, &workdir);
|
let out = finalize_run(out, &workdir);
|
||||||
println!(" ✓ COMPLETE — {} validated finding(s) · status: {}/status.json", out.findings.len(), workdir.display());
|
println!(" ✓ COMPLETE — {} validated finding(s)", out.findings.len());
|
||||||
|
println!(" \x1b[36mreport: {}\x1b[0m", report_url(&workdir));
|
||||||
Ok(out)
|
Ok(out)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -560,6 +581,7 @@ pub(crate) fn render_compact(raw: &str) -> Option<String> {
|
|||||||
if let Some((label, rest)) = stripped.split_once(' ') { who = format!("[{label}] "); line = rest; }
|
if let Some((label, rest)) = stripped.split_once(' ') { who = format!("[{label}] "); line = rest; }
|
||||||
}
|
}
|
||||||
let (tag, rest) = line.split_once(": ").unwrap_or(("", line));
|
let (tag, rest) = line.split_once(": ").unwrap_or(("", line));
|
||||||
|
if tag == "finding_json" { return None; } // captured for /results & /finding, not shown
|
||||||
let s = match tag {
|
let s = match tag {
|
||||||
"exec" | "danger" => format!("\x1b[33m ⌘ {who}{}\x1b[0m", trunc1(rest, 110)),
|
"exec" | "danger" => format!("\x1b[33m ⌘ {who}{}\x1b[0m", trunc1(rest, 110)),
|
||||||
"net" => format!("\x1b[36m 🌐 {who}{}\x1b[0m", trunc1(rest, 110)),
|
"net" => format!("\x1b[36m 🌐 {who}{}\x1b[0m", trunc1(rest, 110)),
|
||||||
|
|||||||
+150
-11
@@ -13,7 +13,7 @@ use rustyline::highlight::Highlighter;
|
|||||||
use rustyline::hint::Hinter;
|
use rustyline::hint::Hinter;
|
||||||
use rustyline::history::FileHistory;
|
use rustyline::history::FileHistory;
|
||||||
use rustyline::validate::{ValidationContext, ValidationResult, Validator};
|
use rustyline::validate::{ValidationContext, ValidationResult, Validator};
|
||||||
use rustyline::{CompletionType, Config, Context, Editor, ExternalPrinter, Helper};
|
use rustyline::{Cmd, CompletionType, Config, Context, Editor, ExternalPrinter, Helper, KeyEvent};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::io::IsTerminal;
|
use std::io::IsTerminal;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
@@ -28,7 +28,9 @@ struct RunLive {
|
|||||||
mode: &'static str,
|
mode: &'static str,
|
||||||
phase: String,
|
phase: String,
|
||||||
started: Instant,
|
started: Instant,
|
||||||
findings: Vec<(String, String)>, // sev, title
|
findings: Vec<(String, String)>, // sev, title (summary)
|
||||||
|
full: Vec<Finding>, // full candidate findings (PoC, evidence) for /finding
|
||||||
|
commands: Vec<String>, // full untruncated commands for /expand & Ctrl+O
|
||||||
agents: usize,
|
agents: usize,
|
||||||
agents_done: usize,
|
agents_done: usize,
|
||||||
}
|
}
|
||||||
@@ -67,14 +69,30 @@ impl RunLive {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Full candidate finding (with PoC/evidence) for /results & /finding.
|
||||||
|
if let Some(j) = line.strip_prefix("finding_json: ") {
|
||||||
|
if let Ok(f) = serde_json::from_str::<Finding>(j) { self.full.push(f); }
|
||||||
|
}
|
||||||
|
// Full untruncated command for /expand & Ctrl+O.
|
||||||
|
let cmd_part = line.strip_prefix('@').and_then(|s| s.split_once(' ').map(|(_, r)| r)).unwrap_or(line);
|
||||||
|
if let Some(c) = cmd_part.strip_prefix("exec: ").or_else(|| cmd_part.strip_prefix("danger: ")) {
|
||||||
|
self.commands.push(c.to_string());
|
||||||
|
if self.commands.len() > 100 { self.commands.remove(0); }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// What to do when the user stops a run.
|
||||||
|
#[derive(Clone, Copy, PartialEq)]
|
||||||
|
enum StopMode { Run, Validate, Raw, Discard }
|
||||||
|
|
||||||
/// A run executing in the background of the REPL.
|
/// A run executing in the background of the REPL.
|
||||||
struct ActiveRun {
|
struct ActiveRun {
|
||||||
live: Arc<Mutex<RunLive>>,
|
live: Arc<Mutex<RunLive>>,
|
||||||
cancel: Arc<AtomicBool>,
|
cancel: Arc<AtomicBool>,
|
||||||
|
soft: Arc<AtomicBool>,
|
||||||
done: Arc<AtomicBool>,
|
done: Arc<AtomicBool>,
|
||||||
|
choice: Arc<Mutex<StopMode>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// All slash-commands, for Tab completion.
|
/// All slash-commands, for Tab completion.
|
||||||
@@ -205,6 +223,8 @@ impl Reader {
|
|||||||
.completion_type(CompletionType::List).build();
|
.completion_type(CompletionType::List).build();
|
||||||
if let Ok(mut ed) = Editor::<NsHelper, FileHistory>::with_config(cfg) {
|
if let Ok(mut ed) = Editor::<NsHelper, FileHistory>::with_config(cfg) {
|
||||||
ed.set_helper(Some(NsHelper));
|
ed.set_helper(Some(NsHelper));
|
||||||
|
// Ctrl+O pre-fills /expand to dump the last full (untruncated) commands.
|
||||||
|
ed.bind_sequence(KeyEvent::ctrl('o'), Cmd::Insert(1, "/expand".to_string()));
|
||||||
let hist = proj_dir().join("history.txt");
|
let hist = proj_dir().join("history.txt");
|
||||||
let _ = ed.load_history(&hist);
|
let _ = ed.load_history(&hist);
|
||||||
return Reader::Rl(Box::new(ed), hist);
|
return Reader::Rl(Box::new(ed), hist);
|
||||||
@@ -376,7 +396,21 @@ pub async fn repl(base: &Path) -> anyhow::Result<()> {
|
|||||||
}
|
}
|
||||||
"/stop" => {
|
"/stop" => {
|
||||||
match &active {
|
match &active {
|
||||||
Some(a) if !a.done.load(Ordering::Relaxed) => { a.cancel.store(true, Ordering::Relaxed); println!(" ⏸ stopping — finishing in-flight work; a report is generated on completion."); }
|
Some(a) if !a.done.load(Ordering::Relaxed) => {
|
||||||
|
println!(" \x1b[1mStop the run — choose:\x1b[0m");
|
||||||
|
println!(" \x1b[36m1\x1b[0m validate the findings found so far, then report \x1b[2m(recommended)\x1b[0m");
|
||||||
|
println!(" \x1b[36m2\x1b[0m report NOW without validating (raw findings)");
|
||||||
|
println!(" \x1b[36m3\x1b[0m discard (no report)");
|
||||||
|
let ans = ask_line(" choice [1/2/3]:");
|
||||||
|
match ans.trim() {
|
||||||
|
"2" => { *a.choice.lock().unwrap() = StopMode::Raw; a.cancel.store(true, Ordering::Relaxed);
|
||||||
|
println!(" ⏹ stopping — generating a RAW report from what was found…"); }
|
||||||
|
"3" => { *a.choice.lock().unwrap() = StopMode::Discard; a.cancel.store(true, Ordering::Relaxed);
|
||||||
|
println!(" 🗑 stopping — discarding this run."); }
|
||||||
|
_ => { *a.choice.lock().unwrap() = StopMode::Validate; a.soft.store(true, Ordering::Relaxed);
|
||||||
|
println!(" ⏸ stopping exploitation — validating what was found, then reporting…"); }
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => println!(" no active run."),
|
_ => println!(" no active run."),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -394,7 +428,44 @@ pub async fn repl(base: &Path) -> anyhow::Result<()> {
|
|||||||
println!(" ↻ retest set up for {} ({} prior finding(s)) — /run to launch", r.target, titles.len());
|
println!(" ↻ retest set up for {} ({} prior finding(s)) — /run to launch", r.target, titles.len());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"/results" => results(&history.lock().unwrap(), arg),
|
"/results" => {
|
||||||
|
// Live findings while a run is active (no arg), else a past run.
|
||||||
|
match &active {
|
||||||
|
Some(a) if arg.is_empty() && !a.done.load(Ordering::Relaxed) => {
|
||||||
|
let l = a.live.lock().unwrap();
|
||||||
|
println!(" ▶ live — {} possible finding(s) so far ({})", l.full.len(), l.phase);
|
||||||
|
let mut f = l.full.clone();
|
||||||
|
f.sort_by_key(|x| sev_rank(&x.severity));
|
||||||
|
for x in &f { println!(" • [{}] {} \x1b[2m({} · {})\x1b[0m", x.severity, x.title, x.agent, x.endpoint); }
|
||||||
|
if !f.is_empty() { println!(" \x1b[2m/finding — pick one to see the command & PoC\x1b[0m"); }
|
||||||
|
}
|
||||||
|
_ => results(&history.lock().unwrap(), arg),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"/finding" | "/findings" => {
|
||||||
|
// Build the finding pool: live run if active, else a past run.
|
||||||
|
let pool: Vec<Finding> = match &active {
|
||||||
|
Some(a) if arg.is_empty() && !a.done.load(Ordering::Relaxed) => a.live.lock().unwrap().full.clone(),
|
||||||
|
_ => { let h = history.lock().unwrap(); pick(&h, arg).map(|r| r.findings.clone()).unwrap_or_default() }
|
||||||
|
};
|
||||||
|
finding_detail(&pool);
|
||||||
|
}
|
||||||
|
"/expand" | "/full" => {
|
||||||
|
// Show full untruncated commands from the active run.
|
||||||
|
match &active {
|
||||||
|
Some(a) => {
|
||||||
|
let l = a.live.lock().unwrap();
|
||||||
|
let n: usize = arg.trim().parse().unwrap_or(5);
|
||||||
|
let cmds = &l.commands;
|
||||||
|
if cmds.is_empty() { println!(" no commands captured yet."); }
|
||||||
|
else {
|
||||||
|
println!(" ── last {} command(s) (full) ──", n.min(cmds.len()));
|
||||||
|
for c in cmds.iter().rev().take(n).rev() { println!(" \x1b[33m$ {c}\x1b[0m"); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => println!(" no active run — /expand shows full commands while a run streams."),
|
||||||
|
}
|
||||||
|
}
|
||||||
"/report" => open_report(&history.lock().unwrap(), arg),
|
"/report" => open_report(&history.lock().unwrap(), arg),
|
||||||
"/status" => {
|
"/status" => {
|
||||||
// Live status if a run is active, else a past run's status.json.
|
// Live status if a run is active, else a past run's status.json.
|
||||||
@@ -585,11 +656,14 @@ async fn start_background(base: &Path, s: &Session, reader: &mut Reader,
|
|||||||
|
|
||||||
let live = Arc::new(Mutex::new(RunLive {
|
let live = Arc::new(Mutex::new(RunLive {
|
||||||
target: target.clone(), mode: mode_s, phase: "starting".into(),
|
target: target.clone(), mode: mode_s, phase: "starting".into(),
|
||||||
started: Instant::now(), findings: vec![], agents: 0, agents_done: 0,
|
started: Instant::now(), findings: vec![], full: vec![], commands: vec![],
|
||||||
|
agents: 0, agents_done: 0,
|
||||||
}));
|
}));
|
||||||
let cancel = sp.cancel.clone();
|
let cancel = sp.cancel.clone();
|
||||||
|
let soft = sp.soft.clone();
|
||||||
let done = Arc::new(AtomicBool::new(false));
|
let done = Arc::new(AtomicBool::new(false));
|
||||||
let (live2, done2, hist2) = (live.clone(), done.clone(), history);
|
let choice = Arc::new(Mutex::new(StopMode::Run));
|
||||||
|
let (live2, done2, hist2, choice2) = (live.clone(), done.clone(), history, choice.clone());
|
||||||
|
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
let crate::Spawned { task, mut rx, workdir, .. } = sp;
|
let crate::Spawned { task, mut rx, workdir, .. } = sp;
|
||||||
@@ -597,20 +671,40 @@ async fn start_background(base: &Path, s: &Session, reader: &mut Reader,
|
|||||||
live2.lock().unwrap().ingest(&line);
|
live2.lock().unwrap().ingest(&line);
|
||||||
if let Some(out) = crate::render_compact(&line) { let _ = printer.print(out); }
|
if let Some(out) = crate::render_compact(&line) { let _ = printer.print(out); }
|
||||||
}
|
}
|
||||||
let out = crate::finalize_run(task.await.unwrap_or_default(), &workdir);
|
let task_out = task.await.unwrap_or_default();
|
||||||
|
let mode_choice = *choice2.lock().unwrap();
|
||||||
|
|
||||||
|
if mode_choice == StopMode::Discard {
|
||||||
|
std::fs::remove_dir_all(&workdir).ok();
|
||||||
|
let _ = printer.print(format!("\x1b[33m🗑 run discarded — {}\x1b[0m", workdir.display()));
|
||||||
|
done2.store(true, Ordering::Relaxed);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Raw → report from the unvalidated candidates we captured live.
|
||||||
|
let (findings, validated_word) = if mode_choice == StopMode::Raw {
|
||||||
|
let raw = live2.lock().unwrap().full.clone();
|
||||||
|
crate::report_raw(&target, &raw, &workdir);
|
||||||
|
(raw, "unvalidated")
|
||||||
|
} else {
|
||||||
|
let out = crate::finalize_run(task_out, &workdir);
|
||||||
|
(out.findings, "validated")
|
||||||
|
};
|
||||||
|
|
||||||
let id = {
|
let id = {
|
||||||
let mut h = hist2.lock().unwrap();
|
let mut h = hist2.lock().unwrap();
|
||||||
let id = h.len() + 1;
|
let id = h.len() + 1;
|
||||||
h.push(RunRecord { id, mode: mode_s.into(), target, workdir: out.workdir.clone(), findings: out.findings.clone() });
|
h.push(RunRecord { id, mode: mode_s.into(), target, workdir: workdir.display().to_string(), findings: findings.clone() });
|
||||||
if let Ok(j) = serde_json::to_string_pretty(&*h) { std::fs::write(proj_dir().join("runs.json"), j).ok(); }
|
if let Ok(j) = serde_json::to_string_pretty(&*h) { std::fs::write(proj_dir().join("runs.json"), j).ok(); }
|
||||||
id
|
id
|
||||||
};
|
};
|
||||||
let _ = printer.print(format!(
|
let _ = printer.print(format!(
|
||||||
"\x1b[1;32m◀ run #{id} done — {} validated finding(s)\x1b[0m · /results {id} · /report {id}",
|
"\x1b[1;32m◀ run #{id} done — {} {} finding(s)\x1b[0m · /results {id} · /finding",
|
||||||
out.findings.len()));
|
findings.len(), validated_word));
|
||||||
|
let _ = printer.print(format!("\x1b[36m report: {}\x1b[0m", crate::report_url(&workdir)));
|
||||||
done2.store(true, Ordering::Relaxed);
|
done2.store(true, Ordering::Relaxed);
|
||||||
});
|
});
|
||||||
Some(ActiveRun { live, cancel, done })
|
Some(ActiveRun { live, cancel, soft, done, choice })
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Project-local store: `<cwd>/.neurosploit/` so each project keeps its own
|
/// Project-local store: `<cwd>/.neurosploit/` so each project keeps its own
|
||||||
@@ -737,6 +831,51 @@ fn diff_runs(history: &[RunRecord]) {
|
|||||||
if a == b { println!(" (no change in finding titles)"); }
|
if a == b { println!(" (no change in finding titles)"); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn sev_rank(s: &str) -> u8 {
|
||||||
|
match s { "Critical" => 0, "High" => 1, "Medium" => 2, "Low" => 3, _ => 4 }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Read one line synchronously (for the /stop choice prompt).
|
||||||
|
fn ask_line(prompt: &str) -> String {
|
||||||
|
use std::io::Write;
|
||||||
|
print!("{prompt} ");
|
||||||
|
std::io::stdout().flush().ok();
|
||||||
|
let mut s = String::new();
|
||||||
|
std::io::stdin().read_line(&mut s).ok();
|
||||||
|
s
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Arrow-key selection menu over findings; prints EVERYTHING about the chosen one
|
||||||
|
/// (command/PoC, evidence, impact, remediation, votes, confidence).
|
||||||
|
fn finding_detail(pool: &[Finding]) {
|
||||||
|
if pool.is_empty() { println!(" no findings to inspect yet."); return; }
|
||||||
|
let mut f = pool.to_vec();
|
||||||
|
f.sort_by_key(|x| sev_rank(&x.severity));
|
||||||
|
let items: Vec<String> = f.iter().map(|x| format!("[{}] {} — {}", x.severity, x.title, x.cwe)).collect();
|
||||||
|
let idx = if std::io::stdin().is_terminal() {
|
||||||
|
match dialoguer::Select::with_theme(&ColorfulTheme::default())
|
||||||
|
.with_prompt("Select a finding (↑/↓, enter)").items(&items).default(0).interact_opt() {
|
||||||
|
Ok(Some(i)) => i, _ => return,
|
||||||
|
}
|
||||||
|
} else { 0 };
|
||||||
|
let x = &f[idx];
|
||||||
|
println!("\n ┌─ \x1b[1m{}\x1b[0m", x.title);
|
||||||
|
println!(" │ severity : {}", x.severity);
|
||||||
|
println!(" │ cwe / cvss : {} · {}", x.cwe, x.cvss);
|
||||||
|
println!(" │ agent : {}", x.agent);
|
||||||
|
println!(" │ endpoint : {}", x.endpoint);
|
||||||
|
println!(" │ votes/conf : {} · {:.2}", x.votes, x.confidence);
|
||||||
|
println!(" ├─ \x1b[33mPayload / PoC\x1b[0m");
|
||||||
|
for l in x.payload.lines() { println!(" │ {l}"); }
|
||||||
|
println!(" ├─ \x1b[36mEvidence (tool output)\x1b[0m");
|
||||||
|
for l in x.evidence.lines() { println!(" │ {l}"); }
|
||||||
|
println!(" ├─ Impact");
|
||||||
|
for l in x.impact.lines() { println!(" │ {l}"); }
|
||||||
|
println!(" ├─ Remediation");
|
||||||
|
for l in x.remediation.lines() { println!(" │ {l}"); }
|
||||||
|
println!(" └─────");
|
||||||
|
}
|
||||||
|
|
||||||
fn run_status(history: &[RunRecord], arg: &str) {
|
fn run_status(history: &[RunRecord], arg: &str) {
|
||||||
let Some(r) = pick(history, arg) else { return };
|
let Some(r) = pick(history, arg) else { return };
|
||||||
match std::fs::read_to_string(Path::new(&r.workdir).join("status.json")) {
|
match std::fs::read_to_string(Path::new(&r.workdir).join("status.json")) {
|
||||||
|
|||||||
@@ -159,7 +159,7 @@ pub async fn run(cfg: RunConfig, lib: &Library, pool: &ModelPool, tx: Sender<Str
|
|||||||
let directives = directives.clone();
|
let directives = directives.clone();
|
||||||
let txc = tx.clone();
|
let txc = tx.clone();
|
||||||
async move {
|
async move {
|
||||||
if pool.is_cancelled() {
|
if pool.stop_exploiting() {
|
||||||
return (ag.name.clone(), String::new(), vec![]);
|
return (ag.name.clone(), String::new(), vec![]);
|
||||||
}
|
}
|
||||||
if verbose {
|
if verbose {
|
||||||
@@ -184,6 +184,7 @@ pub async fn run(cfg: RunConfig, lib: &Library, pool: &ModelPool, tx: Sender<Str
|
|||||||
// Live findings feed: surface each candidate the moment it appears.
|
// Live findings feed: surface each candidate the moment it appears.
|
||||||
for c in &f {
|
for c in &f {
|
||||||
let _ = txc.send(format!("finding: [{}] {} @ {}", c.severity, c.title, c.endpoint)).await;
|
let _ = txc.send(format!("finding: [{}] {} @ {}", c.severity, c.title, c.endpoint)).await;
|
||||||
|
if let Ok(j) = serde_json::to_string(c) { let _ = txc.send(format!("finding_json: {j}")).await; }
|
||||||
}
|
}
|
||||||
(ag.name.clone(), text, f)
|
(ag.name.clone(), text, f)
|
||||||
}
|
}
|
||||||
@@ -378,7 +379,7 @@ pub async fn run_greybox(cfg: RunConfig, lib: &Library, pool: &ModelPool, tx: Se
|
|||||||
let leads = leads_ctx.clone();
|
let leads = leads_ctx.clone();
|
||||||
let txc = tx.clone();
|
let txc = tx.clone();
|
||||||
async move {
|
async move {
|
||||||
if pool.is_cancelled() {
|
if pool.stop_exploiting() {
|
||||||
return (ag.name.clone(), String::new(), vec![]);
|
return (ag.name.clone(), String::new(), vec![]);
|
||||||
}
|
}
|
||||||
if verbose {
|
if verbose {
|
||||||
@@ -931,7 +932,7 @@ pub async fn run_host(cfg: RunConfig, lib: &Library, pool: &ModelPool, tx: Sende
|
|||||||
let directives = directives.clone();
|
let directives = directives.clone();
|
||||||
let txc = tx.clone();
|
let txc = tx.clone();
|
||||||
async move {
|
async move {
|
||||||
if pool.is_cancelled() { return (ag.name.clone(), String::new(), vec![]); }
|
if pool.stop_exploiting() { return (ag.name.clone(), String::new(), vec![]); }
|
||||||
if verbose {
|
if verbose {
|
||||||
let _ = txc.send(format!(" ▶ launching agent: {} ({})", ag.name, ag.title.replace(" Agent", ""))).await;
|
let _ = txc.send(format!(" ▶ launching agent: {} ({})", ag.name, ag.title.replace(" Agent", ""))).await;
|
||||||
}
|
}
|
||||||
@@ -944,7 +945,10 @@ pub async fn run_host(cfg: RunConfig, lib: &Library, pool: &ModelPool, tx: Sende
|
|||||||
Ok((m, text)) => {
|
Ok((m, text)) => {
|
||||||
let f = extract_findings(&text, &ag.name);
|
let f = extract_findings(&text, &ag.name);
|
||||||
let _ = txc.send(format!("test {} via {} → {} candidate(s)", ag.name, m.label(), f.len())).await;
|
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; }
|
for c in &f {
|
||||||
|
let _ = txc.send(format!("finding: [{}] {} @ {}", c.severity, c.title, c.endpoint)).await;
|
||||||
|
if let Ok(j) = serde_json::to_string(c) { let _ = txc.send(format!("finding_json: {j}")).await; }
|
||||||
|
}
|
||||||
(ag.name.clone(), text, f)
|
(ag.name.clone(), text, f)
|
||||||
}
|
}
|
||||||
Err(e) => { let _ = txc.send(format!("test {} failed: {e}", ag.name)).await;
|
Err(e) => { let _ = txc.send(format!("test {} failed: {e}", ag.name)).await;
|
||||||
|
|||||||
@@ -34,9 +34,11 @@ pub struct ModelPool {
|
|||||||
/// Progress channel: when set, the subscription CLI streams structured
|
/// Progress channel: when set, the subscription CLI streams structured
|
||||||
/// activity (tools called, commands run, files read) here live.
|
/// activity (tools called, commands run, files read) here live.
|
||||||
progress: std::sync::Mutex<Option<tokio::sync::mpsc::Sender<String>>>,
|
progress: std::sync::Mutex<Option<tokio::sync::mpsc::Sender<String>>>,
|
||||||
/// Cooperative cancellation: when set, in-flight model calls short-circuit
|
/// HARD cancellation: when set, in-flight model calls short-circuit (abort).
|
||||||
/// and the pipeline stops launching new agents (graceful stop).
|
|
||||||
cancel: std::sync::Arc<std::sync::atomic::AtomicBool>,
|
cancel: std::sync::Arc<std::sync::atomic::AtomicBool>,
|
||||||
|
/// SOFT stop: stop launching new EXPLOIT agents, but let in-flight finish and
|
||||||
|
/// VALIDATION still run — so "stop and validate what was found" works.
|
||||||
|
soft: std::sync::Arc<std::sync::atomic::AtomicBool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ModelPool {
|
impl ModelPool {
|
||||||
@@ -65,6 +67,7 @@ impl ModelPool {
|
|||||||
mcp_config,
|
mcp_config,
|
||||||
progress: std::sync::Mutex::new(None),
|
progress: std::sync::Mutex::new(None),
|
||||||
cancel: Arc::new(std::sync::atomic::AtomicBool::new(false)),
|
cancel: Arc::new(std::sync::atomic::AtomicBool::new(false)),
|
||||||
|
soft: Arc::new(std::sync::atomic::AtomicBool::new(false)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,13 +83,23 @@ impl ModelPool {
|
|||||||
self.progress.lock().ok().and_then(|g| g.clone())
|
self.progress.lock().ok().and_then(|g| g.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Handle to request graceful cancellation of an in-progress engagement.
|
/// Handle to request HARD cancellation (abort all model calls).
|
||||||
pub fn cancel_handle(&self) -> Arc<std::sync::atomic::AtomicBool> {
|
pub fn cancel_handle(&self) -> Arc<std::sync::atomic::AtomicBool> {
|
||||||
self.cancel.clone()
|
self.cancel.clone()
|
||||||
}
|
}
|
||||||
|
/// Handle to request a SOFT stop (stop launching new exploit agents; keep
|
||||||
|
/// validation running).
|
||||||
|
pub fn soft_handle(&self) -> Arc<std::sync::atomic::AtomicBool> {
|
||||||
|
self.soft.clone()
|
||||||
|
}
|
||||||
pub fn is_cancelled(&self) -> bool {
|
pub fn is_cancelled(&self) -> bool {
|
||||||
self.cancel.load(std::sync::atomic::Ordering::Relaxed)
|
self.cancel.load(std::sync::atomic::Ordering::Relaxed)
|
||||||
}
|
}
|
||||||
|
/// Should the exploit phase stop launching new agents? (hard OR soft stop)
|
||||||
|
pub fn stop_exploiting(&self) -> bool {
|
||||||
|
self.cancel.load(std::sync::atomic::Ordering::Relaxed)
|
||||||
|
|| self.soft.load(std::sync::atomic::Ordering::Relaxed)
|
||||||
|
}
|
||||||
|
|
||||||
/// One completion for a model, via subscription CLI (optionally with MCP) or
|
/// One completion for a model, via subscription CLI (optionally with MCP) or
|
||||||
/// HTTP API, with a short retry/backoff. `label` (e.g. the agent name) tags
|
/// HTTP API, with a short retry/backoff. `label` (e.g. the agent name) tags
|
||||||
|
|||||||
@@ -139,9 +139,10 @@ pub fn typst_report(target: &str, findings: &[Finding], dir: &Path) -> std::io::
|
|||||||
));
|
));
|
||||||
data.push_str("#let findings = (\n");
|
data.push_str("#let findings = (\n");
|
||||||
for f in sorted_findings(findings) {
|
for f in sorted_findings(findings) {
|
||||||
|
let owasp = if f.owasp.is_empty() { f.cwe.clone() } else { f.owasp.clone() };
|
||||||
data.push_str(&format!(
|
data.push_str(&format!(
|
||||||
" (severity: {}, title: {}, agent: {}, cwe: {}, cvss: {}, endpoint: {}, payload: {}, evidence: {}, impact: {}, remediation: {}, votes: {}, confidence: {}),\n",
|
" (severity: {}, title: {}, agent: {}, cwe: {}, owasp: {}, cvss: {}, endpoint: {}, payload: {}, evidence: {}, impact: {}, remediation: {}, votes: {}, confidence: {}),\n",
|
||||||
tq(&f.severity), tq(&f.title), tq(&f.agent), tq(&f.cwe), tq(&f.cvss),
|
tq(&f.severity), tq(&f.title), tq(&f.agent), tq(&f.cwe), tq(&owasp), tq(&f.cvss),
|
||||||
tq(&f.endpoint), tq(&f.payload), tq(&f.evidence), tq(&f.impact),
|
tq(&f.endpoint), tq(&f.payload), tq(&f.evidence), tq(&f.impact),
|
||||||
tq(&f.remediation), tq(&f.votes), f.confidence,
|
tq(&f.remediation), tq(&f.votes), f.confidence,
|
||||||
));
|
));
|
||||||
|
|||||||
@@ -71,13 +71,32 @@
|
|||||||
)
|
)
|
||||||
]
|
]
|
||||||
|
|
||||||
|
#let sorted = findings.sorted(key: f => sevrank(f.severity))
|
||||||
|
|
||||||
|
// ---- Vulnerability summary table ----
|
||||||
|
#if sorted.len() > 0 [
|
||||||
|
#v(8pt)
|
||||||
|
== Vulnerability Summary
|
||||||
|
#v(4pt)
|
||||||
|
#table(
|
||||||
|
columns: (auto, 1fr, auto, auto, auto),
|
||||||
|
inset: 6pt, align: (left + horizon, left + horizon, center + horizon, center + horizon, center + horizon),
|
||||||
|
stroke: 0.5pt + rgb("#dddddd"),
|
||||||
|
table.header(
|
||||||
|
text(weight: "bold")[\#], text(weight: "bold")[Vulnerability],
|
||||||
|
text(weight: "bold")[Severity], text(weight: "bold")[CVSS], text(weight: "bold")[OWASP / CWE],
|
||||||
|
),
|
||||||
|
..sorted.enumerate().map(((i, f)) => (
|
||||||
|
str(i + 1), f.title, sevbadge(f.severity), f.cvss, f.owasp,
|
||||||
|
)).flatten()
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
||||||
#v(10pt)
|
#v(10pt)
|
||||||
#line(length: 100%, stroke: 0.5pt + gray)
|
#line(length: 100%, stroke: 0.5pt + gray)
|
||||||
|
|
||||||
// ---- Findings ----
|
// ---- Detailed findings ----
|
||||||
= Findings
|
= Findings
|
||||||
|
|
||||||
#let sorted = findings.sorted(key: f => sevrank(f.severity))
|
|
||||||
#if sorted.len() == 0 [
|
#if sorted.len() == 0 [
|
||||||
#text(fill: gray)[_Nothing to report._]
|
#text(fill: gray)[_Nothing to report._]
|
||||||
]
|
]
|
||||||
@@ -86,14 +105,20 @@
|
|||||||
stroke: (left: 3pt + sevcolor.at(f.severity, default: gray), rest: 0.5pt + rgb("#dddddd")))[
|
stroke: (left: 3pt + sevcolor.at(f.severity, default: gray), rest: 0.5pt + rgb("#dddddd")))[
|
||||||
#sevbadge(f.severity) #h(6pt) #text(12pt, weight: "bold")[#str(i + 1). #f.title]
|
#sevbadge(f.severity) #h(6pt) #text(12pt, weight: "bold")[#str(i + 1). #f.title]
|
||||||
#v(4pt)
|
#v(4pt)
|
||||||
#text(9pt, fill: gray)[
|
#table(
|
||||||
agent: #raw(f.agent) · CWE: #f.cwe · CVSS: #f.cvss · votes: #f.votes · confidence: #str(f.confidence)
|
columns: (auto, 1fr, auto, 1fr),
|
||||||
]
|
inset: 4pt, stroke: none, align: left + horizon,
|
||||||
#v(2pt) #text(9pt)[Endpoint: #raw(f.endpoint)]
|
text(8pt, fill: gray)[Criticality], text(8pt)[#f.severity],
|
||||||
#v(5pt) #strong[Payload] #linebreak() #raw(f.payload)
|
text(8pt, fill: gray)[CVSS], text(8pt)[#f.cvss],
|
||||||
|
text(8pt, fill: gray)[OWASP/CWE], text(8pt)[#f.owasp · #f.cwe],
|
||||||
|
text(8pt, fill: gray)[Confidence], text(8pt)[#f.votes votes · #str(f.confidence)],
|
||||||
|
text(8pt, fill: gray)[Location], text(8pt)[#raw(f.endpoint)],
|
||||||
|
text(8pt, fill: gray)[Agent], text(8pt)[#raw(f.agent)],
|
||||||
|
)
|
||||||
|
#v(4pt) #strong[Description / Impact] #linebreak() #text(9pt)[#f.impact]
|
||||||
|
#v(4pt) #strong[Proof of Concept] #linebreak() #raw(f.payload)
|
||||||
#v(3pt) #strong[Evidence] #linebreak() #raw(f.evidence)
|
#v(3pt) #strong[Evidence] #linebreak() #raw(f.evidence)
|
||||||
#v(3pt) #strong[Impact:] #f.impact
|
#v(3pt) #strong[Remediation] #linebreak() #text(9pt)[#f.remediation]
|
||||||
#v(2pt) #strong[Remediation:] #f.remediation
|
|
||||||
]
|
]
|
||||||
#v(8pt)
|
#v(8pt)
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -0,0 +1,143 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
NeuroSploit v3.5.1 — application-stack & CVE-hunting agents.
|
||||||
|
Adds IIS/.NET, CMS (WordPress/Drupal/Joomla/etc.), app-server and known-CVE
|
||||||
|
exploitation agents to agents_md/vulns/. 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", "vulns")
|
||||||
|
|
||||||
|
|
||||||
|
def render(a):
|
||||||
|
L = [f"# {a['title']} Agent\n", "## User Prompt",
|
||||||
|
f"You are testing **{{target}}** for {a['for']}.\n",
|
||||||
|
"**Recon Context:**\n{recon_json}\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']} at [endpoint]", f"- Severity: {a['sev']}", f"- CWE: {a['cwe']}",
|
||||||
|
"- Endpoint: [full URL]", "- Vector: [what/where]", "- Payload: [exact payload/command]",
|
||||||
|
"- 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 a specialist in {vc}. AUTHORIZED engagement. Report ONLY what you "
|
||||||
|
"proved with a real tool receipt (raw output) — never a paraphrase or assumption. "
|
||||||
|
"Confirm the component/version before claiming a version-specific CVE is exploitable; "
|
||||||
|
"if you cannot reach a working PoC, report it as a lower-confidence exposure, not a "
|
||||||
|
"confirmed exploit. No destructive/DoS actions. Credits: Joas A Santos and Red Team Leaders.")}
|
||||||
|
|
||||||
|
|
||||||
|
AGENTS = [
|
||||||
|
# ---- IIS / ASP.NET ----
|
||||||
|
A("iis_tilde_shortname", "IIS Tilde (~) Short-Name Enumeration", "IIS 8.3 short-name disclosure",
|
||||||
|
"CWE-200", "Medium",
|
||||||
|
[("Detect", ["Probe `GET /*~1*/.aspx` style requests; a 404-vs-error differential reveals 8.3 short names",
|
||||||
|
"Confirm IIS version from Server header"]),
|
||||||
|
("Enumerate", ["Brute the short names char by char to reveal hidden files/dirs"]),
|
||||||
|
("Confirm", ["Show recovered short names mapping to real sensitive files"])],
|
||||||
|
"Disable 8.3 name creation; patch IIS", "Discovery of hidden files/backups/configs"),
|
||||||
|
A("iis_webdav", "IIS WebDAV Misconfiguration", "exposed/unsafe WebDAV on IIS",
|
||||||
|
"CWE-650", "High",
|
||||||
|
[("Detect", ["`OPTIONS /` — look for DAV header / PUT/MOVE/COPY allowed"]),
|
||||||
|
("Test write", ["Attempt PUT of a benign file; if blocked, try `.txt`→MOVE→`.asp` trick"]),
|
||||||
|
("Confirm", ["Show an uploaded file is served (and if executable → RCE)"])],
|
||||||
|
"Disable WebDAV or restrict methods/authn", "Arbitrary upload, potential RCE"),
|
||||||
|
A("aspnet_viewstate", "ASP.NET ViewState Deserialization", "unprotected/known-key __VIEWSTATE deserialization",
|
||||||
|
"CWE-502", "Critical",
|
||||||
|
[("Inspect", ["Capture __VIEWSTATE; check if MAC is disabled (enableViewStateMac=false) or a known/leaked machineKey is in play"]),
|
||||||
|
("Weaponize", ["With a known/guessed machineKey, craft a ysoserial.net ViewState gadget"]),
|
||||||
|
("Confirm", ["Prove code execution via OOB callback or command output tied to a unique marker"])],
|
||||||
|
"Enable ViewState MAC; rotate machineKey; patch", "Remote code execution"),
|
||||||
|
A("aspnet_debug_trace", "ASP.NET Debug/Trace Exposure", "debug/trace enabled in production ASP.NET",
|
||||||
|
"CWE-489", "Medium",
|
||||||
|
[("Probe", ["Request `trace.axd`; send `DEBUG` verb; check `<compilation debug=...>` leakage via errors"]),
|
||||||
|
("Assess", ["Harvest request/session data, stack traces, app internals from trace output"]),
|
||||||
|
("Confirm", ["Show sensitive runtime data exposed"])],
|
||||||
|
"Disable debug/trace; custom errors", "Information disclosure"),
|
||||||
|
A("iis_handler_bypass", "IIS Handler/Extension Bypass", "auth or filter bypass via IIS handler quirks",
|
||||||
|
"CWE-288", "High",
|
||||||
|
[("Probe", ["Test path/extension tricks: `;.asp`, `::$DATA`, trailing dot, `%20`, case, `/admin/.`/`..%2f`"]),
|
||||||
|
("Bypass", ["Reach a protected handler/endpoint via a normalization or handler-mapping quirk"]),
|
||||||
|
("Confirm", ["Show access to a resource that should be blocked"])],
|
||||||
|
"Consistent normalization; patch; tighten ACLs", "Auth/control bypass"),
|
||||||
|
# ---- CMS general & specific ----
|
||||||
|
A("cms_fingerprint", "CMS Fingerprint & Version", "CMS identification and version disclosure",
|
||||||
|
"CWE-200", "Info",
|
||||||
|
[("Identify", ["Detect CMS via meta generator, paths (`/wp-`, `/sites/`, `/administrator/`), headers, favicon hash",
|
||||||
|
"Run whatweb/wpscan-style detection without auth"]),
|
||||||
|
("Version", ["Pin exact version from readme/changelog/asset hashes"]),
|
||||||
|
("Map", ["List plugins/themes/modules and their versions for CVE correlation"])],
|
||||||
|
"Hide version/generator; keep components updated", "Targeted exploitation surface"),
|
||||||
|
A("wordpress_audit", "WordPress Security Audit", "WordPress core/plugin/theme weaknesses",
|
||||||
|
"CWE-1395", "High",
|
||||||
|
[("Enumerate", ["Users (`/?author=`, REST `/wp-json/wp/v2/users`), plugins/themes + versions, `xmlrpc.php`"]),
|
||||||
|
("Correlate CVEs", ["Map plugin/theme versions to known vulns (arbitrary upload, SQLi, auth bypass, LFI)"]),
|
||||||
|
("Confirm", ["Reproduce one concrete issue (e.g. unauth arbitrary file upload) with proof"])],
|
||||||
|
"Update core/plugins/themes; harden; disable xmlrpc", "Site takeover / RCE"),
|
||||||
|
A("joomla_audit", "Joomla Security Audit", "Joomla core/extension weaknesses",
|
||||||
|
"CWE-1395", "High",
|
||||||
|
[("Enumerate", ["Version (`administrator/manifests/files/joomla.xml`), components/extensions + versions"]),
|
||||||
|
("Correlate CVEs", ["Map to known Joomla/extension CVEs (SQLi, LFI, object injection)"]),
|
||||||
|
("Confirm", ["Reproduce one with proof"])],
|
||||||
|
"Update core/extensions; harden admin", "Site takeover / data breach"),
|
||||||
|
A("drupal_audit", "Drupal Security Audit", "Drupal core/module weaknesses (e.g. Drupalgeddon class)",
|
||||||
|
"CWE-1395", "Critical",
|
||||||
|
[("Enumerate", ["Version (CHANGELOG, headers), enabled modules"]),
|
||||||
|
("Correlate CVEs", ["Map to known Drupal RCE/SQLi (e.g. SA-CORE highly-critical classes)"]),
|
||||||
|
("Confirm", ["Reproduce with an OOB/output proof where applicable"])],
|
||||||
|
"Patch core/modules promptly", "Remote code execution"),
|
||||||
|
A("cms_default_admin", "CMS Admin Panel & Default Creds", "exposed CMS admin with weak/default credentials",
|
||||||
|
"CWE-1392", "High",
|
||||||
|
[("Locate", ["Find admin (`/wp-admin`, `/administrator`, `/user/login`, `/admin`)"]),
|
||||||
|
("Test (in scope)", ["Try supplied/default credentials; respect lockout/ROE — no out-of-scope brute force"]),
|
||||||
|
("Confirm", ["Show authenticated admin access"])],
|
||||||
|
"Remove defaults; strong creds + MFA; restrict admin", "Full CMS compromise"),
|
||||||
|
# ---- app servers / panels ----
|
||||||
|
A("appserver_exposure", "App-Server Console Exposure", "exposed Tomcat/JBoss/Jenkins/Actuator consoles",
|
||||||
|
"CWE-1188", "High",
|
||||||
|
[("Discover", ["Probe `/manager/html`, `/jmx-console`, `/jenkins`, `/actuator`, `/console`, `/admin`"]),
|
||||||
|
("Assess", ["Test default/weak creds (in scope); check unauth-exposed management endpoints"]),
|
||||||
|
("Confirm", ["Demonstrate a management action / deploy / info-leak proving exposure (→ often RCE)"])],
|
||||||
|
"Authenticate & network-restrict consoles; remove defaults", "Remote code execution / takeover"),
|
||||||
|
A("git_svn_exposure_app", "Exposed VCS / Build Artifacts", "exposed .git/.svn/CI artifacts on the app host",
|
||||||
|
"CWE-527", "High",
|
||||||
|
[("Probe", ["Request `/.git/HEAD`, `/.svn/entries`, `/.env`, build/CI artifact paths"]),
|
||||||
|
("Recover", ["Dump source (git-dumper) / read secrets"]),
|
||||||
|
("Confirm", ["Show recovered source or live secret"])],
|
||||||
|
"Block VCS/dotfiles from web; rotate secrets", "Source/secret disclosure → RCE"),
|
||||||
|
# ---- CVE hunting ----
|
||||||
|
A("cve_known_exploitation", "Known-CVE Exploitation Specialist", "exploiting known CVEs for the detected stack",
|
||||||
|
"CWE-1395", "Critical",
|
||||||
|
[("Identify versions", ["From recon, list each component + exact version (server, framework, CMS, plugins, libs)"]),
|
||||||
|
("Map to CVEs", ["Match versions to known CVEs; prioritise unauth RCE/SQLi/auth-bypass; note CVE id + CVSS",
|
||||||
|
"Prefer issues with a reliable, non-destructive PoC"]),
|
||||||
|
("Reproduce safely", ["Run a benign PoC (e.g. a version/echo check or OOB callback) to confirm the CVE is actually present and exploitable — never a destructive payload"]),
|
||||||
|
("Confirm", ["Report the CVE only when the PoC produced concrete proof (output/OOB); otherwise report it as 'potentially vulnerable (version match, unconfirmed)'"])],
|
||||||
|
"Patch/upgrade the affected components; apply vendor advisories", "Depends on CVE — up to full compromise"),
|
||||||
|
A("outdated_dependency_cve", "Outdated Component CVE Specialist", "outdated front-end/back-end components with known CVEs",
|
||||||
|
"CWE-1104", "High",
|
||||||
|
[("Inventory", ["Extract JS libs (jQuery, Angular, etc.), server modules, framework versions from responses/JS/headers"]),
|
||||||
|
("Correlate", ["Map each to known CVEs; flag the exploitable, reachable ones"]),
|
||||||
|
("Confirm", ["Prove exploitability where a safe PoC exists; else report as version-based exposure"])],
|
||||||
|
"Upgrade components; dependency scanning in CI", "Varies — XSS/RCE/info-leak"),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
os.makedirs(OUT, exist_ok=True)
|
||||||
|
for a in AGENTS:
|
||||||
|
open(os.path.join(OUT, a["name"] + ".md"), "w").write(render(a))
|
||||||
|
print(f"wrote {len(AGENTS)} app-stack/CVE agents to {OUT}")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
Reference in New Issue
Block a user