Skip to content
KitploitKITPLOIT
ToolsExploitsBlog
Log in
Submit
ToolsExploitsBlog
Submit

Hacking, PenTest, and Cybersecurity Tools for Your Security Arsenal!

Kitploit is a directory of hacking, cybersecurity, and pentesting tools. Discover the latest project updates to find vulnerabilities, analyze systems, automate testing, and strengthen your security.

FeedsContactPrivacy© 2026 Kitploit

Tool Directory

Categories

View all categories
Loading categories
gitlab-gitaly-fetch-ssrf — Proof-of-concept and lab for a GitLab CE 19.4.1 Gitaly fetch SSRF: GitHub/Bitbucket importers skip resolved_address pinning, letting an authenticated user rebind DNS and fetch internal hosts. | Kitploit
Tools/GitHubGitHub/abraxas/gitlab-gitaly-fetch-ssrf
Vulnerability AnalysisExploitationWeb Application ExploitationSecurity VirtualizationPenetration TestingRed TeamingLabs & Practice
GitHub
abraxas/gitlab-gitaly-fetch-ssrf

gitlab-gitaly-fetch-ssrf

Proof-of-concept and lab for a GitLab CE 19.4.1 Gitaly fetch SSRF: GitHub/Bitbucket importers skip resolved_address pinning, letting an authenticated user rebind DNS and fetch internal hosts.

View Repository
215 days agoNot yet reviewed

Most Popular

View all →

Discover the most used tools by our community.

Explore all tools

Browse our collection of tools

View all tools →
Share

Abraxas Labs - gitlab-gitaly-fetch-ssrf

abraxaslabs.tech  ·  github.com/abraxas  ·  @abraxas_null  ·  [email protected]  ·  gitlab-gitaly-fetch-ssrf

gitlab-gitaly-fetch-ssrf

GitLab CE 19.4.1 - GitLab Inc.

GitHub Enterprise / Bitbucket Server import (and Direct Transfer, when that flag is on) skip Projects::ImportService#get_resolved_address. Repository#fetch_as_mirror defaults resolved_address: "". Gitaly FetchRemote / CreateRepositoryFromURL resolves the clone hostname again. UrlBlocker already allowed a public A-record. Rebind, and git-HTTP objects land in the imported project.

A signed-in user who can run GitHub Enterprise import can make Gitaly git-fetch a host that is no longer the IP UrlBlocker pinned.

IDno CVE yet
CWECWE-918, CWE-367
CVSSHigh: 7.7 CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:C/C:H/I:L/A:N
ProductGitLab CE
Affected19.4.1-ce.0 (26212baacadb on the v19.4.1-ee tree)
Authauthenticated user, GitHub Enterprise / Bitbucket Server importer
LicenseGNU Affero GPL v3.0
Lab127.0.0.1 only

What an attacker can do

Point GitHub Enterprise import at a hostname they control. Rails UrlBlocker.validate! allows a public A-record. ParallelImporter.imports_repository? is true, so ImportService never pins. RepositoryImporter#fetch_as_mirror calls Gitaly with an empty resolved_address. Gitaly git HTTP-fetches whatever that name is now. The imported repo contains the objects.

On all-in-one omnibus that is the worker loopback. Direct http://127.0.0.1 clone hostnames fail closed. Repository-by-URL import already pins. Direct Transfer needs bulk_import_enabled (self-managed default false).

Same product, sibling leftovers: Gitea HTTPS import pin drop, webhook 0.0.0.0/8.

How I found it

I read the 19.4.1 patch notes, then importers. Projects::ImportService#get_resolved_address rewrites the hostname to the validated IP and passes it into Gitaly. GitHub and Bitbucket Server parallel importers set imports_repository? and skip that path.

project.repository.fetch_as_mirror(project.unsafe_import_url, refmap: refmap, forced: true)

fetch_as_mirror defaults resolved_address: "". Direct Transfer's repository_pipeline.rb even calls validate! and discards the return value.

I stood up stock gitlab/gitlab-ce:19.4.1-ce.0. CE import_sources starts empty; the lab enables GitHub. POST /api/v4/import/github with github_hostname=http://ghe.lab:18080. Catcher in the GitLab netns speaks fake GHE JSON plus git http-backend. Seed repo blob WITNESS is GITLAB-GITALY-FETCH-SSRF-WITNESS.

GET /api/v4/projects/1/repository/files/WITNESS/raw?ref=master returned the witness. Gitaly log: FetchRemote grpc.code=OK on root/ghe-ssrf. Catcher: git/2.55 info/refs and git-upload-pack. Negative github_hostname=http://127.0.0.1:18080 is 400 Invalid URL.

Wrong turns already recorded: seed CveLabRoot9! is a WeakPasswords reject (root substring); 19.4 pads past that check at 64 chars; Faraday cannot connect to a public IP that is not the catcher, so the harness REDIRECTs 1.1.1.1:18080 while UrlBlocker still sees a public A; git-HTTP can win before GET /repos/org/repo flips DNS; GitLab 502s mid-import and then the file is there anyway. A reverse shell. Theatre. The oracle is the imported blob plus Gitaly FetchRemote.

Lab

cd lab
./run.sh

Target only http://127.0.0.1:18410. GitLab CE wants several GB RAM. Authorized lab only.

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

The leftover

def import_repository
  project.ensure_repository
  refmap = Gitlab::GithubImport.refmap
  project.repository.fetch_as_mirror(project.unsafe_import_url, refmap: refmap, forced: true)

Pass get_resolved_address into fetch_as_mirror the way repository-by-URL import already does. Do not discard UrlBlocker.validate! in Direct Transfer either.

References

  • gitlab.com/gitlab-org/gitlab tag v19.4.1-ee
  • repository_importer.rb · repository.rb · import_service.rb
  • Same product: gitlab-gitea-import-ssrf · gitlab-webhook-0net-ssrf
  • CWE-918 · CWE-367

License

GNU Affero GPL v3.0. See LICENSE. Loopback lab only. No warranty.

Download Tool