Like giving a janitor with a master key a periscope into every room in the building
CVE-2026-34966 is a server-side request forgery in Gitea < 1.27.0. Three code paths — migration release-asset downloads, pull-request patch fetches, and OAuth2 avatar synchronization — call Go's bare http.Get() instead of the custom httplib client with Gitea's hostmatcher SSRF guard. An attacker who already holds admin or organization-owner privileges can craft a migration that points asset-download URLs at http://169.254.169.254/latest/meta-data/…, file:///data/gitea/conf/app.ini, or any RFC-1918 address. The response body is persisted as a release asset the attacker can later download, turning a blind SSRF into a full-read SSRF with data exfiltration. All Gitea versions from at least 1.20 through 1.26.x are affected; the fix shipped in 1.27.0 on 12 July 2026.
The vendor scored this 7.6 HIGH (CVSS 3.1) and 8.3 HIGH (CVSS 4.0). That rating is directionally correct but slightly generous for most real deployments. The PR:H requirement means the attacker must already control an admin-level Gitea account — a position that already grants read/write to every repository and every CI secret stored in Gitea. The SSRF's incremental value is lateral movement to cloud instance-metadata credentials and internal services, which is serious in cloud-hosted CI environments but conditional on IMDSv1 being available and internal services being unauthed. We shave ~0.8 points off the vendor score but keep the verdict firmly at HIGH because Gitea is, by definition, a CI/CD and source-code management component where a successful chain can cascade to supply-chain compromise.
4 steps from start to impact.
Obtain Gitea admin or org-owner session
PR:H — a Gitea account with admin or organization-owner privileges. This could come from credential stuffing, a prior phishing campaign, a leaked API token, or an insider threat. Without this, the SSRF is unreachable.- Valid admin or org-owner Gitea credentials or API token
- Network access to the Gitea web UI or API (typically port 3000 or reverse-proxied on 443)
- Admin accounts are a small fraction of total Gitea users
- MFA, if enforced, blocks credential-stuffing
- SSO/OIDC login flows add a second IdP layer the attacker must also defeat
user.token audit eventsCreate a malicious migration manifest
POST /api/v1/repos/migrate) or the web UI's 'New Migration' flow to import a repository. The migration payload includes release-asset download URLs pointing at internal targets — e.g., http://169.254.169.254/latest/meta-data/iam/security-credentials/ for AWS metadata, or file:///data/gitea/conf/app.ini for the Gitea config file containing DB credentials, JWT signing secrets, and internal OAuth2 client secrets.- Admin session from Step 1
- Migration feature enabled (default: enabled for admins)
- Gitea instances behind corporate proxies may not route to 169.254.169.254
- IMDSv2 (token-required) blocks GET-only SSRF to AWS metadata
- Non-cloud (bare-metal / VM) deployments have no metadata endpoint to hit
169.254.169.254 or file:// in request bodies can catch the payloadGitea fetches attacker-controlled URLs via raw http.Get
http.Get() without a custom DialContext, Gitea's hostmatcher SSRF allowlist/blocklist is never consulted. The request goes directly to the attacker-specified URL, including RFC-1918 addresses, link-local metadata IPs, and file:// URIs.- Vulnerable Gitea version < 1.27.0
- Network-level egress filtering or microsegmentation may block Gitea from reaching internal services
- Host-based firewall on the Gitea server blocking outbound to 169.254.169.254
Exfiltrate response as a release asset
- SSRF response returns useful data (not a 403 or empty body)
- Attacker retains access to download the release asset
- Cloud IAM roles scoped to least-privilege limit blast radius of stolen credentials
- STS credential lifetime (typically 1-6 hours) limits the exploitation window
- CloudTrail / GCP Audit Logs flag credential use from unexpected source IPs
AssumeRole / GetCallerIdentity from anomalous IP; Gitea release-asset download audit logThe supporting signals.
| In-the-Wild Exploitation | Not observed. No KEV listing, no known campaigns as of 2026-08-12. No GreyNoise tags for this CVE. |
|---|---|
| Proof of Concept | No public PoC repository identified. The attack is straightforward for anyone familiar with Gitea's migration API — no novel tooling required beyond curl. |
| EPSS Score | 0.00314 (~31st percentile) — low predicted exploitation probability in the next 30 days. |
| KEV Status | Not listed on CISA KEV as of 2026-08-12. |
| CVSS Vector | CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:C/C:H/I:L/A:N — network-attackable, low complexity, but requires high privileges. Changed scope reflects SSRF reaching beyond Gitea itself. |
| Affected Versions | All Gitea versions < 1.27.0 (at least back to 1.20.x). Forgejo forks may also be affected — check Forgejo advisories separately. |
| Fixed Version | Gitea 1.27.0 (released 2026-07-12). Patch commit: b969123b7fac51c88daab5cb64e5b2f4abd53288. |
| Exposure Data | Shodan shows ~34,000 internet-facing Gitea instances globally. Roughly 4,000 appear to be production systems on major cloud/VPS platforms (SecurityWeek). |
| Disclosure Date | 2026-08-05 (CVE reserved 2026-03-31). Gitea 1.27.0 shipped the fix on 2026-07-12, ~3 weeks before public disclosure. |
| Reporter | Khaled Nassar via Gitea's security advisory process (GHSA-2wm4-vwp6-v7xc). |
noisgate verdict.
The single most decisive factor is the PR:H (admin-level) authentication requirement, which limits the reachable attacker population to those who have already compromised a Gitea administrator account — a prerequisite that itself represents a significant prior breach. However, Gitea's canonical deployment role as a CI/CD source-code forge means a successful chain can cascade to cloud-credential theft and supply-chain compromise, which floors the verdict at HIGH despite the access friction.
Why this verdict
- PR:H gate is real friction. The attacker must already hold admin or org-owner privileges on Gitea. This is not a low-privilege or unauthenticated bug — it assumes a prior significant compromise of the Gitea identity layer, compounding downward pressure by ~0.8 points from the vendor's 7.6.
- Role multiplier: CI/CD source-code forge (HIGH floor). Gitea is canonically a CI/CD component. In cloud-hosted deployments, SSRF to
169.254.169.254yields IAM credentials whose blast radius can be fleet-scale (S3, EC2, Lambda). In on-prem deployments,file:///reads exposeapp.inicontaining DB credentials and JWT signing keys. Both outcomes are supply-chain-adjacent. This floors the verdict at HIGH. - Changed scope (S:C) is earned. The SSRF escapes the Gitea application boundary to reach cloud metadata services, internal APIs, and the local filesystem — resources the attacker's admin session was never intended to access.
- No exploitation in the wild and low EPSS (0.00314) mean there is no urgency multiplier pushing the score upward. The bug is a post-compromise lateral-movement primitive, not a first-stage entry vector.
Why not higher?
CRITICAL would require either active exploitation, unauthenticated access, or a scenario where the majority of Gitea installs are immediately exploitable. The PR:H requirement means the attacker has already breached an admin account — a non-trivial precondition. Additionally, IMDSv2 adoption on AWS (and equivalent on GCP/Azure) blocks the highest-impact cloud-metadata exfiltration path in modern deployments. The bug amplifies an existing compromise rather than creating one.
Why not lower?
Gitea is a supply-chain component by definition — it holds source code, CI secrets, and pipeline definitions. The SSRF's ability to read app.ini (DB creds, JWT secrets) and cloud IAM credentials from a single HTTP request represents a qualitative escalation beyond what even a Gitea admin should be able to do. Roughly 34,000 instances are internet-facing, with ~4,000 on cloud platforms where metadata SSRF is most dangerous. Downgrading to MEDIUM would undervalue the blast radius when the chain succeeds.
What to do — in priority order.
- Enforce IMDSv2 on all cloud instances hosting Gitea — IMDSv2 requires a PUT-based token exchange before metadata reads succeed. A GET-only SSRF like this one cannot obtain the token, completely neutralizing the cloud-credential theft path. Deploy within the noisgate HIGH mitigation SLA of 30 days if you cannot patch Gitea immediately.
- Disable repository migration for non-site-admin users — Set
[migrations] ALLOWED_DOMAINSto an empty list or setDISABLE_MIGRATIONS = trueinapp.inito eliminate the primary attack surface. This breaks legitimate migration workflows but removes the SSRF entry point entirely. - Enforce MFA on all Gitea admin accounts — The PR:H requirement means MFA on admin accounts directly raises the cost of reaching the vulnerable code path. Use TOTP or WebAuthn — not email-based OTP.
- Network-segment the Gitea server away from sensitive internal services — Place Gitea in a DMZ or isolated VLAN with egress filtering that blocks RFC-1918, link-local (169.254.0.0/16), and localhost destinations. This limits SSRF blast radius even if the vulnerability is triggered.
- Rotate app.ini secrets and DB credentials after patching — If you suspect exploitation, the SSRF may have already exfiltrated
app.ini. RotateJWT_SECRET,INTERNAL_TOKEN,SECRET_KEY,LFS_JWT_SECRET, and database passwords after upgrading to 1.27.0.
- WAF rules on the Gitea reverse proxy — the SSRF is server-initiated, not client-initiated. The malicious URL is in the migration API payload body, not in the request URL. Most WAFs won't inspect JSON body fields for SSRF patterns unless explicitly configured with custom rules.
- Gitea's built-in
[migrations] ALLOWED_DOMAINSallowlist — this CVE exists precisely because the vulnerable code paths bypass the hostmatcher. The allowlist is not consulted for the affectedhttp.Get()calls. Only upgrading to 1.27.0 fixes the bypass. - Rate limiting on the Gitea API — the attacker needs only a single migration request to exfiltrate data. Rate limiting does not meaningfully reduce risk.
Crowdsourced verification payload.
Run this on the Gitea server itself (or any host that can reach the Gitea API). Requires curl and jq. Invoke as: bash check_cve_2026_34966.sh https://gitea.example.com. No authentication needed — it checks the version endpoint.
#!/usr/bin/env bash
# check_cve_2026_34966.sh — Detect Gitea SSRF (CVE-2026-34966)
# Usage: bash check_cve_2026_34966.sh <GITEA_BASE_URL>
# Exit codes: 0 = PATCHED, 1 = VULNERABLE, 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%/}"
# Fetch Gitea version from the API
VERSION_JSON=$(curl -sf --max-time 10 "${BASE_URL}/api/v1/version" 2>/dev/null) || {
echo "UNKNOWN — could not reach ${BASE_URL}/api/v1/version"
exit 2
}
VERSION=$(echo "$VERSION_JSON" | jq -r '.version // empty' 2>/dev/null)
if [ -z "$VERSION" ]; then
echo "UNKNOWN — could not parse version from API response"
exit 2
fi
echo "Detected Gitea version: $VERSION"
# Compare versions: fixed in 1.27.0
# Extract major.minor.patch
IFS='.' read -r MAJOR MINOR PATCH <<< "$(echo "$VERSION" | sed 's/-.*//' | sed 's/+.*//')"
MAJOR=${MAJOR:-0}; MINOR=${MINOR:-0}; PATCH=${PATCH:-0}
FIXED_MAJOR=1; FIXED_MINOR=27; FIXED_PATCH=0
if [ "$MAJOR" -gt "$FIXED_MAJOR" ] 2>/dev/null; then
echo "PATCHED — version $VERSION >= 1.27.0"
exit 0
elif [ "$MAJOR" -eq "$FIXED_MAJOR" ] 2>/dev/null; then
if [ "$MINOR" -gt "$FIXED_MINOR" ] 2>/dev/null; then
echo "PATCHED — version $VERSION >= 1.27.0"
exit 0
elif [ "$MINOR" -eq "$FIXED_MINOR" ] 2>/dev/null; then
if [ "$PATCH" -ge "$FIXED_PATCH" ] 2>/dev/null; then
echo "PATCHED — version $VERSION >= 1.27.0"
exit 0
fi
fi
fi
echo "VULNERABLE — version $VERSION < 1.27.0 (CVE-2026-34966)"
exit 1If you remember one thing.
app.ini secrets (JWT_SECRET, INTERNAL_TOKEN, SECRET_KEY) and database credentials as a precaution.Sources
- CVE-2026-34966 Detail — THREATINT
- Gitea 1.27.0 Release Notes
- GHSA-2wm4-vwp6-v7xc — GitHub Security Advisory
- Gitea 1.27.0 Patches Major Security Flaws — AlternativeTo
- Gitea 1.27 Delivers 45 Security Fixes — LinuxSecurity
- Gitea Vulnerability Exposed 30,000 Deployments — SecurityWeek
- Gitea June 2026 Security Release Explained — Hive Security
What defenders are saying.
Crowdsourced verification outputs.
Results submitted by users who ran the verification payload against their environment.