Skip to content

CompassSecurity/gitlabhound

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

GitLabHound - GitLab OpenGraph Collector

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

Schema

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
Loading

Note

The above diagram was generated with gitlabhound visualize --traversable.

Nodes

Most important nodes, see the documentation for more specific information.

Icon Node Kind Display Name
GL_Instance GL_Instance GitLab instance metadata
GL_User GL_User User (or bot) account
GL_Group GL_Group A collection of user and sub groups
GL_Project GL_Project GitLab project with associated a repository, CI/CD pipelines, variables, ...
GL_Repository GL_Repository Git repository 1:1 relationship with project
GL_Branch GL_Branch Named branch associated with a repository
GL_InstanceRole GL_InstanceRole Instance-wide permissions, administrator, member, external, unauthenticated
GL_GroupRole GL_GroupRole Group-level permissions, owner, maintainer, developer, ...
GL_ProjectRole GL_ProjectRole Project-level permissions, owner, maintainer, developer, ...

Edges

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

Usage Examples

An example dataset and a few saved queries can be used to explore the graph structure.

Attack path starting in Entra ID, traversing GitLab and ending in Active Directory

Example attack path starting in Entra ID, traversing GitLab and ending in Active Directory.

Collector Usage

Build the collector using:

go build
  1. For hybrid-edges, ingest Active Directory (with ADIDNS information) and Entra ID data.

  2. Create a Personal Access Token (PAT) by navigating to Edit profile > Access tokens and grant it the read_api scope. For best results, use an administrator account.

  3. Create a new BloodHound user with read-only permissions.

  4. Configure credentials using environment variables:

    export GITLAB_URL=https://git.pwn.gift/
    export GITLAB_TOKEN=glpat-...
    export BLOODHOUND_USERNAME=api
    export BLOODHOUND_PASSWORD=...
  5. Register custom node kinds and associated icons in BloodHound:

    gitlabhound register
  6. Collect data from your GitLab instance:

    gitlabhound collect > gitlab.json
  7. Upload the resulting JSON file into BloodHound. Once ingested, perform post-processing to further enrich the graph:

    gitlabhound enrich > gitlab-enrichment.json
  8. 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.

  9. 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

References

About

GitLabHound is a BloodHound OpenGraph collector for GitLab

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Languages