mirror of
https://github.com/CyberSecurityUP/NeuroSploit.git
synced 2026-10-13 19:20:46 +02:00
feat: graph memory in the test loop, wire-bound evidence, authz matrix, report redaction
Harness - graph_memory: typed attack graph (web + internal network) with Neo4j sync over the HTTP API. Graph checkpoints run after recon, validation, each chaining round and at the end. They direct agent selection and feed the live chains, next moves and dependency todolist into every prompt. - wire: every reported exchange is checked against recorded flows and re-sent through the scope-guarded replay engine. A contradiction means the finding cannot stand as confirmed. - traces: each agent's transcript is shared with the others as a fenced, structured digest (endpoints, params, error signatures, versions). - authz_matrix: role x route authorization matrix validator, with identity tiers in creds. - deliverables: evidence custody, exploit-as-test (honest oracles only), detection content and personas, with no credential leakage. - redact: credentials quoted by agents become $SECRET_n in every human-facing report (md/html/json/sarif/typ, findings.md). findings.json and the vault keep raw values for re-testing. - compliance: GDPR replaces LGPD. - New agents: authorization_matrix, session_lifecycle, api_token_structure, exposed_datastore, postgres_superuser_rce, meta/graph_reasoning. Web console - New tabs, mounted in both the detail and live views: Attack graph (replaces Generative Attack Path Chaining), Chains, Dependencies, Traffic, Traces, Deliverables. - Wire/Status columns, KPIs, CSV export. - Graph recompute / Neo4j push / Re-test actions, all through the CLI. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015v24fuCWxkanS31HDmaDB6
This commit is contained in:
1 parent
f279817cfe
commit
79685cee49
29 files changed
+8052
-483
No files matched your search
@@ -0,0 +1,152 @@
|
||||
# Graph memory & attack-path reasoning (Neo4j)
|
||||
|
||||
NeuroSploit keeps three graphs: the **knowledge graph** (what was learned about a
|
||||
target, across runs), the **internal graph** (hosts, identities, credentials, the
|
||||
AD credential loop) and the **chain links** (which finding hands which capability
|
||||
to which). `graph_memory.rs` projects all three into one typed schema, reasons
|
||||
over it, and — when configured — MERGEs it into Neo4j and reads accumulated paths
|
||||
back.
|
||||
|
||||
It works without Neo4j. Every run writes `graph/` next to the report:
|
||||
|
||||
| file | what |
|
||||
|---|---|
|
||||
| `graph/attack-chains.md` | chains in the 11-field analyst format, plus next moves |
|
||||
| `graph/graph.cypher` | idempotent MERGE script: `cypher-shell -a <uri> -u <user> -p <pw> -f graph.cypher` |
|
||||
| `graph/graph.json` | nodes/edges as JSON |
|
||||
| `graph/queries.cypher` | ready-made analyst queries |
|
||||
|
||||
## The graph is a phase of the test
|
||||
|
||||
The pipeline runs a **graph checkpoint** at each stage, in every mode (black-box,
|
||||
grey-box, host/infra, container, mobile, AI):
|
||||
|
||||
| checkpoint | what goes in | what it changes |
|
||||
|---|---|---|
|
||||
| `recon` | recon surface (in-scope hosts, services, endpoints, tech, secret fingerprints) + this target's cross-run memory + saved internal graph + Neo4j paths | black-box selection adds **graph-directed agents** (e.g. SSRF → `ssrf_cloud`, authz/identities → `authorization_matrix`, yielded secret → `session_lifecycle`, PostgreSQL+SQLi → `postgres_superuser_rce`, datastore tech → `exposed_datastore`) |
|
||||
| `validated` | the validated findings | every chaining agent sees the new chains |
|
||||
| `chain round N` | each round's validated findings | footholds on the most valuable chains are expanded first |
|
||||
| `final` | final findings (before secrets are masked) | Neo4j sync, report |
|
||||
|
||||
Each checkpoint publishes a **live context** that `operator_directives` adds to
|
||||
every exploit and chaining prompt: the surface, the top chains with their status,
|
||||
the next moves, and the graph discipline (report obtained credentials in
|
||||
`secret`/`account`, dependencies in `chains_from`, unproven steps as hypotheses).
|
||||
It also writes `graph/live-chains.md` and `graph/live.json` so the operator can
|
||||
watch the graph evolve, and MERGEs the run's slice into Neo4j when configured.
|
||||
|
||||
**Dependency todolist** (`graph/todo.md`). Every unconfirmed chain is laid out
|
||||
step by step: `done` when the graph holds evidence for the step, `ready` when every
|
||||
earlier step is done, `blocked` otherwise. The graph *is* the persistent list, so
|
||||
it survives rounds and restarts. The chaining rounds dispatch `ready` steps first,
|
||||
one per foothold, each with the exact step to perform and an instruction not to
|
||||
jump ahead; a step already attempted is not re-sent.
|
||||
|
||||
**Shared traces** (`traces/<agent>.md`). Each agent's transcript is written as it
|
||||
finishes. Every prompt carries a structured digest of the other agents' traces
|
||||
(endpoint paths, parameter names, error signatures, product versions, block
|
||||
signals — never raw lines), fenced as untrusted data, plus the directory to
|
||||
`grep` for full context.
|
||||
|
||||
**Wire-bound evidence** (always on, before validation). Each exchange an agent
|
||||
reports is checked against recorded traffic (when the recording interceptor ran)
|
||||
and re-sent by the harness itself through the scope-guarded replay engine
|
||||
(non-mutating verbs only; identities only when their credentials were recorded).
|
||||
When the wire reproduces the account, the harness's observation replaces it and
|
||||
the validators judge real bytes (`[wire-verified]`). When it does not, the
|
||||
disagreement is recorded and the finding cannot stand as confirmed. What cannot be
|
||||
checked stays as reported and is counted as unverifiable.
|
||||
|
||||
Cross-run memory is scoped to the target: only the component reachable from this
|
||||
target's asset node, through target-specific nodes, is used — another
|
||||
engagement's history never enters this one's prompts.
|
||||
|
||||
## Using it
|
||||
|
||||
```bash
|
||||
# During a run: chaining rounds get the graph's chains and next moves, and the
|
||||
# result is pushed to Neo4j at the end.
|
||||
export NEO4J_USER=neo4j NEO4J_PASSWORD='…'
|
||||
neurosploit run https://app.example --neo4j bolt://localhost:7687
|
||||
|
||||
# After a run (offline, or with --push to MERGE + read back accumulated paths)
|
||||
neurosploit graph <run-id|dir> [--internal internal-graph.json] [--push] [--top 10]
|
||||
|
||||
# Internal network: save the AD/host graph into the run, then reason over both
|
||||
neurosploit internal --graph ad.json --from <node> --save runs/<id>/internal-graph.json
|
||||
neurosploit graph <run-id>
|
||||
```
|
||||
|
||||
Configuration: `--neo4j <uri>` or `NEO4J_URI` (`bolt://`, `neo4j://`, `neo4j+s://`
|
||||
are mapped to the HTTP transactional endpoint on 7474/7473; `http(s)://` is used
|
||||
as is). Credentials only from `NEO4J_USER` (or `NEO4J_USERNAME`),
|
||||
`NEO4J_PASSWORD`, `NEO4J_DATABASE` — never from flags, never written to disk.
|
||||
|
||||
## Schema
|
||||
|
||||
Labels: Target, Asset, Host, IPAddress, Domain, Service, Endpoint, Technology,
|
||||
Vulnerability, Finding, Evidence, Credential, Identity, Role, Permission, Secret,
|
||||
ExploitAttempt, ValidationResult, AttackStep, AttackPath, BusinessImpact, Control,
|
||||
Mitigation.
|
||||
|
||||
Relationships: HAS_ASSET, RESOLVES_TO, EXPOSES, RUNS, HOSTS, USES_TECH,
|
||||
HAS_ENDPOINT, HAS_VULNERABILITY, HAS_FINDING, SUPPORTED_BY, OBSERVED_IN, REQUIRES,
|
||||
ENABLES, YIELDS, CAN_ACCESS, HAS_PERMISSION, ESCALATES_TO, PIVOTS_TO, DEPENDS_ON,
|
||||
VALIDATED_BY, MITIGATED_BY, IMPACTS, PART_OF_PATH.
|
||||
|
||||
Stable keys (MERGE): Host = target + host; Service = host + port + proto; Endpoint =
|
||||
method + normalized URL (ids collapsed to `{id}`, query dropped); Vulnerability =
|
||||
CWE/type + endpoint; Finding = target + type + host + evidence hash; Evidence =
|
||||
hash of the observed exchange (write-once); Credential/Secret = salted fingerprint.
|
||||
|
||||
How web and internal meet: a finding whose evidence demonstrates code execution or
|
||||
internal reach gets `AttackStep -CAN_ACCESS-> Host`. The internal graph's node for
|
||||
the same host shares the key, so its credentials and pivots continue the path.
|
||||
|
||||
## Reasoning rules (enforced in code)
|
||||
|
||||
- **Local vs chain severity.** A chain is only scored above its findings' local
|
||||
severity when it is `confirmed` — every finding confirmed by the validation
|
||||
engine and every edge evidenced. Otherwise it keeps the local severity and states
|
||||
the severity it would have once validated (`High — Critical once validated`).
|
||||
- **Impact must be demonstrated.** An `IMPACTS` edge is confirmed only when the
|
||||
evidence reached that rung: code execution needs a nonce in output or an
|
||||
out-of-band callback; sensitive-data exposure needs sensitive data actually read.
|
||||
"Could lead to RCE" stays `needs_validation`.
|
||||
- **Derived links are hypotheses.** Capability-matched links between findings are
|
||||
`needs_validation`; cross-run memory is never more than `needs_review`.
|
||||
- **Status.** `confirmed` / `needs_validation` / `hypothesis` (rests on an
|
||||
unconfirmed finding, an unproven node, or history).
|
||||
- **Next moves.** The first unproven edge of the most valuable chains, yielded
|
||||
credentials not yet tried against in-scope services (lockout-aware), code
|
||||
execution not yet connected to the internal network, SSRF without a recorded
|
||||
internal target. These feed the attack-chaining rounds, and footholds on the
|
||||
most valuable chains are expanded first.
|
||||
|
||||
## Data minimisation
|
||||
|
||||
No raw secret, token, cookie, password, response body or model narrative becomes a
|
||||
graph property. Secrets are fingerprints salted per installation
|
||||
(`~/.neurosploit/graph-salt`, or `NEUROSPLOIT_GRAPH_SALT` — share one value across a
|
||||
team that shares a Neo4j so credential reuse still correlates). Identities that
|
||||
look like personal data are masked (`a***@corp.test`). Endpoints lose their query
|
||||
strings. Internal credential nodes are named from the node id and kind, never from
|
||||
the label. Labels and relationship types come from closed enums; all values are
|
||||
statement parameters.
|
||||
|
||||
## Queries
|
||||
|
||||
See `graph/queries.cypher`. Examples:
|
||||
|
||||
```cypher
|
||||
// Hypotheses still worth validating, best first
|
||||
MATCH (p:AttackPath) WHERE p.status IN ['needs_validation','hypothesis']
|
||||
RETURN p ORDER BY p.risk_score DESC;
|
||||
|
||||
// Credentials that reach privileged identities (internal)
|
||||
MATCH path = (c)-[:CAN_ACCESS|HAS_PERMISSION|ESCALATES_TO*1..6]->(r)
|
||||
WHERE (c:Credential OR c:Secret) AND (r:Role OR r:Identity) AND r.privileged = true
|
||||
RETURN path;
|
||||
```
|
||||
|
||||
The LLM-facing version of these rules is `agents_md/meta/graph_reasoning.md`.
|
||||
Reference in new issue
Block a user