OpenGraph collector for BloodHound that maps attack paths from DevOps to MLOps infrastructure, collecting CI/CD pipeline, service principal, and ML platform resources for lateral movement analysis.
A proof-of-concept OpenGraph collector for BloodHound that maps attack paths from DevOps to MLOps infrastructure.
Based on Pipelines of Privilege: Attack Paths from DevOps to MLOps Infrastructure by Brett Hawkins (@h4wkst3r).
Dop2Mop collects data from DevOps and MLOps platforms to identify attack paths that enable lateral movement from CI/CD pipelines to machine learning training infrastructure. It outputs BloodHound-compatible OpenGraph JSON for visualization and analysis.
| DevOps Platforms | MLOps Platforms | Identity Providers |
|---|---|---|
| GitHub (Actions, Repos, Secrets) | Azure Machine Learning | Azure AD Service Principals |
| Azure DevOps (Pipelines, Service Connections) | Amazon SageMaker | AWS IAM Roles OIDC/Federated Identity |
Dop2Mop models the five critical trust boundaries identified in the research:
git clone https://github.com/h4wkst3r/dop2mop.git
cd dop2mop
pip install -r requirements.txt
pip install -e .
Each collector gathers platform-specific resources and maps the trust boundaries between them:
| Collector | Resources Collected |
|---|---|
| GitHub | Organizations, repositories, workflows, secrets, branch protection rules, OIDC configurations, container image references, S3 bucket references |
| Azure DevOps | Organizations, projects, pipelines (YAML), service connections (with scope details), variable groups, agent pools, repositories |
| Azure ML | Workspaces, compute clusters/instances, datastores, ML environments, registered models, jobs/experiments, online & batch endpoints |
| SageMaker | Training jobs, models, endpoints, domains, notebook instances, IAM execution roles (with policy analysis), ECR repositories/images, S3 buckets (filtered to ML-relevant) |
The fastest way to see Dop2Mop in action is to generate demo data showing the attack scenarios from the research:
dop2mop demo -o demo.json
// Azure DevOps to Azure ML Lateral Movement
MATCH p=(repo)-[:TriggersPipeline]->(pipeline)-[:UsesServiceConnection]->(svcconn)-[:AuthenticatesAs]->(workspace)-[:CodeExecution]->(compute)
RETURN p
// Container Image Poisoning (Supply Chain Attack)
MATCH p=(workflow)-[:CanPoisonImage]->(image)<-[:PullsImage]-(job)
RETURN p
// OIDC/Federated Identity Abuse (confirmed edges)
MATCH p=(workflow)-[:OIDCTrust]->(oidc)-[:CanAssumeRole]->(role)-[:SubmitsJob]->(job)
RETURN p
// OIDC abuse including inferred paths
MATCH p=(workflow)-[:OIDCTrust]->(oidc)-[:InferredCanAssumeRole]->(role)
RETURN p
// Dataset Poisoning via Pickle Deserialization
MATCH p=(workflow)-[:CanPoisonDataset]->(dataset)<-[:LoadsDataset]-(job)
RETURN p
// Find repos with weak/no branch protection (TB1 exploitable)
MATCH (repo)-[:BypassesProtection]->(repo)
RETURN repo.name, repo.default_branch
// Find overprivileged SageMaker IAM roles
MATCH (role:IAMRole) WHERE role.is_admin = true OR role.has_s3_full_access = true
RETURN role.name, role.attached_policies
// Find SageMaker notebooks with root + internet access
MATCH (nb:SMNotebook) WHERE nb.root_access = 'Enabled' AND nb.direct_internet_access = 'Enabled'
RETURN nb.name, nb.status
// Find self-hosted ADO agent pools
MATCH (agent:ADOAgent) WHERE agent.is_hosted = false
RETURN agent.name, agent.pool_type
Instead of passing credentials on every run, you can save them in a config file. Copy the included example and fill in your values:
cp dop2mop.yaml.example dop2mop.yaml
# Edit dop2mop.yaml with your credentials
Dop2Mop checks these locations in order:
--configdop2mop.yaml / dop2mop.yml / .dop2mop.yaml in the current directory~/.dop2mop.yamlNote:
dop2mop.yamlis in.gitignoreto prevent accidentally committing credentials. The example file (dop2mop.yaml.example) is safe to commit.
See dop2mop.yaml.example for all available options with comments.
Priority order: CLI arguments > config file > environment variables.
Test credentials before running a full collection:
# Validate all configured collectors
dop2mop collect --validate -v
# Validate specific collectors
dop2mop collect --validate --collectors github,sagemaker -v
This makes a lightweight API call per platform to verify tokens are valid before starting collection.
You can use short names instead of full class names with --collectors:
| Alias | Collector |
|---|---|
github, gh | GitHubCollector |
ado, azuredevops, azure-devops | AzureDevOpsCollector |
azureml, azure-ml | AzureMLCollector |
sagemaker, sm | SageMakerCollector |