목록으로 돌아가기
Security
5분 읽기

CVE-2026-34040 — 1MB 요청으로 Docker AuthZ 플러그인을 무력화

Docker Engine의 AuthZ 플러그인을 우회하는 CVE-2026-34040. 1MB짜리 패딩 하나로 권한 검사를 건너뛰고 호스트를 장악할 수 있다.

ㅂㄹㄱ

2026-04-12 ·

들어가며

컨테이너 보안의 핵심은 격리다. 컨테이너 안에서 무슨 짓을 하든 호스트에 영향을 줄 수 없어야 한다. Docker의 AuthZ(Authorization) 플러그인은 그 격리를 정책 수준에서 강제하는 장치다. "이 사용자는 privileged 컨테이너를 못 만든다", "호스트 볼륨 바인드는 금지" 같은 규칙을 걸어두는 용도다.

2026년 3월 말, 그 AuthZ 플러그인을 HTTP 요청 하나로 완전히 건너뛸 수 있는 취약점이 공개됐다. CVE-2026-34040. CVSS 8.8(High). 공격에 필요한 건 Docker API 접근 권한과 1MB 남짓한 JSON 패딩뿐이다.


핵심 질문: 왜 요청 본문이 크면 권한 검사가 사라지는가

AuthZ 플러그인의 동작 구조

Docker 데몬은 API 요청을 받으면, 실행하기 전에 AuthZ 플러그인에 "이 요청 허용할까?"라고 물어본다. 플러그인은 요청의 메서드, 경로, **본문(body)**을 보고 판단한다.

sequenceDiagram
    participant C as Client
    participant D as Docker Daemon
    participant A as AuthZ Plugin<br/>(OPA 등)

    C->>D: API 요청
    D->>A: 검증 요청 (메서드 + 경로 + 본문)
    A-->>D: 허용 / 거부
    D->>D: 허용 시 요청 실행
Diagram: sequenceDiagram participant C as Client participant D as Docker Daemon participant A as AuthZ Plugin<br/>(OPA 등) C->>D: API 요청 D->>A: 검증 요청 (메서드 + 경로 + 본문) A-->>D: 허용 / 거부 D->>D: 허용 시 요청 실행

문제는 Docker 데몬이 플러그인에 요청 본문을 전달하는 방식에 있다.

drainBody 함수의 1MB 제한

Docker 데몬의 미들웨어에는 drainBody라는 함수가 있다. 요청 본문을 읽어서 AuthZ 플러그인에 넘기는 역할인데, 여기에 하드코딩된 1MB 제한이 걸려 있었다.

// 취약한 코드 (의사 코드)
func drainBody(body io.ReadCloser) ([]byte, error) {
    bufReader := bufio.NewReaderSize(body, maxBodySize) // maxBodySize = 1MB
    data, err := bufReader.Peek(maxBodySize)
    if err != nil {
        return []byte{}, nil  // ← 여기가 문제. 에러 시 빈 바이트를 반환한다
    }
    return data, nil
}

Peek은 버퍼 크기를 넘어서면 에러를 반환한다. 정상적인 설계라면 여기서 요청을 거부해야 한다. 그런데 이 코드는 에러를 무시하고 빈 바이트 슬라이스를 반환한다. 빈 본문이 플러그인에 전달되는 것이다.

공격 흐름

이걸 악용한 공격 흐름을 정리하면 이렇다.

sequenceDiagram
    participant A as 공격자
    participant D as Docker Daemon
    participant P as AuthZ Plugin

    A->>D: POST /containers/create<br/>Body: 1.05MB (Padding 포함)
    Note right of D: drainBody()<br/>→ 1MB 초과<br/>→ 빈 본문 반환
    D->>P: 검증 요청 (빈 본문)
    Note right of P: 본문이 비어 있으니<br/>거부할 근거 없음
    P-->>D: ✅ 허용
    Note right of D: 원본 요청 그대로 실행<br/>Binds: ["/:/host"]
    D->>D: 호스트 전체 마운트된 컨테이너 생성
Diagram: sequenceDiagram participant A as 공격자 participant D as Docker Daemon participant P as AuthZ Plugin A->>D: POST /containers/create<br/>Body: 1.05MB (Padding 포함) Note right of D: drainBody()<br/>→ 1MB 초과<br/>→ 빈 본문 반환 D->>P: 검증 요청 (빈 본문) Note right of P: 본문이 비어 있으니<br/>거부할 근거 없음 P-->>D: ✅ 허용 Note right of D: 원본 요청 그대로 실행<br/>Binds: ["/:/host"] D->>D: 호스트 전체 마운트된 컨테이너 생성

공격 페이로드는 이렇게 생겼다.

{
  "Image": "alpine",
  "Cmd": ["cat", "/host/etc/shadow"],
  "HostConfig": {
    "Binds": ["/:/host"]
  },
  "Padding": "AAAA...1.05MB...AAAA"
}

핵심은 3번이다. 플러그인 입장에서는 빈 요청이 들어왔으니 거부할 근거가 없다. "호스트 볼륨 바인드 금지" 정책이 걸려 있어도, 본문에 Binds 필드가 없으니 통과시켜 버린다. 한편 Docker 데몬은 원본 요청을 그대로 갖고 있으므로, 플러그인이 허용하면 그 요청을 그냥 실행한다.


10년 묵은 버그, 불완전한 패치

이 취약점은 하늘에서 뚝 떨어진 게 아니다. CVE-2024-41110이라는 선행 취약점의 불완전한 수정에서 비롯됐다.

CVE공개 시점CVSS핵심
CVE-2024-411102024년 7월10.0 (Critical)AuthZ 플러그인에 빈 본문이 전달되는 최초 발견
CVE-2026-340402026년 3월8.8 (High)위 패치가 불완전해서 1MB 초과 요청으로 여전히 우회 가능

CVE-2024-41110은 CVSS 만점(10.0)을 받은 심각한 취약점이었다. Docker 측에서 패치를 냈지만, drainBody의 1MB 제한이라는 근본 원인을 놓쳤다. 그리고 이 코드는 Docker Engine 1.10 시절부터 있었다. 거의 10년 동안 잠복해 있던 버그인 셈이다.


영향 범위

누가 영향을 받는가

조건영향 여부
AuthZ 플러그인 사용 중 (OPA, Prisma Cloud, 커스텀 정책 등)영향 받음
AuthZ 플러그인 미사용영향 없음
Docker Desktop (기본 설정)대부분 영향 없음
쿠버네티스 + containerdDocker 데몬을 쓰지 않으면 영향 없음

엔터프라이즈 환경에서 Docker를 멀티테넌트로 운영하면서 AuthZ 플러그인으로 정책을 강제하는 곳이 직격탄이다. CI/CD 파이프라인에서 Docker-in-Docker를 돌리면서 AuthZ로 권한을 제한하는 구성도 위험하다.

공격 전제 조건

  • Docker API에 접근할 수 있어야 한다 (Unix 소켓 또는 TCP)
  • 별도의 익스플로잇 코드나 특수 도구가 필요 없다
  • HTTP 요청 하나면 된다

공격 난이도가 극히 낮다는 게 이 취약점의 진짜 위험이다.


패치 내용

Docker(Moby) 29.3.1에서 수정됐다. 패치 커밋(e89edb19)의 핵심 변경은 세 가지다.

변경 사항이전이후
maxBodySize1MB4MiB (4,194,304 bytes)
초과 시 동작빈 본문 반환 (fail-open)에러 반환 + 요청 거부 (fail-closed)
검증 방식Peek(maxBodySize)Peek(maxBodySize + 1)로 초과 여부 탐지

가장 중요한 변경은 fail-open에서 fail-closed로 전환한 것이다. 이전에는 "모르겠으면 통과시키자"였고, 지금은 "모르겠으면 거부하자"다. 보안 설계의 기본 원칙인데, 이게 10년 동안 빠져 있었다.


당장 해야 할 것

1. Docker Engine 업데이트

# 현재 버전 확인
docker version

# 29.3.1 이상으로 업데이트
sudo apt-get update && sudo apt-get install docker-ce docker-ce-cli containerd.io

# 또는 공식 설치 스크립트
curl -fsSL https://get.docker.com | sh

2. AuthZ 플러그인 의존도 점검

AuthZ 플러그인이 요청 본문을 기반으로 접근 제어를 하고 있다면, 플러그인 자체의 업데이트도 확인해야 한다. 일부 플러그인은 자체적으로 본문 크기 제한을 추가했을 수 있다.

3. Docker API 접근 제한

패치와 별개로, Docker API 접근 자체를 최소 권한 원칙으로 제한하는 게 근본 대책이다.

# Docker 소켓 권한 확인
ls -la /var/run/docker.sock

# TCP로 열려 있는지 확인
ss -tlnp | grep 2375

TCP 2375/2376 포트로 Docker API가 외부에 노출되어 있다면, 이 취약점과 관계없이 즉시 차단해야 한다.


개발자로서 느낀 점

fail-open은 시한폭탄이다

이 취약점의 근본 원인은 에러 처리 한 줄이다. return []byte{}, nil — 에러가 났는데 빈 값을 정상 반환한 것. 언어를 막론하고 err를 받아놓고 무시하는 패턴은 흔한 실수지만, 보안 경로에서 이걸 하면 이런 일이 생긴다. 인증/인가 경로에서 예외를 빈 값으로 무해화하는 순간 정책 자체가 무력화된다.

보안 경로의 기본 원칙: 모르겠으면 거부한다(fail-closed).

패치의 패치는 위험하다

CVE-2024-41110을 패치했는데 CVE-2026-34040이 나왔다. 원인을 정확히 분석하지 않고 증상만 막으면 이렇게 된다. "빈 본문이 전달되는 경로"를 하나 막았지만, "왜 빈 본문이 만들어지는가"라는 근본 원인은 건드리지 않았다.

코드 리뷰에서 보안 패치를 볼 때는 "이 수정이 증상을 막는 건가, 원인을 막는 건가"를 항상 구분해야 한다.


정리

항목내용
CVECVE-2026-34040
대상Moby(Docker Engine) < 29.3.1
CVSS8.8 (High)
분류CWE-288: Authentication Bypass Using an Alternate Path
공격 방식1MB 초과 요청으로 AuthZ 플러그인 우회
전제 조건Docker API 접근 권한
패치Docker Engine 29.3.1
선행 취약점CVE-2024-41110 (CVSS 10.0)의 불완전한 수정

Docker를 프로덕션에서 쓰고 있고 AuthZ 플러그인에 의존하고 있다면, 29.3.1로 즉시 업데이트하는 게 맞다. AuthZ를 안 쓴다면 직접적 영향은 없지만, Docker API 접근 범위를 점검하는 계기로 삼으면 좋겠다.

HTTP 요청 하나에 패딩을 좀 넣는 것만으로 컨테이너 격리가 무너진다는 사실 — 컨테이너 보안이 "기본으로 안전하다"는 환상을 다시 한번 깨는 사례다.


참고자료

자주 묻는 질문

AuthZ 플러그인을 안 쓰면 영향 없나?
그렇다. 이 취약점은 AuthZ 플러그인이 활성화된 환경에서만 동작한다. 플러그인 없이 Docker를 쓰고 있다면 직접적 영향은 없다.
Docker Desktop도 영향을 받나?
기본 설정의 Docker Desktop은 AuthZ 플러그인을 사용하지 않으므로 대부분 영향 없다. 별도로 AuthZ 플러그인을 설정한 경우만 해당된다.
쿠버네티스 환경은 안전한가?
containerd를 직접 사용하는 쿠버네티스 환경은 영향 없다. Docker 데몬을 CRI로 쓰고 있고 AuthZ 플러그인이 걸려 있다면 영향 받는다.
패치 전까지 임시 대응 방법은?
Docker API 접근을 최소 권한으로 제한하고, TCP 2375/2376 포트 노출을 차단한다. AuthZ 플러그인이 요청 본문에 의존하지 않도록 네트워크 레벨 접근 제어를 강화하는 게 임시 대안이다.