Your forge's import wizard is a free proxy to your cloud metadata endpoint and config files
CVE-2026-59765 is a server-side request forgery in Gitea's repository migration feature affecting all versions before 1.27.0. Gitea enforces SSRF protections via hostmatcher.NewDialContext() on webhook and clone URLs, validating resolved IPs at the TCP dial level. However, three code paths — in modules/uri/uri.go, services/migrations/gitea_uploader.go, and services/migrations/dump.go — use Go's bare http.Get() / http.DefaultClient, completely bypassing the hostmatcher. An attacker with migration privileges (admin or org owner) can craft release assets or PR patch URLs pointing to file:///etc/gitea/app.ini (exposing DB credentials, JWT signing secrets, SMTP passwords) or http://169.254.169.254/latest/meta-data/iam/security-credentials/ (stealing cloud IAM tokens). The fetched content lands in the migrated repo's release assets, readable by the attacker.
The vendor rates this MEDIUM at CVSS 6.8 (AV:N/AC:L/PR:H/UI:N/S:C/C:H/I:N/A:N). That score correctly captures the PR:H (high-privilege) prerequisite and the Scope:Changed / Confidentiality:High impact. But the vendor label under-weights what Gitea *is*: a self-hosted Git forge that frequently serves as the CI/CD backbone (Gitea Actions), artifact store, and secret vault for development teams. When an SSRF on this component leaks app.ini, the attacker gets database credentials and JWT signing keys — enough to forge admin tokens and pivot to supply-chain compromise. The 6.8 is defensible in isolation; for the role Gitea actually plays, HIGH is the right bucket.
5 steps from start to impact.
Attain migration-capable account
[service].DISABLE_REGISTRATION = false, [migrations].ALLOWED_DOMAINS unconfigured), a newly registered user may have migration rights depending on instance policy.- Valid account on target Gitea instance
- Migration permission (admin or org owner by default)
- Requires PR:H — not accessible to anonymous or low-privilege users
- Many production Gitea instances disable open registration
POST /repo/migrate or POST /api/v1/repos/migrate endpointsSet up malicious migration source
file:///etc/gitea/app.ini, http://169.254.169.254/latest/meta-data/iam/security-credentials/, or http://10.0.0.1:8500/v1/kv/secrets. The migration protocol trusts these URLs as legitimate asset sources.- Attacker-controlled external Gitea instance or compatible API
- Knowledge of internal target URLs (cloud metadata is deterministic)
- Attacker must host infrastructure; not a single-packet exploit
- Cloud metadata theft only works against IMDSv1 — IMDSv2 requires a PUT hop that raw GET cannot perform
169.254.169.254 should trigger egress monitoring alertsTrigger migration via raw http.Get() path
uri.Open() or direct http.Get() in gitea_uploader.go and dump.go. These calls use Go's DefaultClient with no hostmatcher validation, so the request goes straight to the internal target. The file:// scheme handler in uri.Open() reads local files directly.- Migration initiated by the privileged user
- Target Gitea instance has network path to internal resources or local filesystem
- The migration must complete without timeout or error for the payload to land
- File-system reads are limited to files readable by the Gitea process user
/etc/gitea/app.iniExfiltrate secrets from migrated assets
- Migration completes successfully
- Attacker has read access to the migrated repository
- Large responses may be truncated or cause migration errors
- Cloud IAM tokens are temporary (typically 1-6 hours)
Pivot to platform or cloud compromise
app.ini secrets, the attacker forges JWT admin tokens or connects directly to the backing database, achieving full Gitea platform compromise. With cloud IAM credentials, they enumerate and access S3 buckets, EC2 instances, RDS databases, or other cloud resources attached to the Gitea instance's IAM role. From full Gitea admin, they can inject malicious commits, alter CI/CD pipelines, and poison downstream builds.- Exfiltrated credentials are valid and unexpired
- Target infrastructure uses the compromised credentials without additional auth barriers (MFA, IP allowlisting)
- Well-configured cloud environments use IMDSv2, VPC endpoints, and role scoping that limit lateral movement
- Database access may require network-level access the attacker doesn't have
The supporting signals.
| In-the-Wild Exploitation | No known active exploitation. Not listed on CISA KEV. No reports of campaigns targeting this specific CVE as of 2026-08-12. |
|---|---|
| Proof of Concept | Conceptual PoC described in the advisory and Kodem analysis — crafting a malicious migration source with release assets pointing to file:// or metadata URLs. No weaponized public exploit tool identified. The attack is straightforward to reproduce for anyone with admin access. |
| EPSS Score | Not yet scored — CVE disclosed 2026-07-21, EPSS data may lag by several weeks for new CVEs. |
| KEV Status | Not listed. No CISA KEV entry as of 2026-08-12. |
| CVSS Vector | CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:C/C:H/I:N/A:N — 6.8 MEDIUM. Key: network-accessible, low complexity, but requires high privileges. Scope is Changed (impact extends beyond the vulnerable component). Confidentiality impact is High. |
| Affected Versions | All Gitea versions < 1.27.0. The vulnerable code paths exist in modules/uri/uri.go, services/migrations/gitea_uploader.go, services/migrations/dump.go, and routers/web/auth/oauth.go. |
| Fixed Version | Gitea 1.27.0 (released 2026-07-22). Fix applied via PR #38406. No backport to 1.26.x branch has been announced. |
| Exposure Data | ~34,000 internet-facing Gitea instances per Shodan scans (SecurityWeek, May 2026). Separate Docker-specific scan found ~6,200 instances. Many enterprises run Gitea internally behind VPN/firewall. |
| Disclosure Date | 2026-07-21 (MITRE CNA submission). Gitea 1.27.0 released 2026-07-22. |
| Reporters | Multiple researchers: @tikket1, @theluckystrike, @Letian-aarch64, @JebeenLee, @JLLeitschuh, @pick, @noobx123 — coordinated disclosure via Gitea security program. |
noisgate verdict.
Role multiplier — Gitea is canonically a source-code forge, CI/CD pipeline hub, and secret store; SSRF-to-credential-theft on this component ends in platform takeover and supply-chain pivot, flooring the verdict at HIGH. The PR:H requirement (admin or org owner) prevents escalation to CRITICAL, as the attacker population is narrowed to a small set of privileged insiders or accounts obtained through prior compromise.
Why this verdict
- PR:H narrows attacker population: The CVSS vector confirms high-privilege access is required. Default Gitea configuration restricts migration to admins and org owners — this is not an unauthenticated or low-privilege bug, which limits mass exploitation.
- Scope:Changed multiplies impact beyond the host: The SSRF reads files and services outside Gitea's own authorization boundary — cloud IAM credentials, database connection strings, and JWT signing secrets in
app.ini. This is not a contained information disclosure. - Role multiplier — CI/CD forge is a high-value target by definition: Gitea sits in the CI/CD + artifact stores category. ≥10% of Gitea installations serve as the primary development platform with Actions runners, making them the CI/CD backbone. Credential theft from
app.iniyields database access and admin token forgery → full platform compromise → malicious commit injection → supply-chain pivot. This floors the verdict at HIGH per the deployment-role blast radius rule. - ~34,000 internet-facing instances provide attack surface: Shodan data confirms significant external exposure. While many enterprise Gitea instances are internal-only, the external population is non-trivial and each is a potential target if the attacker can obtain or already holds an admin account.
- IMDSv2 adoption limits cloud metadata theft: AWS environments enforcing IMDSv2 are immune to the metadata theft vector (raw GET cannot complete the PUT token hop). This friction applies to the cloud-pivot path specifically but does not reduce the local file-read (
file:///etc/gitea/app.ini) vector.
Why not higher?
CRITICAL would require either unauthenticated access or active exploitation. This CVE demands PR:H (admin/org owner), which means the attacker is either a malicious insider or has already compromised a high-privilege account through a separate chain. The bug amplifies an existing privileged position rather than granting initial access. No KEV listing and no observed campaigns further weigh against CRITICAL.
Why not lower?
MEDIUM ignores the deployment-role reality. Gitea is not a peripheral tool — it is the source-code repository, CI/CD engine, and often the secret store for development organizations. The SSRF reads app.ini containing database credentials and JWT signing secrets, which is a direct path to full platform takeover. The Scope:Changed designation in the CVSS vector confirms impact extends beyond the component. Downgrading past HIGH would require evidence that <1% of Gitea installations occupy the CI/CD high-value role, which contradicts Gitea's entire market position.
What to do — in priority order.
- Enforce IMDSv2 on all cloud instances running Gitea — IMDSv2 requires a PUT request to obtain a session token before metadata is accessible. The raw
http.Get()in the vulnerable code paths cannot complete this handshake, fully neutralizing the cloud metadata theft vector. Deploy within the noisgate mitigation SLA of 30 days for HIGH. - Restrict migration permissions to a dedicated admin account — Set
[service].ENABLE_NOTIFY_MAIL = falseand review[migrations]config. Ensure only a single, tightly-controlled admin account can perform migrations. This shrinks the attacker population to near-zero for external threat actors. Implement within 30 days. - Block outbound requests from Gitea to RFC 1918 and link-local ranges — Configure egress firewall rules (iptables, security groups, or network policy) on the Gitea host to deny outbound connections to
169.254.169.254,10.0.0.0/8,172.16.0.0/12, and192.168.0.0/16from the Gitea process. This breaks both the cloud metadata and internal network scanning vectors at the network layer. - Disable open registration if not required — Open registration with default migration permissions gives external attackers a path to PR:H. Set
[service].DISABLE_REGISTRATION = trueorREGISTER_MANUAL_CONFIRM = trueto prevent self-service account creation. This eliminates the external attacker's easiest path to the required privilege level. - Rotate secrets in app.ini post-patch — After upgrading to 1.27.0, rotate the
JWT_SECRET,INTERNAL_TOKEN,SECRET_KEY, database password, and any SMTP credentials stored inapp.ini. If the SSRF was exploited before patching, these secrets are compromised. Treat this as a mandatory post-remediation step.
- WAF rules on inbound requests — the SSRF is triggered by Gitea's own outbound HTTP client during migration, not by a malicious inbound payload. A WAF in front of Gitea cannot inspect or block the server-side request.
- Gitea's built-in hostmatcher allowlist/blocklist — this is precisely what the vulnerability bypasses. The three affected code paths do not use
hostmatcher.NewDialContext(), so configuring[migrations].ALLOWED_DOMAINSorBLOCKED_DOMAINShas no effect on these specific paths until you upgrade to 1.27.0. - Network segmentation alone (without egress filtering) — segmenting Gitea into its own VLAN helps, but if the Gitea host can still reach its own
localhostor the cloud metadata endpoint, the most damaging vectors remain viable.
Crowdsourced verification payload.
Run this script on the Gitea host (or any host where you can query the Gitea API) as any user. It checks the Gitea version via the API. Usage: bash check_cve_2026_59765.sh https://gitea.example.com. No special privileges required — uses the public /api/v1/version endpoint.
#!/usr/bin/env bash
# check_cve_2026_59765.sh — Detect CVE-2026-59765 (Gitea SSRF via migration asset downloads)
# Usage: bash check_cve_2026_59765.sh <GITEA_BASE_URL>
# Exit codes: 1 = VULNERABLE, 0 = PATCHED, 2 = UNKNOWN
set -euo pipefail
if [ $# -lt 1 ]; then
echo "Usage: $0 <GITEA_BASE_URL>"
echo "Example: $0 https://gitea.example.com"
exit 2
fi
BASE_URL="${1%/}"
API_URL="${BASE_URL}/api/v1/version"
# Fetch version
RESPONSE=$(curl -sf --max-time 10 "$API_URL" 2>/dev/null) || {
echo "UNKNOWN — Could not reach Gitea API at $API_URL"
exit 2
}
VERSION=$(echo "$RESPONSE" | grep -oP '"version"\s*:\s*"\K[^"]+' 2>/dev/null) || {
echo "UNKNOWN — Could not parse version from API response"
exit 2
}
echo "Detected Gitea version: $VERSION"
# Compare version — vulnerable if < 1.27.0
# Extract major.minor.patch
IFS='.' read -r MAJOR MINOR PATCH <<< "$(echo "$VERSION" | grep -oP '^[0-9]+\.[0-9]+\.[0-9]+')"
if [ -z "$MAJOR" ] || [ -z "$MINOR" ]; then
echo "UNKNOWN — Could not parse version components from '$VERSION'"
exit 2
fi
PATCH=${PATCH:-0}
if [ "$MAJOR" -lt 1 ]; then
echo "VULNERABLE — Gitea $VERSION is affected by CVE-2026-59765 (fixed in 1.27.0)"
exit 1
elif [ "$MAJOR" -eq 1 ] && [ "$MINOR" -lt 27 ]; then
echo "VULNERABLE — Gitea $VERSION is affected by CVE-2026-59765 (fixed in 1.27.0)"
exit 1
else
echo "PATCHED — Gitea $VERSION includes the fix for CVE-2026-59765"
exit 0
fiIf you remember one thing.
app.ini (JWT_SECRET, INTERNAL_TOKEN, SECRET_KEY, database and SMTP credentials) as a precaution. No active exploitation is known, but the attack requires zero tooling beyond a crafted migration source — do not wait for a weaponized exploit to appear.Sources
- GitLab Advisory Database — CVE-2026-59765
- Kodem — CVE-2026-59765 Analysis
- Gitea Blog — Release of 1.27.0
- GitHub Advisory — GHSA-2wm4-vwp6-v7xc
- SecurityWeek — Gitea Vulnerability Exposed 30,000 Deployments
- Miggo Vulnerability Database — CVE-2026-59765
- Hive Security — Gitea/Forgejo CVE Analysis
- AlternativeTo — Gitea 1.27.0 Security Fixes
What defenders are saying.
Crowdsourced verification outputs.
Results submitted by users who ran the verification payload against their environment.