SK쉴더스 로고
ADT캡스캡스홈
SK쉴더스

GitLab CI/CD 정규식 컴파일러 원격 코드 실행 취약점 (CVE-2026-97229)

EQST Now | 2026.10.14

NOW Briefing

Brief 1 GitLab CE/EE의 CI/CD 정규식 컴파일러에서 정수 오버플로우로 발생하는 원격 코드 실행 취약점(CVE-2026-97229)이 공개됐습니다.
Brief 2 공개 프로젝트에 푸시할 수 있는 공격자는 조작된 CI 설정과 UTF-8 경로를 올려 서버에서 git 사용자 권한으로 OS 명령을 실행할 수 있습니다.
Brief 3 자체 설치형 인스턴스는 19.4.2·19.3.5·19.2.8로 즉시 업그레이드해야 합니다. GitLab.com과 Dedicated는 조치 대상이 아닙니다.

‍

GitLab CI/CD 정규식 컴파일러 원격 코드 실행 취약점 (CVE-2026-97229)

‍

■ 개요

‍

GitLab은 소스 코드 저장소와 CI/CD(코드 변경을 자동으로 빌드·테스트·배포하는 파이프라인)를 한 제품에 묶은 개발 플랫폼입니다. 2026년 10월 12일 공개된 긴급 패치 릴리스 19.4.2·19.3.5·19.2.8은 CI/CD 정규식 컴파일러의 정수 오버플로우 취약점 CVE-2026-97229를 포함해 다수의 보안 취약점을 수정했습니다. 영향 대상은 조직이 자체 서버에 직접 설치해 운영하는 자체 설치형(self-managed) GitLab CE/EE이며, 19.2·19.3·19.4 계열의 패치 이전 버전이 모두 포함됩니다.

GitLab 공지 기준, 공격자는 조작된 정규식을 담은 CI/CD 설정으로 GitLab 서버에서 임의 코드를 실행할 수 있습니다. Rapid7은 이를 통제 환경에서 독립적으로 재현해 GitLab git 사용자 권한으로 OS 명령이 실행되는 것을 확인했습니다. 이 권한으로는 저장소 데이터와 CI/CD 설정에 직접 접근할 수 있습니다. 단 Rapid7 분석에 따르면 공격자는 먼저 대상 인스턴스의 공개 프로젝트 브랜치에 악성 설정을 심을 수단이 있어야 합니다. 누구나 계정을 만들고 프로젝트를 생성할 수 있도록 설정된 인스턴스에서는 이 전제가 일반 계정으로도 쉽게 충족됩니다.

CVSS 최고점을 받은 취약점이고, 재현 가능한 PoC 코드도 이미 공개됐습니다. Rapid7은 분석 시점 기준 실제 악용이 확인된 사례는 없다고 밝혔지만, 공개된 PoC가 사용하는 트리거는 외부에서 접근 가능한 API 호출이므로 외부 노출 인스턴스는 패치 전까지 공격 표면에 남습니다. GitLab.com은 이미 패치된 버전으로 운영 중이고 GitLab Dedicated 이용 조직은 별도 조치가 필요하지 않습니다.

‍

■ 요약

‍

항목 내용
CVE ID CVE-2026-97229 (CI/CD 정규식 컴파일러 정수 오버플로우)
CVSS 점수 CVSS v3.1 10.0
취약점 유형 CWE-190, 정수 오버플로우에 의한 원격 코드 실행
영향/위험 • GitLab git 사용자 권한으로 OS 명령 실행 가능(연구진이 통제 환경에서 재현함)
• 저장소 콘텐츠·CI/CD 설정 등 git 사용자 접근 범위의 자산 노출 가능
• 분석 공개 시점까지 실제 악용 사례는 보고되지 않음
취약 버전 • 19.2 이상 19.2.8 미만 (CE/EE)
• 19.3 이상 19.3.5 미만 (CE/EE)
• 19.4 이상 19.4.2 미만 (CE/EE)
패치 버전 • 19.2.8
• 19.3.5
• 19.4.2

‍

■ 기술 분석

‍

이 취약점은 정규식이 두 번 컴파일되는 과정에서 발생합니다. Rapid7 분석에 따르면 조작된 정규식은 처음에 US-ASCII 기반으로 컴파일돼 GitLab의 정규식 검증을 통과합니다. 이후 이 정규식이 UTF-8 문자가 포함된 저장소 경로와 비교될 때, 루비가 사용하는 정규식 엔진 Onigmo가 새 인코딩에 맞춰 같은 정규식을 다시 컴파일합니다. 재컴파일은 검증이 모두 끝난 뒤에 일어나며, 이 단계의 프로그램 크기 계산에서 정수 오버플로우(값이 자료형의 최대치를 넘어 작은 값으로 뒤집히는 현상)가 발생합니다.

크기 계산이 뒤집히면 엔진은 잘못된 정규식 명령을 생성하고, 그 결과 실행 흐름이 의도한 경로를 벗어납니다. Rapid7은 이 상태에서 공격자가 Onigmo 가상 머신의 동작을 조작해 루비 프로세스가 OS 명령을 실행하게 만들 수 있음을 확인했습니다. 메모리 주소 무작위화(ASLR)가 켜진 공식 GitLab CE 19.4.1 리눅스 amd64 이미지에서 코드 실행을 재현했으며, 같은 입력은 패치된 19.4.2 CE 이미지에서는 실행되지 않았습니다. 공개된 PoC는 파이프라인을 직접 실행시키지 않고, 설정 문법을 검사하는 CI lint API(/api/v4/projects/:id/ci/lint)를 호출해 정규식 평가를 유발합니다. GitLab 문서의 요청 예시는 접근 토큰 헤더를 사용하지만, 해당 코드는 공개 프로젝트를 대상으로 토큰 없이 GET 요청을 보냅니다.

GitLab 패치는 10월 12일 릴리스로 배포됐고, 취약점은 HackerOne 버그 바운티 제보로 접수됐습니다. 다만 GitLab 소스는 공개돼 있어도 확인한 공개 릴리스 노트와 저장소 범위에서 이 취약점의 수정 커밋 diff는 식별하지 못했습니다.

‍

■ PoC

‍

Rapid7은 익스플로잇 코드 poc.py를 GitHub 저장소에 게시했습니다. 이 코드는 저장소에 세 가지 산출물을 심습니다. 파일 존재 조건을 정규식으로 검사하도록 작성된 CI/CD 설정 파일, 그 설정이 참조하는 정상 동작용 포함 파일, UTF-8 문자로 구성된 긴 경로에 만들어지는 빈 파일입니다. 아래 코드는 공개 PoC 원문 poc.py의 generate() 함수 일부입니다. 코드는 수정하지 않고 그대로 옮겼습니다. 파일 전체는 참고 자료의 원문 링크에서 확인할 수 있습니다.

def generate(directory, command, quiet=False):
    pattern, subject = build(command)
    output = os.fsencode(os.path.abspath(directory))
    os.makedirs(output, exist_ok=True)
    Path(directory, ".gitlab-ci.yml").write_text(
        "include:\n  - local: '/benign.yml'\n    rules:\n      - exists:\n          regexp: "
        + json.dumps(pattern, ensure_ascii=True)
        + "\nbase:\n  script: ':'\n"
    )
    Path(directory, "benign.yml").write_text('included:\n  script: ":"\n')
    payload_path = os.path.join(output, subject)
    os.makedirs(os.path.dirname(payload_path), exist_ok=True)
    open(payload_path, "wb").close()
    if not quiet:
        print(f"[+] payload generated: regex={len(pattern)} chars, path={len(subject)} bytes", flush=True)

GitLab은 설정 검사 단계에서 이 산출물을 읽습니다. 해당 브랜치의 설정을 평가하면서 rules:exists:regexp에 들어 있는 공격자 제공 정규식을 경로 목록과 비교하고, 이 과정에서 UTF-8 경로를 만나 재컴파일이 일어납니다. 해당 코드는 공격자가 대상 인스턴스에 공개 프로젝트와 그 프로젝트의 인증된 클론 URL을 미리 확보한 상태를 전제로 합니다. 명령 실행 결과는 공격자가 띄운 외부 수신 서버로 전송됩니다. 정규식 페이로드를 조립하는 부분과 메모리 관련 상수는 본문에 싣지 않았습니다.

⚠️ WARN

재현 검증은 공식 CE 19.4.1 amd64 이미지에서 확인된 결과이며, 다른 빌드에서는 상수 조정이 필요합니다.
운영 중인 인스턴스에서 직접 재현을 시도하면 루비 프로세스가 비정상 종료될 수 있으므로, 검증이 필요하면 격리된 테스트 환경에서만 수행해야 합니다.

‍

■ 탐지 및 점검

‍

• 설치된 GitLab 버전을 확인해 19.2.8·19.3.5·19.4.2 미만이면 영향 대상으로 분류하고, 외부 노출 여부에 따라 조치 순서를 정합니다.

• 자체 설치형 인스턴스의 공개 프로젝트에서 설정 파일에 rules:exists:regexp 형태로 사용자 제공 정규식이 들어간 사례가 있는지 점검합니다.

• Rails 요청 로그에서 /api/v4/projects/:id/ci/lint 호출 이력, 특히 content_ref 파라미터로 기본 브랜치가 아닌 참조를 지정한 요청과 인증 토큰 없이 들어온 요청을 확인합니다.

• Puma 등 루비 작업자 프로세스의 비정상 종료·세그멘테이션 오류 기록과, git 사용자 권한에서 예상되지 않은 자식 프로세스(셸·네트워크 전송 도구) 생성 이력을 함께 확인합니다. 단독 신호만으로는 침해로 단정할 수 없으므로 동일 시점의 요청 로그·브랜치 변경 이력과 함께 대조합니다.

• 최근 생성된 브랜치에 UTF-8 문자로 채워진 비정상적으로 긴 경로의 파일이 추가됐는지 점검합니다.

✅ CHECK

위 항목 중 로그와 프로세스 기록은 패치 적용 이후에도 과거 노출 구간을 판단하는 근거가 됩니다.
패치 전 외부에 노출된 기간이 있었다면 업그레이드와 별도로 해당 구간의 로그를 먼저 확보해야 합니다.

‍

■ 대응 방안

‍

• 근본 대응은 19.4.2·19.3.5·19.2.8 중 운영 계열에 맞는 버전으로 업그레이드하는 것입니다. GitLab 공지가 제시한 다른 영구 조치는 없습니다.

• 즉시 업그레이드가 어렵다면 임시로 익명 사용자의 공개 프로젝트 접근을 제한하고, 신뢰할 수 없는 사용자가 프로젝트를 생성하거나 공개 프로젝트에 쓰지 못하게 막습니다.

• 가입 승인 없이 누구나 계정을 만들 수 있는 셀프 등록 설정을 점검하고, 업무상 필요하지 않으면 비활성화하거나 관리자 승인 절차를 적용합니다.

• 패치 전 외부 노출 이력이 있는 인스턴스는 침해 가능성을 전제로 우선 조사하고, git 사용자 권한 범위에서 접근 가능한 CI/CD 변수·러너 등록 토큰·배포 키의 재발급을 검토합니다.

• GitLab.com과 GitLab Dedicated만 사용하는 조직은 추가 조치가 필요하지 않습니다. 다만 사업부별로 운영되는 자체 설치형 인스턴스가 있는지 자산 목록에서 다시 확인해야 합니다.

💡 TIP

이번 패치 릴리스에는 데이터베이스 마이그레이션이 포함돼 업그레이드 절차에 영향을 줍니다. 단일 노드 인스턴스는 중단이 발생하며, 19.3.5에는 배포 후 마이그레이션도 포함됩니다.
버전을 올리기 전에 릴리스 노트의 업그레이드 경로와 마이그레이션 안내를 먼저 확인해야 합니다.

‍

[참고 자료]

• GitLab 긴급 패치 릴리스 19.4.2 · 19.3.5 · 19.2.8

• CVE 레코드 — CVE-2026-97229

• Rapid7 기술 분석 — CVE-2026-97229 미인증 원격 코드 실행

• Rapid7 공개 PoC 원문 — poc.py

• GitLab CI Lint API 문서

• BleepingComputer — GitLab 최고 심각도 코드 실행 취약점 경고

• Help Net Security — GitLab 관리자 패치 권고 보도

• 인포그랩 GitLab 제품·구축 사례 소개

  • #EQST_NOW
  • #취약점

관련 서비스

더 많은 보안 인사이트

SK쉴더스 유튜브 채널에서 확인하세요.

SK쉴더스 유튜브 채널에서 확인하세요.
보안 트렌드와 대응방법

매월 뉴스레터로 확인하세요.

매월 뉴스레터로 확인하세요.