콘텐츠로 이동

CVE-2026-19478 GitLab GraphQL 코드 인젝션 취약점

CVE-2026-19478 GitLab GraphQL 코드 인젝션 취약점 thumbnail
CVE-2026-19478은 자체 관리형 GitLab CE와 EE의 GraphQL 디렉티브 처리에서 발생하는 코드 인젝션 취약점이다. 인증되지 않은 원격 사용자가 특정 조건에서 공개 프로젝트와 사용자 데이터를 변경하거나 삭제할 수 있다.

요약

CVE-2026-19478은 자체 관리형 GitLab CE와 EE의 GraphQL 디렉티브 처리에서 발생하는 코드 인젝션 취약점이다. 인증되지 않은 원격 사용자가 특정 조건에서 공개 프로젝트와 사용자 데이터를 변경하거나 삭제할 수 있다.

GitLab은 2026-08-17에 긴급 패치를 배포했다. 영향 버전은 18.2부터 19.2 계열이며 CVSS v3.1 점수는 9.4다. GitLab.com과 GitLab Dedicated에는 수정 버전이 적용됐다.

자체 관리형 GitLab 패치 필요

영향받는 인스턴스는 현재 버전 계열에 맞춰 18.11.11, 19.0.8, 19.1.6, 19.2.4 이상으로 업데이트한다. 패치 전 로그와 저장소 상태도 함께 점검한다.

취약점 분석

항목 내용
취약점 식별자 CVE-2026-19478
취약점 유형 GraphQL 디렉티브를 통한 코드 인젝션
심각도 CVSS v3.1 9.4
인증 조건 인증 없이 원격 접근 가능
영향 제품 자체 관리형 GitLab CE와 EE
영향 버전 18.2 이상 18.11.11 미만, 19.0 이상 19.0.8 미만, 19.1 이상 19.1.6 미만, 19.2 이상 19.2.4 미만
수정 버전 18.11.11, 19.0.8, 19.1.6, 19.2.4
공격 표면 /api/graphql
관련 디렉티브 @gl_introduced
주요 영향 공개 프로젝트와 사용자 데이터 변경 또는 삭제
서비스형 GitLab GitLab.com과 GitLab Dedicated는 수정 완료

취약점은 롤링 배포 중 아직 존재하지 않는 GraphQL 필드를 처리하는 기능에서 발생한다. @gl_introduced(version:)가 지정된 미래 필드는 현재 인스턴스에서 오류 대신 null을 반환하도록 설계됐다.

패치 전 Gitlab::Graphql::VersionFilter::FutureFieldFallbackfallback_field 메서드는 요청에 포함된 필드 이름으로 GraphQL::Schema::Field를 만들었다. 이 필드에는 명시적인 리졸버가 없었다.

def fallback_field(name:)
  GraphQL::Schema::Field.new(
    owner: self,
    name: name,
    type: GraphQL::Types::Boolean,
    fallback_value: nil
  )
end

graphql-ruby는 명시적인 리졸버가 없는 필드를 처리할 때 대상 객체에서 필드 이름과 같은 메서드를 찾는다. 공격자가 지정한 GraphQL 필드 이름이 서버 객체의 메서드 호출로 이어질 수 있었던 이유다.

관련 처리 흐름은 다음과 같다.

  1. /api/graphql@gl_introduced 디렉티브가 포함된 요청이 들어온다.
  2. 버전 필터가 미래 버전 필드를 제거하고 contain_future_fields 상태를 설정한다.
  3. 실행 단계에서 FutureFieldFallback#get_field가 스키마에 없는 필드를 동적으로 만든다.
  4. 리졸버가 지정되지 않은 필드 이름이 대상 객체의 메서드 이름으로 해석된다.
  5. 공개 프로젝트처럼 인증 없이 조회할 수 있는 객체에서 상태 변경 메서드에 도달할 수 있다.

패치는 동적 필드에 Resolvers::NilResolver를 고정했다. 공격자가 지정한 이름으로 객체 메서드를 찾지 않고 항상 nil을 반환한다.

 GraphQL::Schema::Field.new(
   owner: self,
   name: name,
-  type: GraphQL::Types::Boolean,
-  fallback_value: nil
+  resolver_class: Resolvers::NilResolver
 )

새 리졸버의 동작은 단순하다.

module Resolvers
  class NilResolver < BaseResolver
    type ::GraphQL::Types::Boolean, null: true

    def resolve
      nil
    end
  end
end

이 변경은 미래 필드가 null로 처리되는 기존 동작을 유지하면서, 요청 필드 이름이 서버 객체의 메서드 호출로 연결되는 경로를 제거한다. 공개 분석에는 무기화된 전체 PoC가 포함되지 않았으며, 이 취약점만으로 운영체제 명령 실행이나 원격 코드 실행이 가능하다는 근거도 제시되지 않았다.

자산 식별

자체 관리형 GitLab에서는 설치 버전, 에디션, 배포 형태를 확인한다.

sudo gitlab-rake gitlab:env:info
sudo gitlab-ctl status
sudo grep -E 'external_url|registry_external_url' /etc/gitlab/gitlab.rb

컨테이너 배포는 이미지 태그와 다이제스트를 함께 수집한다.

### docker inspect gitlab \

  --format '{{.Config.Image}} {{index .RepoDigests 0}}'

가변 태그만으로는 패치 적용 여부를 확정하기 어렵다. 패키지 버전이나 이미지 다이제스트를 다음 기준과 대조한다.

설치 계열 영향받는 마지막 버전 수정 버전
18.2부터 18.11 18.11.10 18.11.11
19.0 19.0.7 19.0.8
19.1 19.1.5 19.1.6
19.2 19.2.3 19.2.4

탐지와 대응

웹 서버와 리버스 프록시 로그에서 /api/graphql 요청을 추출하고 @gl_introduced가 포함된 요청을 찾는다.

grep -E '/api/graphql.*@gl_introduced|@gl_introduced.*\/api\/graphql' \
  /var/log/gitlab/nginx/gitlab_access.log

@gl_introduced는 정상적인 버전 호환 처리에도 사용될 수 있으므로 문자열 하나만으로 공격을 확정하지 않는다. 다음 기록을 같은 시간대의 인증 정보와 대조한다.

  • 익명 세션에서 발생한 GraphQL 요청
  • 공개 프로젝트의 삭제, 이름 변경, 가시성 변경
  • 프로젝트 구성원과 유지관리자 권한 변경
  • 사용자 데이터 변경
  • 짧은 시간에 반복된 GraphQL 요청
  • 백업본이나 개발자 로컬 복제본과 일치하지 않는 커밋 및 태그

Check Point와 CERT.LV는 공개 이후 공격 시도가 관측됐다고 전했다. 다만 GitLab 권고문에는 피해 조직, 공격자 기반시설 또는 파일 해시 형태의 침해지표가 포함되지 않았다. 점검은 @gl_introduced 요청과 인증 기록이 없는 상태 변경을 함께 확인하는 방식이 적절하다.

업데이트 전에는 접근 로그, GitLab 감사 로그, 저장소 상태와 백업본을 보존한다. 즉시 업데이트할 수 없다면 /api/graphql의 미인증 접근이나 공개 프로젝트 노출을 일시적으로 제한한다. 이 제한은 패치를 대신하지 않는다.

참고 자료