SSRF via project import (remote_attachment_url)
GitLab's project-import feature fetched a remote attachment URL server-side without sufficient validation, enabling SSRF against internal services - a great example of how import/integration features expand the attack surface.
Read the original HackerOne reportGitLab's project import feature fetched a remote attachment URL on the server's behalf with no meaningful validation - a $10,000 SSRF that gave a researcher access to GitLab's internal services straight from a legitimate project import request.
Stay Legal
This breakdown is for educational purposes and understanding real-world vulnerabilities. Only test techniques like these on systems you own or have explicit written authorization to assess.
The Target
GitLab is a self-hosted and cloud-hosted DevOps platform used by millions of developers and enterprises. Its project import feature allows users to migrate repositories and their associated data - issues, merge requests, attachments - from external sources. As part of importing a project, GitLab's server accepted a remote_attachment_url parameter and fetched the resource at that URL server-side to incorporate it into the imported project.
This server-side fetch, performed by GitLab's backend without sufficient validation of the destination address, was a textbook SSRF vector in a particularly sensitive context: GitLab's infrastructure hosts CI/CD secrets, access tokens, repository data, and typically runs on a network with privileged access to internal services.
The Vulnerability
The vulnerability was a Server-Side Request Forgery (SSRF) via the remote_attachment_url parameter in the project import API. GitLab's import processing accepted user-supplied URLs for attachment resources and fetched them without applying adequate SSRF protections - specifically without blocking requests to private IP ranges, loopback addresses, or cloud metadata endpoints.
GitLab had SSRF protections in other parts of its codebase (for example, the webhook and integration URL validators), but the project import code path either bypassed or did not share those protections. Security controls applied inconsistently across a codebase create exactly this kind of gap: the security team believes SSRF is blocked everywhere, while individual code paths quietly lack the shared validation.
In the GitLab.com context, the backend server could reach the EC2 metadata service, internal GitLab services (such as internal APIs, Consul, Redis), and any host on the internal VPC network.
How It Was Found
The researcher examined the project import API and identified the remote_attachment_url parameter as a server-side fetch that was worth probing. The discovery workflow followed the standard SSRF methodology: confirm the server makes the fetch using an out-of-band callback, then probe internal addresses.
An illustrative import API request with the SSRF payload:
POST /api/v4/projects/import HTTP/1.1
Host: gitlab.com
Authorization: Bearer <personal_access_token>
Content-Type: multipart/form-data; boundary=----Boundary
------Boundary
Content-Disposition: form-data; name="path"
test-import-project
------Boundary
Content-Disposition: form-data; name="remote_attachment_url"
http://169.254.169.254/latest/meta-data/iam/security-credentials/
------Boundary--With SSRF confirmed, the researcher could probe the internal network, enumerate the metadata endpoint, and access GitLab's internal services - any of which could contain secrets or provide further privilege escalation.
Impact
- Direct access to GitLab's internal network from an authenticated but otherwise unprivileged user account.
- Potential retrieval of AWS IAM credentials from the EC2 metadata service, enabling lateral movement across GitLab's cloud infrastructure.
- Access to internal GitLab services - Workhorse (GitLab's HTTP proxy), Gitaly (Git RPC service), Redis, Consul, and any internal APIs - which may expose secrets, configuration data, or allow further exploitation.
- In a DevOps platform context, compromising internal services could expose CI/CD secrets, private repository contents, deployment keys, and customer project data.
- GitLab awarded a bounty of $10,000, reflecting the critical severity of internal network access on a platform hosting sensitive developer infrastructure.
The Fix
GitLab addressed the vulnerability by applying consistent SSRF protections to the project import code path:
- Apply SSRF validation to every server-side URL fetch - use a shared, centralized URL validation library so that protections cannot be accidentally omitted from new code paths.
- Block private IP ranges and link-local addresses at the validator level:
127.0.0.0/8,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,169.254.0.0/16, and::1. - Re-resolve DNS after initial validation to prevent DNS rebinding attacks where an attacker-controlled domain initially resolves to a public IP but later resolves to an internal one.
- Enforce IMDSv2 on all GitLab.com EC2 instances to prevent single-request metadata access.
- Segment the internal network - even if SSRF is achieved, the application server should not have a direct route to sensitive internal services like Redis or Consul.
- Security review new code paths that involve outbound HTTP requests as a standard part of the code-review process.
What You Can Learn
- SSRF protections must be applied consistently. A single unprotected code path undoes all the protections elsewhere in the application; centralized validation libraries and mandatory security review gates are essential.
- Import and migration features are high-value SSRF targets. They accept arbitrary external URLs by design and are often developed with less security scrutiny than core authentication or API features.
- The internal network is the real target. Once SSRF is confirmed, the question is not "does the server fetch my URL?" but "what can the server reach that I cannot?"
- DevOps platforms amplify SSRF impact. A GitLab server has privileged access to source code, secrets, CI/CD pipelines, and deployment credentials - SSRF on a platform like this is closer to supply-chain compromise than a typical web bug.
- DNS rebinding can bypass IP-based SSRF filters. A hostname that resolves to a public IP at validation time but to
127.0.0.1at fetch time defeats simple resolve-and-check approaches; the fix is to resolve once, use the resolved IP for the actual fetch, and re-validate the IP.
Canonical Report
Full technical details are in the original HackerOne disclosure: HackerOne #826361 - SSRF via remote_attachment_url in GitLab project import ($10,000)
Learn the skill behind it
Server-Side Request Forgery