Skip to content

Attack Path Analysis

Once the offensive lifecycle has collected configs, MCP/A2A enumeration, AI-service posture, looted credentials, and model artifacts, AgentHound's analysis layer turns those facts into attack paths. The graph combines MCP trust relationships, A2A delegation chains, network-discovered AI services, instruction files, untrusted-input signals, and credential reuse into one directed graph where composite edges and Cypher queries reveal multi-hop exploitation routes.

The canonical graph schema is in Graph Model. The canonical processor details and cleanup semantics are in Post-Processors.

Path Families

AgentHound computes 12 composite edge types. Operators usually reason about them in these path families:

Family Composite edges What it answers
Reachability HAS_ACCESS_TO, CAN_REACH What can an agent, tool, or A2A boundary reach if trust edges are followed?
Credential chains CAN_REACH with via_credential or source_collector='cross_service_credential_chain' Where does credential reuse create implicit access?
Execution and exfiltration CAN_EXECUTE, CAN_EXFILTRATE_VIA Which paths lead to command/code execution or sensitive-data egress?
Poisoning and context manipulation SHADOWS, POISONED_DESCRIPTION, POISONED_INSTRUCTIONS, POISONS_CONTEXT Which tools or instruction files can steer model behavior?
Untrusted-input data flow TAINTS, IFC_VIOLATION Can attacker-controlled input flow into compatible tools or high-impact sinks?
A2A identity and delegation CAN_IMPERSONATE, CONFUSED_DEPUTY, cross-protocol CAN_REACH Can an A2A agent mimic, delegate into, or pivot across trust boundaries?

Not every composite edge has a named pre-built query. Pre-built queries live under agenthound-server query --prebuilt <id>; composite-edge findings are always available through agenthound-server query --findings and GET /api/v1/analysis/findings.

1. Reachability (HAS_ACCESS_TO, CAN_REACH)

HAS_ACCESS_TO links tools to resources when capability surface and resource URI scheme line up, or when a tool description references a resource. CAN_REACH then folds agent trust into transitive access:

(:AgentInstance)-[:TRUSTS_SERVER]->(:MCPServer)
  -[:PROVIDES_TOOL]->(:MCPTool)
  -[:HAS_ACCESS_TO]->(:MCPResource)

The can_reach processor emits:

(:AgentInstance)-[:CAN_REACH {hops: 3, via_server, via_tool}]->(:MCPResource)

Credential-mediated reachability is also a CAN_REACH variant. If an agent can reach a tool that can read credentials, and another MCP server authenticates with one of those credentials, AgentHound emits a longer CAN_REACH path with via_credential and hops: 6.

Pre-built queries

agenthound-server query --prebuilt credential-chain
agenthound-server query --prebuilt shortest-to-database
agenthound-server query --prebuilt agents-shell-access

2. Cross-Service Credential Chains (value_hash)

Credential.value_hash is the cross-collector merge primitive. Every collector and looter that observes a Credential value populates it with SHA-256(raw credential value). An explicit observed-material hash match correlates independently discovered records without returning the raw value. Identity-only synthetic hashes are excluded from this join.

Example: a local MCP config exposes a LiteLLM master key, and the LiteLLM looter uses that same key to inventory provider and virtual-key references. LiteLLM masks provider keys and returns virtual keys as hashes, so those downstream records do not imply usable plaintext.

(:AgentInstance)-[:TRUSTS_SERVER]->(:MCPServer)
  -[:AUTHENTICATES_WITH]->(:Identity)
  -[:USES_CREDENTIAL]->(:Credential {value_hash: H1})

(:LiteLLMGateway)-[:EXPOSES_CREDENTIAL]->(:Credential {value_hash: H1})
(:LiteLLMGateway)-[:EXPOSES_CREDENTIAL]->(:Credential {type: "apiKey"})

The cross_service_credential_chain processor joins on value_hash and emits:

(:AgentInstance)-[:CAN_REACH {
  source_collector: "cross_service_credential_chain",
  via_gateway,
  merge_value_hash,
  upstream_provider,
  hops: 6
}]->(:Credential)

via_gateway is always traceable: it uses the gateway name when available, then its endpoint, and otherwise the immutable gateway object ID emitted by the LiteLLM looter.

It also writes blast_radius on the joined credential nodes: the number of distinct agents correlated with the merged secret. The upstream target remains typed as credential_chain_observed_material or credential_chain_reference; masked/hashed references are not usable-secret evidence.

Pre-built query

agenthound-server query --prebuilt litellm-credential-leak

This pre-built query is centered on observed, exposed LiteLLM master-key evidence. Any upstream apiKey or virtual-key nodes in its output are explicit masked/hashed, not-observed references and are not claimed as usable material.

3. Cross-Protocol Pivots (CAN_REACH)

When an external A2A agent delegates to another A2A agent recorded on the same host as an MCP server, AgentHound can emit a cross-protocol CAN_REACH correlation edge. This is a 50%-confidence shared-host correlation, not proof that the A2A actor can invoke the MCP path end to end.

(:A2AAgent)-[:DELEGATES_TO*1..3]->(:A2AAgent)
  -[:RUNS_ON]->(:Host)<-[:RUNS_ON]-(:MCPServer)
  -[:PROVIDES_TOOL]->(:MCPTool)
  -[:HAS_ACCESS_TO]->(:MCPResource)

The emitted edge is:

(:A2AAgent)-[:CAN_REACH {
  cross_protocol: true,
  source_collector: "a2a",
  via_host,
  via_mcp_server,
  via_mcp_tool
}]->(:MCPResource)

The processor requires canonical explicit unauthenticated evidence (auth_assurance = 'unauthenticated'). Missing authentication evidence is unknown and does not match.

Pre-built query

agenthound-server query --prebuilt cross-protocol-paths

4. Execution and Exfiltration

CAN_EXECUTE links an MCP tool to its host when narrow metadata rules classify the tool as shell_access or code_execution. The edge is an 80%-confidence candidate; database-only names such as execute_query do not match.

(:MCPTool)-[:CAN_EXECUTE]->(:Host)

CAN_EXFILTRATE_VIA links an agent to a matched output-channel tool when both conditions are inferred:

  1. The agent can reach a critical or high sensitivity MCPResource.
  2. The agent trusts a server with an outbound-capable tool.

Matched output channel means the tool has one of:

email_send, network_outbound, file_write, auto_fetch_render, allowlisted_proxy

This is a potential route. It does not record an observed transfer.

Pre-built query

agenthound-server query --prebuilt exfiltration-routes

5. Poisoning and Context Manipulation

AgentHound models both direct poisoning indicators and graph-level paths where poisoned context can influence high-impact tools.

SHADOWS

A tool on one server references a tool on another server by name in its description. This can support tool-confusion attacks where the agent calls the shadowing tool instead of the intended capability.

MATCH (shadow:MCPTool)-[r:SHADOWS]->(original:MCPTool)
MATCH (shadow)<-[:PROVIDES_TOOL]-(shadow_server:MCPServer)
MATCH (original)<-[:PROVIDES_TOOL]-(original_server:MCPServer)
WHERE shadow_server.objectid <> original_server.objectid
RETURN shadow.name AS shadowing_tool,
       shadow_server.name AS shadowing_server,
       original.name AS shadowed_tool,
       original_server.name AS shadowed_server,
       r.confidence AS confidence
ORDER BY r.confidence DESC

POISONED_DESCRIPTION

A tool description contains injection patterns detected by the rules engine. This is a self-edge on the tool:

(:MCPTool)-[:POISONED_DESCRIPTION]->(:MCPTool)

POISONED_INSTRUCTIONS

An instruction file loaded by an agent contains suspicious patterns such as imperative overrides, exfiltration commands, or hidden Unicode:

(:InstructionFile)-[:POISONED_INSTRUCTIONS]->(:InstructionFile)

POISONS_CONTEXT

The shadows processor also emits POISONS_CONTEXT when an injection-bearing tool can poison the same agent context that drives a high-capability sibling tool:

(:MCPTool)-[:POISONS_CONTEXT]->(:MCPTool)

The sink tool must carry one of:

shell_access, code_execution, credential_access, email_send

Fan-out is capped to 20 sinks per (agent, source tool) pair to avoid cartesian blowups while still surfacing high-risk sources.

Pre-built queries

agenthound-server query --prebuilt tool-shadowing
agenthound-server query --prebuilt poisoned-tools
agenthound-server query --prebuilt instruction-poisoning

6. Untrusted-Input Data Flow (TAINTS, IFC_VIOLATION)

The MCP collector emits raw INGESTS_UNTRUSTED edges for tools tagged by rules such as untrusted web, email, or fileshare input.

(:MCPTool)-[:INGESTS_UNTRUSTED]->(:MCPResource)

The taints processor emits TAINTS when an untrusted-input tool shares at least two input-schema keys with a tool on another server:

(:MCPTool)-[:TAINTS]->(:MCPTool)

The ifc_violation processor emits IFC_VIOLATION when an untrusted-input tool shares a resource path, within three HAS_ACCESS_TO hops, with a high-impact sink:

(:MCPTool)-[:IFC_VIOLATION]->(:MCPTool)

High-impact sink capabilities are:

credential_access, file_write, email_send

These edges surface through findings rather than dedicated pre-built query IDs.

7. A2A Identity and Delegation

CAN_IMPERSONATE

The can_impersonate processor computes TF-IDF cosine similarity over A2A skill descriptions and emits bidirectional CAN_IMPERSONATE edges for cross-provider agent pairs with similarity greater than 0.8.

(:A2AAgent)-[:CAN_IMPERSONATE]->(:A2AAgent)

CONFUSED_DEPUTY

The auth_strength pre-pass preserves collector-owned configured and observed fields, then writes one paired effective_auth_* tuple and, when supported by evidence, a numeric weakness score. An effective auth_method=none is unauthenticated only when paired with auth_evidence=anonymous_probe_succeeded and effective_auth_source=observed; configured none claims and unknown/custom methods remain unknown. A2A card enumeration alone does not prove anonymous protocol access. Only an exact task-not-found result from the bounded read-only nonexistent-task lookup authors A2A observed anonymous evidence; protected and inconclusive outcomes retain configured card posture. This observation does not claim anonymous task creation or message execution.

The confused_deputy processor emits CONFUSED_DEPUTY when a weakly authenticated A2A agent delegates to a strongly authenticated one:

(:A2AAgent {effective_auth_assurance: "unauthenticated|weak"})
  -[:CONFUSED_DEPUTY]->
(:A2AAgent {effective_auth_assurance: "strong"})

This models a low-trust caller borrowing the privileges of a higher-trust callee.

Traversal Operations and Minimum Weight

The default path operations use only outgoing relationships in the server's explicit security policy. Summary/similarity edges such as CAN_REACH, SAME_AUTH_DOMAIN, SHADOWS, and CAN_IMPERSONATE are not composable security-path steps. Undirected graph navigation is available only through the explicit /api/v1/analysis/topology/... operations; request bodies have no scope field.

Shortest and weighted requests use one deployment-independent bounded minimum-cost implementation. Results do not depend on APOC packaging. max_hops and an expansion cap bound work; response metadata reports the direction, relationship kinds, algorithm, and whether the result is complete. Weighted traversal requires every traversed relationship to carry a non-negative finite risk_weight; missing or invalid weights fail the request.

The critical shortest-to-database pre-built query always uses security scope.

Findings and Path Details

The Findings API returns composite-edge findings ranked by severity. The response shape uses edge_kind, not a processor name.

# Critical findings
agenthound-server query --findings --severity critical

# High findings (the CLI accepts one exact severity per invocation)
agenthound-server query --findings --severity high

# All CAN_REACH findings
curl -s localhost:8080/api/v1/analysis/findings | \
    jq '.findings[] | select(.edge_kind == "CAN_REACH")'

# Fetch one published finding detail and its persisted exact witness
finding_id=$(curl -s localhost:8080/api/v1/analysis/findings | jq -r '.findings[0].id')
curl -s "localhost:8080/api/v1/analysis/findings/${finding_id}" | jq .

The finding detail response serves the detector witness persisted with the published row. Its attack_path field is a typed evidence graph with shape, continuity, direction, completeness, synthetic-join provenance, and nullable attack cost. It is never reconstructed from mutable Neo4j.

The Findings panel renders a path strip only for a complete directed linear graph. Branched, disconnected, cyclic, mixed-direction, and nodes-only evidence is rendered literally as relationships/nodes, without inventing intermediate hops. The Graph Explorer allows click-through from graph entities.

Post-Processor Execution Order

Processors run in dependency order. A processor may only read edges or properties produced by earlier processors.

# Processor Produces Dependencies
1 auth_strength paired node effective_auth_*, auth_strength, and effective TRUSTS_SERVER assessment properties None
2 has_access_to HAS_ACCESS_TO Raw edges
3 can_execute CAN_EXECUTE Raw edges
4 shadows SHADOWS, POISONS_CONTEXT Raw edges
5 poisoned_description POISONED_DESCRIPTION Raw edges
6 poisoned_instructions POISONED_INSTRUCTIONS Raw edges
7 taints TAINTS INGESTS_UNTRUSTED, schema_keys
8 can_reach CAN_REACH auth_strength, HAS_ACCESS_TO
9 cross_service_credential_chain CAN_REACH to upstream credentials, Credential.blast_radius HAS_ACCESS_TO, CAN_REACH, value_hash
10 ifc_violation IFC_VIOLATION HAS_ACCESS_TO, INGESTS_UNTRUSTED
11 can_exfiltrate CAN_EXFILTRATE_VIA CAN_REACH
12 can_impersonate CAN_IMPERSONATE Raw edges
13 confused_deputy CONFUSED_DEPUTY auth_strength, CAN_REACH
14 cross_protocol Cross-protocol CAN_REACH auth_strength, HAS_ACCESS_TO, DELEGATES_TO
15 risk_score risk_score node property Prior processors

Each post-processor is idempotent. Promoting any complete raw scope retires the entire composite epoch, then re-runs every processor against the retained current raw projection. This global replacement ensures narrow MCP, config, or A2A rescans also refresh transitive and cross-domain findings.