GitLab CE 19.4.1 Gitaly fetch SSRF에 대한 개념 증명 및 실습 환경: GitHub/Bitbucket importer가 resolved_address 고정을 건너뛰어, 인증된 사용자가 DNS를 리바인딩하여 내부 호스트를 가져올 수 있습니다.
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · gitlab-gitaly-fetch-ssrf
GitLab CE 19.4.1 - GitLab Inc.
GitHub Enterprise / Bitbucket Server 가져오기(그리고 해당 플래그가 켜져 있을 때 Direct Transfer)는 Projects::ImportService#get_resolved_address를 건너뜁니다. Repository#fetch_as_mirror는 resolved_address: ""를 기본값으로 사용합니다. Gitaly FetchRemote / CreateRepositoryFromURL은 클론 호스트 이름을 다시 확인합니다. UrlBlocker는 이미 공용 A 레코드를 허용했습니다. 리바인드하면 git-HTTP 객체가 가져온 프로젝트에 들어갑니다.
GitHub Enterprise 가져오기를 실행할 수 있는 로그인한 사용자는 Gitaly가 더 이상 UrlBlocker가 고정한 IP가 아닌 호스트를 git-fetch하도록 만들 수 있습니다.
| ID | 아직 CVE 없음 |
| CWE | CWE-918, CWE-367 |
| CVSS | High: 7.7 CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:C/C:H/I:L/A:N |
| 제품 | GitLab CE |
| 영향받는 버전 | 19.4.1-ce.0 (v19.4.1-ee 트리의 26212baacadb) |
| 인증 | 인증된 사용자, GitHub Enterprise / Bitbucket Server importer |
| 라이선스 | GNU Affero GPL v3.0 |
| 랩 | 127.0.0.1 전용 |
GitHub Enterprise 가져오기를 자신이 제어하는 호스트 이름으로 지정합니다. Rails UrlBlocker.validate!는 공용 A 레코드를 허용합니다. ParallelImporter.imports_repository?가 true이므로 ImportService는 절대 고정하지 않습니다. RepositoryImporter#fetch_as_mirror는 빈 resolved_address로 Gitaly를 호출합니다. Gitaly git은 해당 이름이 지금 가리키는 대상을 HTTP-fetch합니다. 가져온 저장소에는 그 객체들이 들어 있습니다.
올인원 omnibus에서는 이것이 워커 루프백입니다. 직접 http://127.0.0.1 클론 호스트 이름은 fail closed됩니다. Repository-by-URL 가져오기는 이미 고정합니다. Direct Transfer는 bulk_import_enabled가 필요합니다(셀프 관리 기본값 false).
같은 제품, 형제로 남은 것들: Gitea HTTPS import pin drop, webhook 0.0.0.0/8.
19.4.1 패치 노트를 읽고, 그다음 importer를 읽었습니다. Projects::ImportService#get_resolved_address는 호스트 이름을 검증된 IP로 다시 쓰고 그것을 Gitaly로 전달합니다. GitHub 및 Bitbucket Server 병렬 importer는 imports_repository?를 설정하고 그 경로를 건너뜁니다.
project.repository.fetch_as_mirror(project.unsafe_import_url, refmap: refmap, forced: true)
fetch_as_mirror는 resolved_address: ""를 기본값으로 사용합니다. Direct Transfer의 repository_pipeline.rb는 심지어 validate!를 호출하고 반환 값을 버립니다.
기본 gitlab/gitlab-ce:19.4.1-ce.0을 세웠습니다. CE import_sources는 비어 있는 상태로 시작하며, 랩에서 GitHub를 활성화합니다. github_hostname=http://ghe.lab:18080으로 POST /api/v4/import/github. GitLab netns의 catcher는 가짜 GHE JSON과 git http-backend를 말합니다. 시드 저장소 blob WITNESS는 GITLAB-GITALY-FETCH-SSRF-WITNESS입니다.
GET /api/v4/projects/1/repository/files/WITNESS/raw?ref=master가 witness를 반환했습니다. Gitaly 로그: root/ghe-ssrf에서 FetchRemote grpc.code=OK. Catcher: git/2.55 info/refs 및 git-upload-pack. 부정 github_hostname=http://127.0.0.1:18080은 400 Invalid URL입니다.
이미 기록된 잘못된 시도들: 시드 CveLabRoot9!는 WeakPasswords 거부입니다(root 부분 문자열); 19.4는 그 검사를 64자에서 넘어가도록 패딩합니다; Faraday는 catcher가 아닌 공용 IP에 연결할 수 없으므로 하네스는 1.1.1.1:18080을 REDIRECT하는 동안 UrlBlocker는 여전히 공용 A를 봅니다; git-HTTP는 GET /repos/org/repo가 DNS를 뒤집기 전에 이길 수 있습니다; GitLab은 가져오기 중간에 502를 내고 그다음에도 파일은 거기에 있습니다. 리버스 셸. 연극. 오라클은 가져온 blob과 Gitaly FetchRemote입니다.
cd lab
./run.sh
오직 http://127.0.0.1:18410만 대상으로 합니다. GitLab CE는 몇 GB의 RAM을 요구합니다. 승인된 랩 전용.
negative-loopback status=400 Invalid URL
witness-import status=201 import_status=scheduled
file-final http=200 body='GITLAB-GITALY-FETCH-SSRF-WITNESS'
SUCCESS GITLAB-GITALY-FETCH-SSRF imported-witness git-fetch-no-pin GITLAB-GITALY-FETCH-SSRF-WITNESS
def import_repository
project.ensure_repository
refmap = Gitlab::GithubImport.refmap
project.repository.fetch_as_mirror(project.unsafe_import_url, refmap: refmap, forced: true)
repository-by-URL 가져오기가 이미 하는 방식대로 get_resolved_address를 fetch_as_mirror에 전달하십시오. Direct Transfer에서도 UrlBlocker.validate!를 버리지 마십시오.
repository_importer.rb · repository.rb · import_service.rbGNU Affero GPL v3.0. LICENSE 참조. 루프백 랩 전용. 보증 없음.