AWS IAM privilege-escalation graph builder and pathfinder for Non-Human Identities (roles, CI/CD pipelines, service accounts).
Project description
nhi-hunter
An AWS IAM privilege-escalation graph builder and pathfinder for Non-Human Identities — find the shortest path from a low-privilege role (a CI/CD pipeline, a service account) to Admin.
⚠️ For authorized security testing only. Run it against an account you own or have explicit permission to assess.
nhi-hunteronly reads a local JSON dump — it never calls the AWS API and never assumes a role.
Why nhi-hunter?
Modern AWS accounts are full of Non-Human Identities (NHIs): CI/CD pipeline roles (GitHub Actions OIDC, GitLab runners), Lambda execution roles, cross-account automation roles. Each one has a permission policy and a trust policy — and the combination of the two, across many identities, can quietly chain together into a path from "deploy bot" to "AdministratorAccess".
nhi-hunter takes a local AWS IAM authorization-details dump (no live API
calls, no credentials needed beyond the read-only export) and:
- Parses every role/user, its permission policies (inline + attached managed + group), and its trust policy.
- Builds a directed graph where an edge
A -> Bmeans "identity A can obtain the effective permissions of identity B", via:sts:AssumeRole— A's policy allows assuming B, and B's trust policy allows A.iam:PassRole+lambda:*— A can pass role B to a new/updated Lambda function and invoke it, running code as B.
- Finds the shortest path from a
--start-roleto any identity flagged as admin (AdministratorAccess/PowerUserAccessattached, or a wildcardAction: "*", Resource: "*"statement) and prints it as a readable attack tree.
V1 is intentionally narrow — full policy-evaluation completeness is
already covered by mature tools like
PMapper,
Cloudsplaining, and
Cartography. nhi-hunter
is for the "give me a readable attack-path tree from this dump, right now"
case — a quick purple-team check, or a CI gate on terraform plan output.
Quick start
pip install nhi-hunter
# Export your account's IAM data (read-only API call):
aws iam get-account-authorization-details > aws_iam_dump.json
nhi-hunter scan --input aws_iam_dump.json --start-role GitHubActionsOIDC
[*] Loaded 5 identities, 11 escalation edge(s)
[*] Admin/power-user targets: AdminRole, LambdaAdminRole
[*] Searching for escalation paths from 'GitHubActionsOIDC'...
ESCALATION PATH FOUND (1 hop(s)) -> LambdaAdminRole
GitHubActionsOIDC
--[iam:PassRole + lambda:*]--> LambdaAdminRole
ESCALATION PATH FOUND (2 hop(s)) -> AdminRole
GitHubActionsOIDC
--[iam:PassRole + lambda:*]--> LambdaAdminRole
--[iam:PassRole + lambda:*]--> AdminRole
SCAN COMPLETE
┌────────────────────────┬───────┐
│ Metric │ Value │
├────────────────────────┼───────┤
│ Escalation paths found │ 2 │
│ Shortest path length │ 1 │
└────────────────────────┴───────┘
A multi-hop sts:AssumeRole chain looks like this (from the bundled
example dump, --start-role CIRunner):
ESCALATION PATH FOUND (2 hop(s)) -> AdminRole
CIRunner
--[sts:AssumeRole]--> DevRole
--[sts:AssumeRole]--> AdminRole
(Note: once a role holds AdministratorAccess, it technically has
iam:PassRole+lambda:*/sts:AssumeRole over every other identity too —
this is why LambdaAdminRole and AdminRole show edges to/from each other
in the example dump. The interesting result is the path into the first
admin identity reached from your low-privilege starting role.)
nhi-hunter scan exits non-zero if any escalation path is found — usable
as a CI gate on infrastructure changes.
Try it without an AWS account
examples/sample_iam_dump.json is a small, deliberately-misconfigured IAM
dump (same data used in the test suite) with two escalation paths baked
in: a 2-hop sts:AssumeRole chain (CIRunner → DevRole → AdminRole)
and a 1-hop iam:PassRole + Lambda chain (GitHubActionsOIDC →
LambdaAdminRole).
nhi-hunter scan --input examples/sample_iam_dump.json --start-role CIRunner
nhi-hunter list-identities --input examples/sample_iam_dump.json
Console commands
nhi-hunter scan --input <file> --start-role <name> [options] find escalation paths
nhi-hunter list-identities --input <file> list all identities + edges
Scan options:
-i, --input AWS IAM authorization-details JSON dump (required)
-s, --start-role identity (role or user) name to start from (required)
-o, --output write full results as JSON
--aegistrace-url report escalation paths to an AegisTrace instance
--aegistrace-key AegisTrace ingest API key
How the graph is built
| Edge technique | Condition | MITRE ATT&CK |
|---|---|---|
sts:AssumeRole |
A's policy allows sts:AssumeRole on B's ARN and B's trust policy allows A (by ARN, account root, or *) |
T1078.004 Valid Accounts: Cloud Accounts |
iam:PassRole + lambda:* |
A's policy allows iam:PassRole on B's ARN and A can lambda:CreateFunction/UpdateFunctionCode/InvokeFunction |
T1078.004 |
An identity is flagged admin if it has AdministratorAccess or
PowerUserAccess attached, has a statement granting Action: "*" on
Resource: "*", or its name contains "admin"/"poweruser".
V1 limitations (documented, not silently swallowed):
- Statements using
NotAction/NotResourceare skipped. Conditionblocks are ignored — a reported edge means "policy allows this", not "no condition would block it in practice".- Only identity-to-identity edges are modeled; resource policies (S3 bucket policies, Lambda resource policies, etc.) are not considered.
AegisTrace integration
AegisTrace's Identity Graph
- Risk Engine models identities and relationships across an environment.
Point
nhi-hunterat a real account and report any discovered escalation paths directly:
nhi-hunter scan \
--input aws_iam_dump.json \
--start-role GitHubActionsOIDC \
--aegistrace-url https://your-aegistrace-instance \
--aegistrace-key $AEGISTRACE_INGEST_KEY
Discovered paths are POSTed to /api/ingest/nhihunter-event, creating
AgentAction(agent_name="nhi-hunter") entries visible in AegisTrace's
AI Action Approval Queue (/app/agent-security) — the same workflow used
for mcp-aegis and prompt-fuzz findings.
Testing
pip install -e ".[dev]"
pytest
Companion projects
- mcp-sploit — Metasploit-style exploitation framework for MCP servers (attacks the AI's tools).
- prompt-fuzz — async LLM jailbreak/prompt-injection fuzzer (attacks the AI's brain).
- AegisTrace — Trust OS that makes AI agent actions auditable and human-approved.
- mcp-aegis — MCP security gateway; blocks dangerous tool calls by default.
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file nhi_hunter-0.1.0.tar.gz.
File metadata
- Download URL: nhi_hunter-0.1.0.tar.gz
- Upload date:
- Size: 13.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1bc5bae96eb4ade7c9d23e0a87e544681c4073a72c5bbb8be344df015887749d
|
|
| MD5 |
af18afe7be36684d74a9ac2edecc6d69
|
|
| BLAKE2b-256 |
8f7a616a35f10430c52269ad3588a435aaecf13776319d0272de5b5384b82fba
|
File details
Details for the file nhi_hunter-0.1.0-py3-none-any.whl.
File metadata
- Download URL: nhi_hunter-0.1.0-py3-none-any.whl
- Upload date:
- Size: 13.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
78164d9f6d21c94fa4d399a1973195239098707a2a9f3ba2aeadd272066b5b88
|
|
| MD5 |
facccfe77d012c89169ac381e3c18fe6
|
|
| BLAKE2b-256 |
b77a6813aabb114b99b0808724ddbcaca00a82dd39870990807fca7dad3a2e83
|