GitLabHound is a BloodHound OpenGraph collector for GitLab to generate a navigable attack‑path graph composed of:
- Core GitLab resources: users, groups, roles, projects, repositories, branches, CI/CD pipelines and jobs
- Hybrid identities using SSO from Active Directory and Entra ID
- Cross-cloud paths through OIDC trust relationships
- Domain-joined Windows runners with shell executors
- Secrets leaked through public resources
- Renovate dependency bot abuse
Traversable edges are illustrated below, check the documentation for more specific information.
flowchart TD
GL_GroupVariable[fa:fa-sliders GL_GroupVariable]
AZUser[fa:fa-user AZUser]
AZServicePrincipal[fa:fa-robot AZServicePrincipal]
Computer[fa:fa-desktop Computer]
GL_ProjectVariable[fa:fa-sliders GL_ProjectVariable]
GL_PipelineSchedule[fa:fa-clock-rotate-left GL_PipelineSchedule]
GL_User[fa:fa-user GL_User]
GL_ProjectRole[fa:fa-user-tie GL_ProjectRole]
GL_GroupAccessToken[fa:fa-key GL_GroupAccessToken]
GL_Branch[fa:fa-code-branch GL_Branch]
GL_Variable[fa:fa-sliders GL_Variable]
GL_AccessToken[fa:fa-key GL_AccessToken]
GL_Instance[fa:fa-building GL_Instance]
GL_InstanceVariable[fa:fa-sliders GL_InstanceVariable]
GL_PipelineVariable[fa:fa-sliders GL_PipelineVariable]
User[fa:fa-user User]
GL_Group[fa:fa-user-group GL_Group]
GL_Project[fa:fa-diagram-project GL_Project]
GL_InstanceRole[fa:fa-user-tie GL_InstanceRole]
GL_ProjectAccessToken[fa:fa-key GL_ProjectAccessToken]
GL_LeakedSecret[fa:fa-key GL_LeakedSecret]
GL_JobLog[fa:fa-scroll GL_JobLog]
GL_JobArtifact[fa:fa-file-zipper GL_JobArtifact]
GL_PersonalAccessToken[fa:fa-key GL_PersonalAccessToken]
GL_GroupRole[fa:fa-user-tie GL_GroupRole]
GL_Group -->|GL_MemberOf| GL_Group
GL_GroupRole -->|GL_MemberOf| GL_Group
GL_Instance -->|GL_Defines| GL_InstanceVariable
GL_Group -->|GL_Defines| GL_GroupVariable
GL_Project -->|GL_Defines| GL_ProjectVariable
GL_PipelineSchedule -->|GL_Defines| GL_PipelineVariable
GL_User -->|GL_Owns| GL_PipelineSchedule
GL_User -->|GL_HasRole| GL_InstanceRole
GL_User -->|GL_HasRole| GL_GroupRole
GL_User -->|GL_HasRole| GL_ProjectRole
GL_GroupAccessToken -->|GL_HasRole| GL_GroupRole
GL_ProjectAccessToken -->|GL_HasRole| GL_ProjectRole
GL_GroupRole -->|GL_InheritRole| GL_GroupRole
GL_GroupRole -->|GL_InheritRole| GL_ProjectRole
GL_InstanceRole -->|GL_HasBaseRole| GL_InstanceRole
GL_InstanceRole -->|GL_HasBaseRole| GL_GroupRole
GL_InstanceRole -->|GL_HasBaseRole| GL_ProjectRole
GL_GroupRole -->|GL_HasBaseRole| GL_GroupRole
GL_ProjectRole -->|GL_HasBaseRole| GL_ProjectRole
GL_Group -->|GL_InvitedTo| GL_Group
GL_Group -->|GL_InvitedTo| GL_Project
GL_GroupRole -->|GL_ManageMembers| GL_Group
GL_ProjectRole -->|GL_ManageMembers| GL_Project
GL_ProjectRole -->|GL_CanPush| GL_Branch
GL_ProjectRole -->|GL_CanMerge| GL_Branch
GL_InstanceRole -->|GL_CanReadSecret| GL_LeakedSecret
GL_ProjectRole -->|GL_CanReadSecret| GL_LeakedSecret
GL_Variable -->|GL_ContainsCredentialsFor| GL_LeakedSecret
GL_JobLog -->|GL_ContainsCredentialsFor| GL_LeakedSecret
GL_JobArtifact -->|GL_ContainsCredentialsFor| GL_LeakedSecret
GL_InstanceVariable -->|GL_IsToken| GL_AccessToken
GL_GroupVariable -->|GL_IsToken| GL_AccessToken
GL_ProjectVariable -->|GL_IsToken| GL_AccessToken
GL_PipelineVariable -->|GL_IsToken| GL_AccessToken
GL_LeakedSecret -->|GL_IsToken| GL_AccessToken
GL_PersonalAccessToken -->|GL_HasPrivilegeOf| GL_User
GL_InstanceRole -->|GL_RenovateInviteAndTakeover| GL_User
AZUser -->|GL_SyncedTo| GL_User
User -->|GL_SyncedTo| GL_User
GL_Branch -->|GL_CanAssumeIdentity| AZServicePrincipal
GL_Branch -->|GL_BuildsAsSystem| Computer
Note
The above diagram was generated with gitlabhound visualize --traversable.
Most important nodes, see the documentation for more specific information.
| Icon | Node Kind | Display Name |
|---|---|---|
| GL_Instance | GitLab instance metadata | |
| GL_User | User (or bot) account | |
| GL_Group | A collection of user and sub groups | |
| GL_Project | GitLab project with associated a repository, CI/CD pipelines, variables, ... | |
| GL_Repository | Git repository 1:1 relationship with project | |
| GL_Branch | Named branch associated with a repository | |
| GL_InstanceRole | Instance-wide permissions, administrator, member, external, unauthenticated | |
| GL_GroupRole | Group-level permissions, owner, maintainer, developer, ... | |
| GL_ProjectRole | Project-level permissions, owner, maintainer, developer, ... |
Most important edges, see the documentation for more specific information.
| Category | Key Edges | Description |
|---|---|---|
| Role Assignment | GL_HasRole, GL_MemberOf, GL_HasBaseRole, GL_InheritRole, GL_InvitedTo | Who holds which role on an instance, group, or project |
| Repository Access | GL_CanPush, GL_CanMerge, GL_CanPull, GL_ManageProtectedBranches | Branch-level read/write/merge rights |
| Privilege Management | GL_ManageMembers, GL_ManageVariables, GL_ManageRunners | Administrative control over resources |
| Secrets & Credentials | GL_IsToken, GL_HasToken, GL_ContainsCredentialsFor, GL_HasPrivilegeOf | Credential exposure via variables, artifacts, and access tokens |
| CI/CD Execution | GL_RunCICD, GL_Triggers, GL_CanUseRunner, GL_RunsOn | Pipeline trigger rights and runner assignment |
| Cross-Cloud & Hybrid | GL_CanAssumeIdentity, GL_SyncedTo, GL_HostedOn, GL_BuildsAsSystem | Attack paths to Azure, Entra ID, and domain-joined hosts |
| Privilege Escalation | GL_RenovateInviteAndTakeover | Lateral movement and account takeover techniques |
An example dataset and a few saved queries can be used to explore the graph structure.
Example attack path starting in Entra ID, traversing GitLab and ending in Active Directory.
Build the collector using:
go build-
For hybrid-edges, ingest Active Directory (with ADIDNS information) and Entra ID data.
-
Create a Personal Access Token (PAT) by navigating to Edit profile > Access tokens and grant it the
read_apiscope. For best results, use an administrator account. -
Create a new BloodHound user with read-only permissions.
-
Configure credentials using environment variables:
export GITLAB_URL=https://git.pwn.gift/ export GITLAB_TOKEN=glpat-... export BLOODHOUND_USERNAME=api export BLOODHOUND_PASSWORD=...
-
Register custom node kinds and associated icons in BloodHound:
gitlabhound register
-
Collect data from your GitLab instance:
gitlabhound collect > gitlab.json -
Upload the resulting JSON file into BloodHound. Once ingested, perform post-processing to further enrich the graph:
gitlabhound enrich > gitlab-enrichment.json -
Upload the resulting JSON file into BloodHound.
[!note] It might be necessary to repeat the enrichment phase multiple times, in case post-processing edges depend on each other.
-
If you have findings from trufflehog or pipeleek in JSON form, you can try to map them to existing GitLab resources:
gitlabhound enrich secrets trufflehog.json pipeleek.json > gitlab-secrets.json
