Skip to main content
Vulnerability Database/CVE-2026-103667

CVE-2026-103667: Gitea Container Registry XSS Vulnerability

CVE-2026-103667 is a cross-site scripting flaw in Gitea's container registry that allows attackers to execute malicious scripts in victim browsers. This post explains its impact, affected versions, and mitigation steps.

Published:

CVE-2026-103667 Overview

CVE-2026-103667 is a stored cross-site scripting (XSS) vulnerability [CWE-79] in Gitea's container registry. The registry serves blob downloads with a Content-Type taken directly from the media type declared in pushed image manifests. Blob responses lack a Content-Disposition header and do not enforce a restrictive content security policy. A user with push access to a container repository can publish a blob containing HTML and JavaScript tagged with a text/html media type. When an authenticated victim opens the blob URL in a browser, the script executes on the Gitea origin and can act on the victim's behalf, including creating API tokens.

Critical Impact

Authenticated attackers with container push permissions can execute JavaScript in victims' browser sessions and hijack Gitea accounts through API token creation.

Affected Products

  • Gitea container registry component
  • Gitea instances prior to version 28.0.0
  • Self-hosted Gitea deployments exposing the OCI/Docker registry endpoint

Discovery Timeline

  • 2026-10-06 - CVE-2026-103667 published to NVD
  • 2026-10-07 - Last updated in NVD database

Technical Details for CVE-2026-103667

Vulnerability Analysis

Gitea exposes an OCI-compatible container registry that stores blobs referenced by image manifests. When a client requests a blob, Gitea responds with the Content-Type recorded in the manifest's media type field. Browsers trust that header to decide how to render the response body.

Because Gitea does not override attacker-controlled media types, a pushed blob declared as text/html is rendered as a web page. The response is served from the same origin as the Gitea web UI, so any JavaScript it contains runs with full access to the victim's session cookies and CSRF tokens.

The response also omits a Content-Disposition: attachment header, which would force the browser to download rather than render the blob. No Content-Security-Policy restricts inline scripts on the registry route. Combined, these gaps turn the registry into a vector for stored XSS against authenticated users.

Root Cause

The root cause is improper neutralization of input during web page generation [CWE-79]. Gitea trusts the media type field supplied by a pushed manifest and reflects it into the response headers of blob downloads. The registry handler does not enforce a safe fixed Content-Type such as application/octet-stream, nor does it apply isolation headers that would prevent script execution on the Gitea origin.

Attack Vector

An attacker with permission to push images to any repository on the Gitea instance crafts an image manifest that references a blob of HTML with a text/html media type. The attacker then shares the blob URL with a target or embeds it where authenticated users will click. On visit, the browser renders the blob, executes the embedded JavaScript, and the script issues same-origin requests to Gitea APIs. Observed impact includes creation of API tokens that grant persistent access to the victim's account.

No verified public exploit code is available. Refer to the GitHub Security Advisory GHSA-7cq7-4v93-8wjm for the vendor's technical description.

Detection Methods for CVE-2026-103667

Indicators of Compromise

  • Container registry blob responses with Content-Type: text/html, text/javascript, application/xhtml+xml, or other script-executing MIME types
  • Image manifests pushed with non-standard media types that do not match conventional OCI or Docker descriptors
  • Unexpected creation of personal access tokens or OAuth applications shortly after a user accessed a /v2/<repo>/blobs/sha256:<digest> URL
  • Access logs showing authenticated user agents fetching registry blob URLs directly from a browser rather than a container client

Detection Strategies

  • Inspect reverse proxy or application logs for GET requests to /v2/.../blobs/sha256:... where the User-Agent is a browser rather than docker, containerd, or skopeo
  • Query the registry database for blob descriptors with media types outside the expected OCI set (for example, anything matching text/* or application/xhtml+xml)
  • Correlate blob fetches with subsequent calls to /api/v1/users/*/tokens or OAuth application creation endpoints

Monitoring Recommendations

  • Alert on new API tokens created within a short window after a blob fetch by the same session
  • Monitor for anomalous push activity from low-reputation or newly created accounts, especially pushes containing a single small blob
  • Enable audit logging for registry push and pull operations and forward events to a centralized analytics platform for retrospective review

How to Mitigate CVE-2026-103667

Immediate Actions Required

  • Upgrade Gitea to version 28.0.0 or later, which addresses the registry Content-Type handling
  • Review recently pushed container manifests for non-standard media types and remove suspicious blobs
  • Rotate API tokens and OAuth client secrets for users who may have opened untrusted registry URLs since the registry was exposed
  • Restrict container push permissions to trusted users and organizations until patching completes

Patch Information

The fix is included in Gitea v28.0.0. See the Gitea GitHub Release v28.0.0, the Gitea Blog Release Announcement, and the upstream Gitea GitHub Pull Request for the code changes that override attacker-controlled media types and add protective response headers.

Workarounds

  • Place Gitea behind a reverse proxy that rewrites responses on /v2/*/blobs/* to Content-Type: application/octet-stream and adds Content-Disposition: attachment
  • Add a strict Content-Security-Policy such as default-src 'none' on registry blob routes to block inline script execution
  • Serve the container registry on a separate hostname from the Gitea web UI so that script execution cannot access session cookies
  • Disable the container registry ([packages] ENABLED = false) if it is not required
bash
# Example nginx snippet to neutralize blob Content-Type
location ~ ^/v2/.+/blobs/ {
    proxy_pass http://gitea_upstream;
    proxy_hide_header Content-Type;
    add_header Content-Type "application/octet-stream" always;
    add_header Content-Disposition "attachment" always;
    add_header Content-Security-Policy "default-src 'none'" always;
    add_header X-Content-Type-Options "nosniff" always;
}

Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

Experience the Most Advanced Cybersecurity Platform

See how the world’s most intelligent, autonomous cybersecurity platform can protect your organization today and into the future.