---
title: "GitHub 내부 Git 인프라 RCE 취약점, git push 한 번으로 서버 실행까지 갔다"
published: 2026-04-28T16:15:43.000Z
canonical: https://jeff.news/article/1924
---
# GitHub 내부 Git 인프라 RCE 취약점, git push 한 번으로 서버 실행까지 갔다

Wiz Research가 GitHub 내부 Git 처리 파이프라인에서 CVE-2026-3854 원격 코드 실행 취약점을 발견했다. 세미콜론으로 구분되는 내부 `X-Stat` 헤더에 사용자 입력이 그대로 들어가면서 보안 필드가 덮어써졌고, GHES는 전체 서버 장악, GitHub.com은 공유 스토리지 노드 접근으로 이어질 수 있었다.

## git push 한 번으로 GitHub 서버에서 코드 실행

- Wiz Research가 GitHub 내부 Git 인프라에서 꽤 센 취약점 CVE-2026-3854를 찾아냄
  - 인증된 사용자라면 표준 Git 클라이언트에서 `git push` 한 번으로 GitHub 백엔드 서버에 임의 명령을 실행할 수 있었음
  - GitHub.com과 GitHub Enterprise Server(GHES) 양쪽에 영향이 있었고, GHES 쪽은 전체 서버 장악으로 이어질 수 있었음
  - GitHub.com은 신고 후 6시간 안에 완화됐고, GHES는 3.19.3 이상 버전 패치가 나옴

- 공개 시점 기준으로 GHES 인스턴스 88%가 아직 취약한 상태였다는 점이 진짜 무서운 숫자임
  - GitHub.com 사용자는 별도 조치가 필요 없지만, GHES 관리자는 즉시 업그레이드해야 함
  - GHES에서 성공하면 모든 호스팅 저장소와 내부 시크릿 접근까지 가능해지는 급의 문제였음
  - GitHub CISO도 버그바운티 프로그램에서 최고 수준 보상에 가까운 발견이라고 평가함

> [!WARNING]
> GHES 운영 중이면 3.19.3 이상으로 올리는 게 우선순위 1번임. 이건 “언젠가 패치”가 아니라 서버 전체 침해로 이어질 수 있는 RCE임.

## 내부 프로토콜 하나가 무너진 과정

- GitHub의 `git push` 요청은 여러 내부 컴포넌트를 지나감
  - `babeld`는 SSH Git 요청의 진입점이자 프록시 역할을 함
  - `gitauth`는 사용자 인증과 push 권한, 파일 크기 제한, 브랜치 규칙 같은 정책을 반환함
  - `gitrpcd`는 `babeld`가 만든 `X-Stat` 헤더를 파싱하고 downstream 프로세스를 준비함
  - pre-receive hook 바이너리는 push가 받아들여지기 전에 보안 정책과 커스텀 훅을 실행함

- 문제의 중심은 `X-Stat`이라는 내부 헤더였음
  - 이 헤더는 `key=value` 쌍을 세미콜론(`;`)으로 구분해서 보안 메타데이터를 전달함
  - 파서는 같은 키가 여러 번 나오면 뒤에 나온 값을 조용히 채택하는 last-write-wins 방식이었음
  - `gitrpcd`는 인증을 직접 하지 않고 `babeld`가 넘긴 필드를 전부 신뢰했음

- Git의 push option이 공격 통로가 됨
  - 사용자는 `git push -o`로 서버에 임의 문자열을 넘길 수 있음
  - `babeld`는 이 값을 `push_option_0`, `push_option_1` 같은 필드로 `X-Stat`에 넣었는데, 세미콜론을 필터링하지 않았음
  - 공격자가 push option 값에 `;large_blob_rejection_enabled=bool:false` 같은 문자열을 넣으면 새 보안 필드가 주입됨
  - 같은 키가 뒤에 나오면 공격자 값이 이겨서 기존 정책을 덮어씀

```mermaid
sequenceDiagram
    participant 사용자
    participant babeld
    participant gitauth
    participant gitrpcd
    participant 사전수신훅
    사용자->>babeld: git push -o "x;보안필드=공격값"
    babeld->>gitauth: 인증 및 정책 확인
    gitauth-->>babeld: 정상 정책 반환
    babeld->>gitrpcd: X-Stat 헤더에 push option 포함
    gitrpcd->>gitrpcd: 세미콜론 기준 파싱, 뒤 필드가 앞 필드 덮어씀
    gitrpcd->>사전수신훅: 조작된 rails_env, hook 설정 전달
    사전수신훅-->>사용자: 샌드박스 밖에서 명령 실행 결과 반환
```

## RCE로 이어진 세 필드

- 단순히 보안 플래그를 끄는 수준에서 끝난 게 아니라, 세 필드를 묶어 원격 코드 실행까지 감
  - `rails_env`를 production이 아닌 값으로 바꿔 샌드박스 없는 실행 경로를 타게 함
  - `custom_hooks_dir`를 조작해 훅 스크립트를 찾는 기준 디렉터리를 공격자가 원하는 쪽으로 돌림
  - `repo_pre_receive_hooks`에 path traversal을 넣어 파일시스템의 임의 바이너리를 실행하게 만듦

- GHES에서는 이 체인이 그대로 먹혔음
  - pre-receive hook 바이너리에는 production이면 샌드박스, 그 외 값이면 샌드박스 없이 실행하는 두 경로가 있었음
  - 이 경로 선택이 `X-Stat`의 `rails_env` 값에 달려 있었고, 그 값이 주입 가능했음
  - 결과적으로 `git` 서비스 사용자 권한으로 `id` 같은 명령 실행이 확인됨

- GitHub.com에서는 처음엔 커스텀 훅 경로가 실행되지 않았지만, 추가 필드 하나로 우회됨
  - GitHub.com은 기본적으로 enterprise mode가 꺼져 있어 GHES와 달리 커스텀 훅 경로에 도달하지 않았음
  - 연구진은 해당 모드 제어 플래그도 `X-Stat`에 있고 주입 가능하다는 걸 바이너리 분석으로 찾음
  - 그 플래그까지 켜자 GitHub.com 인프라 내부에서 `hostname` 실행 결과가 반환됨

> [!IMPORTANT]
> 이 취약점의 본질은 세미콜론 하나가 아님. “한 서비스는 입력을 안전하다고 가정하고, 다른 서비스는 헤더를 신뢰하고, 또 다른 바이너리는 환경값을 신뢰한” 조합이 RCE를 만든 것임.

## 멀티테넌트 영향과 AI 리버스 엔지니어링

- GitHub.com에서의 영향은 공유 인프라 때문에 더 민감했음
  - GitHub.com은 수많은 사용자와 조직의 저장소가 같은 백엔드 스토리지 노드에 올라가는 멀티테넌트 구조임
  - `git` 사용자는 해당 노드의 저장소 작업을 처리하기 위해 넓은 파일시스템 접근권한을 가짐
  - Wiz는 다른 테넌트 저장소 내용을 읽지는 않았지만, 두 개 노드에서 수백만 개 저장소 인덱스 엔트리에 접근 가능한 구조를 확인했다고 밝힘

- 이 연구는 AI 보조 리버스 엔지니어링의 상징적인 사례이기도 함
  - GitHub의 내부 Git 파이프라인은 닫힌 소스 바이너리와 여러 언어 서비스가 섞인 구조라 수동 분석 비용이 컸음
  - Wiz는 IDA MCP 같은 AI 보조 도구로 컴파일된 바이너리를 분석하고 내부 프로토콜을 복원했다고 설명함
  - 예전엔 시간이 너무 많이 들어 포기했을 종류의 분석이 이제는 현실적인 연구 대상이 되고 있음

- 타임라인도 꽤 빠르게 움직였음
  - 2026년 3월 4일 취약점 발견, GHES 3.19.1에서 RCE 확인, GitHub 신고와 GitHub.com 완화가 같은 날 이뤄짐
  - 3월 10일 CVE-2026-3854가 CVSS 8.7로 할당되고 GHES 패치가 공개됨
  - 4월 28일 공개 분석이 나옴

- 다른 팀들이 가져갈 교훈은 명확함
  - 내부 프로토콜이라고 문자열 델리미터 기반 포맷을 대충 믿으면 안 됨
  - 사용자 입력이 여러 서비스를 지나며 보안 설정으로 승격되는 경로를 추적해야 함
  - production 바이너리에 non-production 실행 경로가 남아 있고, 그 경로가 헤더나 환경값으로 열리면 공격면이 됨
  - path traversal 검증, 입력 escaping, 구조화된 포맷, 필드 신뢰 경계 정리가 전부 보안 요구사항임

---

## 기술 맥락

- GitHub가 선택했던 구조는 여러 내부 서비스가 `X-Stat`이라는 공통 헤더로 정책과 상태를 넘기는 방식이에요. 왜냐면 Git push 처리처럼 빠르고 오래된 경로에서는 간단한 문자열 프로토콜이 구현하기 쉽고 서비스 사이 연결 비용도 낮거든요.

- 문제는 그 간단함이 보안 경계가 되는 순간이에요. `babeld`는 push option을 그냥 넣었고, `gitrpcd`는 헤더를 신뢰했고, pre-receive hook은 `rails_env` 값으로 실행 경로를 골랐어요. 각각은 작아 보이지만 이어 붙이면 공격자가 보안 설정을 작성하는 구조가 돼요.

- GHES와 GitHub.com 차이도 중요해요. GHES는 enterprise mode가 기본으로 켜져 커스텀 훅 경로가 바로 열렸고, GitHub.com은 꺼져 있었지만 그 플래그마저 같은 헤더로 제어됐어요. 환경별 방어 차이가 있어도 제어면이 같은 입력에 묶여 있으면 우회 여지가 생기는 거예요.

- 실무팀은 내부 포맷을 JSON이나 protobuf로 바꾸는 것만으로 끝내면 안 돼요. 어떤 필드가 사용자 영향권에 있고, 어떤 서비스가 그 필드를 권한 있는 설정으로 해석하는지 데이터 흐름을 추적해야 이런 종류의 취약점을 잡을 수 있어요.

- AI 보조 리버스 엔지니어링은 방어팀에도 신호예요. 공격자와 연구자가 닫힌 바이너리를 훨씬 빠르게 읽을 수 있다면, 벤더도 내부 프로토콜과 레거시 실행 경로를 더 적극적으로 감사해야 해요.

## 핵심 포인트

- 인증된 사용자가 표준 git 클라이언트의 push option만으로 내부 헤더 필드를 주입할 수 있었음
- GHES 3.19.3 이상으로 즉시 업그레이드해야 하며 공개 시점 기준 88% 인스턴스가 취약한 상태로 관측됨
- GitHub.com은 신고 후 6시간 안에 완화됐고 일반 GitHub.com 사용자는 별도 조치가 필요 없음
- AI 보조 리버스 엔지니어링 도구가 폐쇄형 바이너리 분석 속도를 크게 끌어올린 사례

## 인사이트

이 취약점은 ‘내부 프로토콜이니까 믿어도 된다’는 가정이 얼마나 쉽게 깨지는지 보여준다. 특히 여러 언어와 서비스가 같은 문자열 포맷을 다르게 해석하는 구조라면, 사용자 입력이 어디까지 흘러가는지 다시 봐야 함.
