Skip to content

Back to the catalog

AWSHound — AWS BloodHound OpenGraph Collector
github.comrepo

AWSHound — AWS BloodHound OpenGraph Collector

AWSHound is a Go CLI/container tool that collects AWS IAM and authorization data across profiles, config-file opt-ins, or an entire AWS Organization (via cross-account role assumption), and outputs a BloodHound OpenGraph for attack-path analysis. It supports phased collection with on-disk artifacts, offline graph building from previously collected data, and safeguards like --max-api-requests and --max-api-requests-per-minute to bound AWS API traffic during collection.

TL;DR Go tool that collects AWS IAM data and builds a BloodHound OpenGraph for mapping cloud privilege-escalation attack paths.
Document

What it is

AWSHound is a Go collector (cmd/awshound, also published as ghcr.io/awshound/awshound:latest) that gathers AWS IAM/authorization data and emits a BloodHound OpenGraph JSON. Collection is split into three phases exposed as standalone subcommands (with on-disk artifacts between them) or as a single all run.

How it works

Live collection (collect/all) picks target accounts from at least one source, which can be combined and are de-duplicated:

  • --profile — one or more named CLI profiles (repeatable or comma-separated); credentials resolve through the normal AWS chain (shared config/credentials, SSO, assumed roles).
  • --aws-config — collects every profile in a config file whose section carries bloodhound_collect = true, so the collection set lives in the config rather than in flags.
  • --org-role ROLE — org-wide collection. The base identity must enumerate the org; AWSHound calls sts:GetCallerIdentity + organizations:ListAccounts, collects the management account directly, then assumes arn:aws:iam::<account-id>:role/ROLE into each child. ROLE has no default — pass OrganizationAccountAccessRole or your own audit role. Child accounts whose role can't be assumed are logged and skipped; the run still produces a graph.

Regions auto-discover per account unless narrowed with --regions.

What to do

Rate-limit safeguards matter in authorized/shared environments — both count retries, both default to unlimited (0):

awshound collect --profile prod --metrics metrics.json \
  --max-api-requests 20000 \
  --max-api-requests-per-minute 600

--max-api-requests stops with non-zero status before exceeding the total, writing the report with awsApi.limitExceeded: true. --max-api-requests-per-minute instead throttles to that many attempt-starts per rolling 60-second window.

Build offline from prior data without touching AWS:

awshound all --input-dir raw/ -o graph.json
awshound all --gaad auth-details.json -o graph.json

Use --metrics <path> to record per-service/operation API activity (attempts, retries, throttles, limiter wait time, latency).

Caveats

  • These limits govern only this process's aggregate traffic. AWS quotas are scoped by service/operation/account/region and shared with other workloads, so pick a conservative value for the authorized environment.
  • --max-concurrency is a worker bound, not an API request-rate limit.
  • An "attempt" is one admitted SDK HTTP send — paginator pages, retries, redirects, and per-account/region calls count separately; a local transport failure conservatively consumes a budget slot even if AWS never processed it.
  • On a --max-api-requests cancellation, raw files already written remain usable with --resume, but the active account batch is not marked complete.

Techniques named here

  • Live AWS collection via CLI profiles
  • Config-based profile opt-in collection
  • Organization-wide role assumption collection
  • Region-scoped collection restriction
  • API request metrics tracking
  • Offline graph building from prior artifacts