
GitHub용 BloodHound OpenGraph 수집기로, 조직 구조, 권한, 클라우드 간 공격 경로를 탐색 가능한 그래프로 매핑하여 보안 감사 및 사고 대응에 활용합니다.

GitHound는 GitHub용 BloodHound OpenGraph 수집기로, 조직의 구조와 권한을 탐색 가능한 공격 경로 그래프로 매핑하도록 설계되었습니다. 다음을 수행합니다.
주요 GitHub 엔티티 모델링
BloodHound에서 시각화 및 분석
GitHound를 사용하면 GitHub 권한 환경의 명확하고 대화형 그래프를 얻을 수 있으며, 보안 검토, 규정 준수 감사 및 신속한 사고 조사에 이상적입니다.
자세한 문서는 BloodHound Docs - GitHound를 참조하세요.
# 1. 수집기 로드
. ./githound.ps1
# 2. 개인 액세스 토큰으로 세션 생성
$session = New-GitHubSession -OrganizationName "YourOrgName" -Token (Get-Clipboard)
# 3. 수집 실행
Invoke-GitHound -Session $session
# 4. 결과 githound_<orgId>.json 파일을 BloodHound에 업로드
수집이 중단된 경우 중단된 지점부터 다시 시작합니다:
Invoke-GitHound -Session $session -Resume
GitHound는 개인 액세스 토큰 세션과 GitHub 앱 설치 세션을 모두 지원합니다. 기존 조직 범위 GitHub 앱 워크플로는 변경되지 않았습니다:
. ./githound.ps1
$session = New-GitHubJwtSession `
-OrganizationName "YourOrgName" `
-ClientId $clientId `
-PrivateKeyPath $privateKeyPath `
-InstallationId $installationId
Invoke-GitHound -Session $session -CollectAll
동일한 함수로 엔터프라이즈 지원 세션도 생성할 수 있습니다:
. ./githound.ps1
$session = New-GitHubJwtSession `
-EnterpriseName "YourEnterpriseSlug" `
-ClientId $clientId `
-PrivateKeyPath $privateKeyPath `
-InstallationId $installationId `
-PersonalAccessToken $pat
엔터프라이즈 지원 세션은 반환된 GitHound.Session에 여러 인증 컨텍스트를 유지합니다:
Headers: 일반 수집에 사용되는 GitHub 앱 설치 토큰 헤더JwtHeaders: 설치 열거 등 앱 수준 엔드포인트에 사용되는 GitHub 앱 JWT 헤더PatHeaders: 사용자 토큰 인증이 필요한 수집 경로에 대한 선택적 개인 액세스 토큰 헤더인증된 GitHub 앱에 속한 설치를 열거하려면:
Get-GitHubAppInstallation -Session $session |
Select-Object TargetType, InstallationId, Login, Name, SuspendedAt
-CollectAll을 사용할 때 워크플로 파싱이 이제 Invoke-GitHound에 내장되었습니다. 수집기는 다음을 수행합니다:
GH_Workflow 노드 및 워크플로 내용 수집GH_WorkflowJob 및 GH_WorkflowStep으로 분석GH_CanPwnRequest 및 GH_CanDispatchTo 계산githound_<orgId>.json 출력에 병합재개/디버깅 목적으로 중간 워크플로 분석 체크포인트가 githound_WorkflowAnalysis_<orgId>.json으로 기록됩니다.
GitHound는 이제 Git-HoundEnterprise를 통해 최소한의 엔터프라이즈 수집 기반을 포함합니다.
해당 수집기는 현재 다음을 생성합니다:
GH_EnterpriseGH_Organization 스텁 노드GH_Contains 엣지Git-HoundEnterpriseUser를 통한 엔터프라이즈 사용자 수집은 다음을 추가합니다:
GH_UserGH_HasMember 엣지Git-HoundEnterpriseSamlProvider를 통한 엔터프라이즈 SAML 수집은 다음을 추가합니다:
GH_SamlIdentityProviderGH_ExternalIdentityGH_HasSamlIdentityProvider이 경로는 GitHub에서 엔터프라이즈 SAML을 enterprise.ownerInfo를 통해 노출하므로 PAT 기반 세션이 필요합니다.
Git-HoundEnterpriseTeam을 통한 엔터프라이즈 팀 수집은 다음을 추가합니다:
GH_EnterpriseTeamGH_AssignedTo 엣지ent: GH_Team 노드로의 GH_MemberOf 엣지members 역할 및 사용자에서 해당 역할로의 GH_HasRole 엣지Git-HoundEnterpriseRole을 통한 엔터프라이즈 역할 수집은 다음을 추가합니다:
GH_EnterpriseRoleGH_Contains 엣지GH_HasRole 엣지enterprise.ownerInfo.admins에서 채워진 기본 owners 역할현재 원시 엔터프라이즈 권한 문자열은 전용 권한 엣지로 확장되지 않고 GH_EnterpriseRole 노드의 permissions 속성에 보존됩니다.
엔터프라이즈 SCIM 수집은 현재 다음을 추가합니다:
SCIM_UserSCIM_GroupSCIM_User에서 GH_ExternalIdentity로의 SCIM_Provisionedgroup_id를 노출할 때 SCIM_Group에서 GH_EnterpriseTeam으로의 SCIM_ProvisionedSCIM_User에서 SCIM_Group으로의 SCIM_MemberOf이를 통해 GitHound는 공유 SCIM 스키마에서 GitHub의 기본 엔터프라이즈 ID 및 팀 모델로의 공급자 중립적 브리지를 제공합니다.
수집된 GH_SamlIdentityProvider가 업스트림 IdP를 식별하는 경우, GitHound는 SCIM 사이드카 출력 내에 공급자 인식 SCIM 상관 관계 엣지를 추가할 수도 있습니다:
Okta_User -> SCIM_User
Okta_User.id = SCIM_User.externalId로 일치Okta_Group -> SCIM_Group
Okta_Group.name = SCIM_Group.externalId로 일치Okta_Group.oktaDomain = GH_SamlIdentityProvider.foreign_environmentidGitHound는 SCIM 계층을 자체 사이드카 출력에 유지하여 이러한 매핑이 SCIM 네이티브 노드를 기본 GitHub 네이티브 엔터프라이즈 그래프에 혼합하지 않고도 계속 표시되도록 합니다:
githound_<entId>.json에는 엔터프라이즈 GitHub 네이티브 데이터 포함githound_scim_<entId>.json에는 SCIM 네이티브 노드 및 SCIM 브리지 엣지 포함githound_saml_<entId>.json에는 SAML 및 외부 ID 데이터 포함githound_hybrid_<entId>.json에는 SAML_Implements, SAML_HasAccount, GH_SyncedTo와 같은 교차 모델 엣지 포함githound_saml_<entId>.json에는 GitHub 서비스 공급자에 대한 정규화된 SAML 토폴로지( SAML_TrustsIssuer 및 SAML_HasAssertionConsumerService 포함)도 포함기본 GitHub ID 공급자 모델은 GitHub/SAML 네이티브 출력에 그대로 유지됩니다:
GH_ExternalIdentityGH_HasExternalIdentityGH_MapsToUsergithound_hybrid_<entId>.json의 정규화된 SAML 계층은 이제 SAML_HasAccount를 GH_User에 직접 배치하고, 연결된 GH_ExternalIdentity의 SAML 지향 속성(예: saml_identity_name_id 및 saml_identity_username)에서 match_values를 파생합니다.
엔터프라이즈 수집에서 내보내는 GH_Organization 스텁은 의도적으로 collected = false로 표시됩니다. 이는 엔터프라이즈 컨텍스트에서 구조적 검색을 나타내며 나중에 일반 조직 수집을 통해 보강됩니다.
엔터프라이즈 우선 오케스트레이션의 경우 Invoke-GitHoundEnterprise는 지원되는 엔터프라이즈 범위 데이터를 수집하고 관련 조직 설치를 열거한 다음, 선택한 체크포인트 경로 아래의 각 조직에 대해 자체 하위 디렉터리에서 기존 Invoke-GitHound 워크플로를 실행합니다.
예:
$session = New-GitHubJwtSession `
-EnterpriseName "your-enterprise-slug" `
-ClientId $clientId `
-PrivateKeyPath $privateKeyPath `
-InstallationId $enterpriseInstallationId `
-PersonalAccessToken $pat
Invoke-GitHoundEnterprise -Session $session -CheckpointPath "./output/your-enterprise" -CollectAll
관련 조직을 열거하지 않고 엔터프라이즈 전용 테스트를 수행하려면:
Invoke-GitHoundEnterprise -Session $session -CheckpointPath "./output/your-enterprise" -EnterpriseOnly

자세한 문서는 BloodHound Docs - GitHound Schema를 참조하세요.
주요 엣지 카테고리:
주요 공격 경로 패턴:
(:GH_User)-[:GH_HasRole|GH_MemberOf|GH_AddMember*1..]->(:GH_RepoRole)-[:GH_AdminTo|GH_CanPush]->(:GH_Repository)
대상 사용자의 개체 식별자를 찾습니다:
MATCH (n:GH_User)
RETURN n
힌트: 테이블 레이아웃 선택
https://github.com/user-attachments/assets/1ddfd075-2a15-4aa9-bad7-74c43e6c82d6
다음 쿼리에서 <object_id> 값을 사용자의 개체 식별자로 바꿉니다:
MATCH p = (:GH_User {objectid:"<object_id>"})-[:GH_MemberOf|GH_AddMember|GH_HasRole|GH_HasBaseRole|GH_Owns*1..]->(:GH_RepoRole)-[:GH_WriteRepoContents]->(:GH_Repository)
RETURN p

대상 저장소의 개체 식별자를 가져옵니다:
MATCH (n:GH_Repository)
RETURN n
대상 저장소의 개체 식별자를 가져와 다음 쿼리의 <object_id> 값으로 바꿉니다:
MATCH p = (:GH_User)-[:GH_MemberOf|GH_HasRole|GH_HasBaseRole|GH_Owns|GH_AddMember*1..]->(:GH_RepoRole)-[:GH_WriteRepoContents]->(:GH_Repository {objectid:"<object_id>"})
RETURN p

MATCH p = (:GH_User)-[:GH_HasRole|GH_HasBaseRole]->(:GH_OrgRole {short_name: "owners"})
RETURN p

MATCH p = (:AZUser)-[:GH_SyncedTo]->(:GH_User)
RETURN p

Azure 페더레이션 ID(OIDC 신뢰 관계)를 가정할 수 있는 GitHub 엔터티를 찾습니다:
// 모든 GitHub → Azure OIDC 공격 경로
MATCH p = (:GH_Repository|GH_Branch|GH_Environment)-[:GH_CanAssumeIdentity]->(:AZFederatedIdentityCredential)
RETURN p
// GitHub Actions를 통해 Azure로 연결되는 경로가 있는 사용자
MATCH p = (:GH_User)-[:GH_HasRole|GH_MemberOf|GH_AddMember*1..]->(:GH_RepoRole)-[:GH_CanPush]->(:GH_Repository)-[:GH_CanAssumeIdentity]->(:AZFederatedIdentityCredential)
RETURN p
MATCH p = (:GH_Repository)-[:GH_HasSecret]->(:GH_OrgSecret)
RETURN p
MATCH p = (:GH_Repository)-[:GH_Contains]->(:GH_SecretScanningAlert)
RETURN p
귀하의 기여를 환영하고 감사드립니다! 프로세스를 원활하고 효율적으로 만들기 위해 다음 단계를 따라주세요:
아이디어 논의
포크 및 브랜치 생성
이 저장소를 자신의 계정으로 포크하세요.
작업할 토픽 브랜치를 만드세요:
git checkout -b feat/my-new-feature
구현 및 테스트
저장소의 기존 스타일과 패턴을 따르세요.
변경 사항을 다루도록 테스트/예제를 추가하거나 업데이트하세요.
코드가 예상대로 실행되는지 확인하세요:
# 예: 수집기를 dot-source하여 실행하거나 BloodHound에서 model.json 로드
풀 리퀘스트 제출
브랜치를 포크에 푸시하세요:
git push origin feat/my-new-feature
이 저장소의 main 브랜치에 대해 풀 리퀘스트를 여세요.
PR 설명에 다음을 포함해주세요:
검토 및 병합
이 확장 기능을 개선하는 데 도움을 주셔서 감사합니다! 🎉
Copyright 2025 Jared Atkinson
Licensed under the Apache License, Version 2.0
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
하위 수준의 LICENSE 파일 또는 라이선스 헤더에 달리 명시되지 않는 한, 이 저장소의 모든 파일은 Apache-2.0 라이선스에 따라 제공됩니다. 라이선스 전문은 최상위 LICENSE 파일에서 확인할 수 있습니다.
| 카테고리 | 주요 엣지 | 설명 |
|---|
| 포함 관계 | GH_Contains, GH_Owns | 조직 계층 구조 |
| 역할 할당 | GH_HasRole, GH_MemberOf, GH_HasBaseRole | 누가 어떤 역할을 가지는지 |
| 저장소 권한 | GH_AdminTo, GH_CanPush, GH_CanPull | 역할이 할 수 있는 것 |
| 브랜치 보호 | GH_BypassPullRequestAllowances, GH_RestrictionsCanPush | 브랜치 수준 액세스 |
| 시크릿 | GH_HasSecret | 시크릿 액세스 매핑 |
| 교차 클라우드 | GH_CanAssumeIdentity, GH_SyncedTo | Azure/AWS 공격 경로 |