CVE-2026-23603

CVE-2026-23603 is a low-severity server-side request forgery (SSRF) vulnerability in code.gitea.io/gitea (go), affecting versions < 1.27.0. It is fixed in 1.27.0.

Does this CVE actually affect you?

Kodem shows which CVEs are reachable and running in your applications, so you fix what's exploitable, not just what's listed.

Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.

Runtime intelligence, not another scanner.

Summary

Gitea: Blind SSRF in OAuth2 avatar synchronization via unvalidated OIDC picture claim

When [oauth2_client] UPDATE_AVATAR = true is enabled, Gitea fetches the avatar URL received from an OAuth2/OIDC provider using Go's default HTTP client. The URL comes from the user's OAuth/OIDC avatar value, commonly the OIDC picture claim.

The affected code path calls http.Get(url) without applying outbound host or IP restrictions. A low-privileged user who can influence their own picture claim under an already-configured OAuth2/OIDC source can cause the Gitea server to make arbitrary outbound HTTP GET requests. This includes requests to loopback addresses, RFC 1918 private network addresses, and IPv4 link-local addresses such as 169.254.169.254.

This is a blind SSRF by default. Impact can increase in deployments where the Gitea host can reach cloud metadata services, localhost-only services, or internal services that return valid image data.

Details

The vulnerable sink is in routers/web/auth/oauth.go:

func oauth2UpdateAvatarIfNeed(ctx *context.Context, url string, u *user_model.User) {
    if setting.OAuth2Client.UpdateAvatar && len(url) > 0 {
        resp, err := http.Get(url)
        if err == nil {
            defer func() { _ = resp.Body.Close() }()
        }
        if err == nil && resp.StatusCode == http.StatusOK {
            data, err := io.ReadAll(io.LimitReader(resp.Body, setting.Avatar.MaxFileSize+1))
            if err == nil && int64(len(data)) <= setting.Avatar.MaxFileSize {
                _ = user_service.UploadAvatar(ctx, u, data)
            }
        }
    }
}

The caller is in routers/web/auth/oauth_signin_sync.go:

func oauth2SignInSync(ctx *context.Context, authSourceID int64, u *user_model.User, gothUser goth.User) {
    oauth2UpdateAvatarIfNeed(ctx, gothUser.AvatarURL, u)
    ...
}

gothUser.AvatarURL is derived from the OAuth2/OIDC provider's avatar value. For OIDC providers, this is commonly populated from the picture claim returned by the provider's userinfo endpoint or ID token.

The issue is that this value can be attacker-influenced in some common IdP configurations, while Gitea fetches it server-side using http.Get with no host/IP validation and no restricted transport.

Comparable outbound fetch paths in Gitea use hostmatcher.NewDialContext to enforce restrictions at TCP dial time. For example, repository migration uses an HTTP transport with host matching. The OAuth2 avatar synchronization path does not apply those restrictions.

PoC

Requirements

  • Local Gitea build or binary
  • Python 3
  • Python packages: requests, pyjwt, cryptography
  • Gitea configured with OAuth2 avatar synchronization enabled

Install Python dependencies:

python3 -m pip install requests pyjwt cryptography

Configure app.ini:

[oauth2_client]
UPDATE_AVATAR = true
ENABLE_AUTO_REGISTRATION = true
USERNAME = userid

Run the fake OIDC provider:

python3 fake_oidc.py http://127.0.0.1:8888/ 9999

Run a listener for the SSRF target:

nc -lvnp 8888

Register an OAuth2 authentication source in Gitea:

  • Provider: OpenID Connect
  • Client ID: gitea-client
  • Client Secret: gitea-secret
  • OpenID Connect Auto Discovery URL: http://127.0.0.1:9999/.well-known/openid-configuration

Then initiate login through the configured OAuth2 source.

Observed request to the SSRF listener:

GET / HTTP/1.1
Host: 127.0.0.1:8888
User-Agent: Go-http-client/1.1
Accept-Encoding: gzip

Observe that Gitea fetched the OIDC picture claim URL from the server side using the default Go HTTP client.

Impact

When [oauth2_client] UPDATE_AVATAR = true is enabled, a low-privileged OAuth2/OIDC user who can influence their own picture claim can force the Gitea server to make outbound HTTP GET requests to attacker-selected URLs. This allows blind SSRF from the Gitea server’s network position, including requests to loopback addresses, RFC1918 private addresses, link-local addresses such as 169.254.169.254, and other internal services that may not be reachable from the public internet. In practical terms, this can enable internal service probing and interaction with localhost-only or private-network services depending on the deployment’s network access controls.

The vulnerability is blind in the common case because non-image responses such as HTML, JSON, or plaintext are rejected during avatar processing and are not directly returned to the attacker. However, impact can increase in cloud or internal-network deployments where metadata services, internal admin panels, monitoring endpoints, or image-generating internal services are reachable from the Gitea host. If an internal endpoint returns a valid supported image format within the configured avatar size limit, the response may be stored as the attacker’s avatar, creating a limited response retrieval primitive.

Untrusted input controls the target URL of a server-initiated request, which may reach internal services not otherwise accessible from outside. Typical impact: access to internal metadata services, internal APIs, or cloud credentials.

CVE-2026-23603 has a CVSS score of 3.1 (Low). The vector is network-reachable, low privileges required, and no user interaction. A CVSS score reflects the worst-case severity of the vulnerability, not your specific exposure. Whether this affects your application depends on whether the vulnerable code is present and reachable in your environment. A fixed version is available (1.27.0); upgrading removes the vulnerable code path.

Affected versions

code.gitea.io/gitea (< 1.27.0)

Security releases

code.gitea.io/gitea → 1.27.0 (go)

Kodem intelligence

Severity tells you how bad this could be in the worst case. It does not tell you whether you are exposed. Exploitability and impact are functions of runtime truth: whether the vulnerable code is present, reachable, and actually executes in your application. A vulnerable package can sit in your dependency tree and never run.

Kodem, an Intelligent Application Security platform, uses runtime intelligence to reveal which vulnerabilities actually execute in production, so teams prioritize the ones that genuinely matter. Kodem's runtime-powered SCA identifies whether this CVE is reachable in your applications.

Already deployed Kodem?

See it in your environmentNew to Kodem? Get a demo →

Remediation advice

Upgrade code.gitea.io/gitea to 1.27.0 or later to resolve this vulnerability.

Kodem Kai can prioritize this vulnerability in your dependency tree and generate a fix recommendation.

Frequently Asked Questions

  1. What is CVE-2026-23603? CVE-2026-23603 is a low-severity server-side request forgery (SSRF) vulnerability in code.gitea.io/gitea (go), affecting versions < 1.27.0. It is fixed in 1.27.0. Untrusted input controls the target URL of a server-initiated request, which may reach internal services not otherwise accessible from outside.
  2. How severe is CVE-2026-23603? CVE-2026-23603 has a CVSS score of 3.1 (Low). This score reflects the worst-case severity of the vulnerability, not your specific exposure. Whether it represents real risk in your environment depends on whether the vulnerable code is present and reachable.
  3. Which versions of code.gitea.io/gitea are affected by CVE-2026-23603? code.gitea.io/gitea (go) versions < 1.27.0 is affected.
  4. Is there a fix for CVE-2026-23603? Yes. CVE-2026-23603 is fixed in 1.27.0. Upgrade to this version or later.
  5. Is CVE-2026-23603 exploitable, and should I be worried? Whether CVE-2026-23603 is exploitable in your environment depends on whether the vulnerable code is present and reachable. A CVSS score is a worst-case rating; it does not account for your specific deployment, configuration, or usage patterns. Kodem, an Intelligent Application Security platform, uses runtime intelligence to show which vulnerabilities actually execute in production, so you can focus on the ones that represent real risk. Get a demo
  6. What actually determines whether CVE-2026-23603 is exploitable, and how bad it is? Exploitability and impact are not fixed properties of a CVE. They depend on runtime truth: whether the vulnerable code is present, reachable, and actually executes in your application. A high CVSS score on a dependency that never runs is not the same as real risk. Kodem, an Intelligent Application Security platform, uses runtime intelligence to reveal which vulnerabilities actually execute in production, so teams prioritize the ones that genuinely matter.
  7. How do I fix CVE-2026-23603? Upgrade code.gitea.io/gitea to 1.27.0 or later.

Other vulnerabilities in code.gitea.io/gitea

Stop the waste.
Protect your environment with Kodem.