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
# 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.