Docker 컨테이너 포렌식 분석은(는) 무엇을 하나요?
사용 시기
- 손상된 Docker 컨테이너 또는 컨테이너 호스트를 조사할 때
- 레지스트리에서 가져온 악성 Docker 이미지를 분석할 때
- 컨테이너화된 애플리케이션 침해 사고 대응 시
- 컨테이너 탈출 시도 또는 권한 상승을 검사할 때
- 컨테이너 구성을 감사하고 잘못된 설정을 식별할 때
전제 조건
- 포렌식 워크스테이션에서 Docker CLI 접근 가능
- Docker 호스트 파일 시스템 접근 가능 (포렌식 이미지 또는 라이브)
- Docker 레이어 파일 시스템(overlay2, aufs) 이해
- 이미지 분석을 위한 dive, docker-explorer, 또는 container-diff
- Docker 데몬 구성 및 소켓 보안에 대한 지식
- 컨테이너 이미지 취약점 스캔을 위한 Trivy 또는 Grype
워크플로우
1단계: 컨테이너 상태 및 증거 보존
# 모든 컨테이너 목록 (중지된 컨테이너 포함)
docker ps -a --no-trunc > /cases/case-2024-001/docker/container_list.txt
# 손상된 컨테이너 검사
CONTAINER_ID="abc123def456"
docker inspect $CONTAINER_ID > /cases/case-2024-001/docker/container_inspect.json
# 컨테이너 파일 시스템을 tarball로 내보내기 (현재 상태 보존)
docker export $CONTAINER_ID > /cases/case-2024-001/docker/container_export.tar
# 컨테이너의 현재 상태로 이미지 생성
docker commit $CONTAINER_ID forensic-evidence:case-2024-001
docker save forensic-evidence:case-2024-001 > /cases/case-2024-001/docker/container_image.tar
# 컨테이너 로그 캡처
docker logs $CONTAINER_ID --timestamps > /cases/case-2024-001/docker/container_logs.txt 2>&1
# 실행 중인 프로세스 캡처 (컨테이너가 여전히 실행 중인 경우)
docker top $CONTAINER_ID > /cases/case-2024-001/docker/container_processes.txt
# 네트워크 연결 캡처
docker exec $CONTAINER_ID netstat -tlnp 2>/dev/null > /cases/case-2024-001/docker/container_network.txt
# 컨테이너에서 특정 파일 복사
docker cp $CONTAINER_ID:/var/log/ /cases/case-2024-001/docker/container_var_log/
docker cp $CONTAINER_ID:/tmp/ /cases/case-2024-001/docker/container_tmp/
docker cp $CONTAINER_ID:/etc/passwd /cases/case-2024-001/docker/container_passwd
# 내보낸 모든 증거 해시
sha256sum /cases/case-2024-001/docker/*.tar > /cases/case-2024-001/docker/evidence_hashes.txt
2단계: 컨테이너 이미지 레이어 분석
# 이미지 레이어 분석을 위한 dive 설치
wget https://github.com/wagoodman/dive/releases/latest/download/dive_linux_amd64.deb
sudo dpkg -i dive_linux_amd64.deb
# 이미지 레이어 대화형 분석
dive forensic-evidence:case-2024-001
# 비대화형 레이어 분석
dive forensic-evidence:case-2024-001 --ci --json /cases/case-2024-001/docker/dive_analysis.json
# 개별 레이어 추출 및 검사
mkdir -p /cases/case-2024-001/docker/layers/
tar -xf /cases/case-2024-001/docker/container_image.tar -C /cases/case-2024-001/docker/layers/
# 이미지 매니페스트 및 레이어 순서 나열
cat /cases/case-2024-001/docker/layers/manifest.json | python3 -m json.tool
# 각 레이어의 변경 사항 검사
for layer in /cases/case-2024-001/docker/layers/*/layer.tar; do
echo "=== 레이어: $(dirname $layer | xargs basename) ==="
tar -tf "$layer" | head -20
echo "..."
done
# container-diff를 사용하여 원본 기본 이미지와 비교
# container-diff 설치
curl -LO https://storage.googleapis.com/container-diff/latest/container-diff-linux-amd64
chmod +x container-diff-linux-amd64
# 커밋된 이미지를 원본과 비교
./container-diff-linux-amd64 diff daemon://nginx:latest daemon://forensic-evidence:case-2024-001 \
--type=file --type=apt --type=history --json \
> /cases/case-2024-001/docker/container_diff.json
3단계: Docker 호스트 아티팩트 검사
# Docker 데이터 디렉터리 (기본값: /var/lib/docker/)
DOCKER_ROOT="/mnt/evidence/var/lib/docker"
# overlay2 파일 시스템 레이어 검사
ls -la $DOCKER_ROOT/overlay2/
# 컨테이너의 병합된 파일 시스템 찾기
CONTAINER_HASH=$(docker inspect $CONTAINER_ID --format '{{.GraphDriver.Data.MergedDir}}' 2>/dev/null)
# 또는 포렌식 이미지에서 수동으로:
# /var/lib/docker/containers/<container_id>/config.v2.json 확인
# 컨테이너 구성 파일 분석
cat $DOCKER_ROOT/containers/$CONTAINER_ID/config.v2.json | python3 -m json.tool \
> /cases/case-2024-001/docker/container_config.json
# Docker 데몬 구성 확인
cat /mnt/evidence/etc/docker/daemon.json 2>/dev/null > /cases/case-2024-001/docker/daemon_config.json
# Docker 이벤트 로그 검사
cat $DOCKER_ROOT/containers/$CONTAINER_ID/*.log > /cases/case-2024-001/docker/container_json_logs.txt
# 볼륨 마운트 확인 (잠재적 호스트 파일 시스템 접근)
python3 << 'PYEOF'
import json
with open('/cases/case-2024-001/docker/container_inspect.json') as f:
data = json.load(f)
inspect = data[0] if isinstance(data, list) else data
print("=== 컨테이너 보안 분석 ===\n")
# 마운트 확인
print("볼륨 마운트:")
for mount in inspect.get('Mounts', []):
rw = "읽기-쓰기" if mount.get('RW') else "읽기 전용"
print(f" {mount.get('Source', 'N/A')} -> {mount.get('Destination', 'N/A')} ({rw})")
if mount.get('Source') in ('/', '/etc', '/var', '/root') and mount.get('RW'):
print(f" 경고: 민감한 호스트 경로가 읽기-쓰기로 마운트되었습니다!")
# 권한 모드 확인
host_config = inspect.get('HostConfig', {})
if host_config.get('Privileged'):
print("\n경고: 컨테이너가 특권(PRIVILEGED) 모드로 실행 중이었습니다!")
# 추가된 Capabilities 확인
cap_add = host_config.get('CapAdd', [])
if cap_add:
print(f"\n추가된 Capabilities: {cap_add}")
dangerous_caps = ['SYS_ADMIN', 'SYS_PTRACE', 'NET_ADMIN', 'SYS_MODULE']
for cap in cap_add:
if cap in dangerous_caps:
print(f" 경고: 위험한 Capability: {cap}")
# PID 네임스페이스 확인
if host_config.get('PidMode') == 'host':
print("\n경고: 컨테이너가 호스트 PID 네임스페이스를 공유합니다!")
# 네트워크 모드 확인
if host_config.get('NetworkMode') == 'host':
print("\n경고: 컨테이너가 호스트 네트워크 네임스페이스를 공유합니다!")
# 사용자 확인
user = inspect.get('Config', {}).get('User', 'root (기본값)')
print(f"\n실행 사용자: {user}")
# 환경 변수에서 비밀 정보 확인
env_vars = inspect.get('Config', {}).get('Env', [])
print(f"\n환경 변수: {len(env_vars)}개")
for env in env_vars:
key = env.split('=')[0]
if any(s in key.upper() for s in ['PASSWORD', 'SECRET', 'KEY', 'TOKEN', 'CREDENTIAL']):
print(f" 민감 정보: {key}=***수정됨***")
PYEOF
4단계: 컨테이너 파일 시스템 변경 사항 분석
# 컨테이너 파일 시스템을 원본 이미지와 비교
docker diff $CONTAINER_ID > /cases/case-2024-001/docker/filesystem_changes.txt
# A = 추가됨, C = 변경됨, D = 삭제됨
# 변경 사항 분석
python3 << 'PYEOF'
added = []
changed = []
deleted = []
with open('/cases/case-2024-001/docker/filesystem_changes.txt') as f:
for line in f:
line = line.strip()
if line.startswith('A '):
added.append(line[2:])
elif line.startswith('C '):
changed.append(line[2:])
elif line.startswith('D '):
deleted.append(line[2:])
print(f"추가된 파일: {len(added)}개")
print(f"변경된 파일: {len(changed)}개")
print(f"삭제된 파일: {len(deleted)}개")
# 의심스러운 추가 파일 플래그 지정
suspicious = [f for f in added if any(s in f for s in
['/tmp/', '/dev/shm/', '/root/', '.sh', '.py', '.elf', 'reverse', 'shell', 'backdoor'])]
if suspicious:
print(f"\n의심스러운 추가 파일:")
for f in suspicious:
print(f" {f}")
# 의심스러운 변경 사항 플래그 지정
sus_changed = [f for f in changed if any(s in f for s in
['/etc/passwd', '/etc/shadow', '/etc/crontab', '/etc/ssh', '.bashrc'])]
if sus_changed:
print(f"\n의심스러운 변경 파일:")
for f in sus_changed:
print(f" {f}")
PYEOF
# 컨테이너 내보내기 파일 추출 및 검사
mkdir -p /cases/case-2024-001/docker/container_fs/
tar -xf /cases/case-2024-001/docker/container_export.tar -C /cases/case-2024-001/docker/container_fs/
# 웹쉘 및 악성 파일 스캔
find /cases/case-2024-001/docker/container_fs/tmp/ -type f -exec file {} \;
find /cases/case-2024-001/docker/container_fs/ -name "*.php" -newer /cases/case-2024-001/docker/container_fs/etc/hostname
5단계: 취약점 스캔 및 보고서 생성
# 알려진 취약점에 대해 이미지 스캔
trivy image forensic-evidence:case-2024-001 \
--format json \
--output /cases/case-2024-001/docker/vulnerability_scan.json
# 내보낸 파일 시스템 스캔
trivy fs /cases/case-2024-001/docker/container_fs/ \
--format table \
--output /cases/case-2024-001/docker/fs_vulnerabilities.txt
# 이미지 내 비밀 정보 확인
trivy image forensic-evidence:case-2024-001 \
--scanners secret \
--format json \
--output /cases/case-2024-001/docker/secrets_scan.json
주요 개념
| 개념 | 설명 |
|---|---|
| 이미지 레이어 | 컨테이너 이미지를 구성하는 읽기 전용 파일 시스템 레이어 |
| overlay2 | 레이어를 위한 유니온 파일 시스템을 사용하는 기본 Docker 스토리지 드라이버 |
| 컨테이너 diff | 런타임 파일 시스템 변경 사항과 원본 이미지의 비교 |
| 특권 모드 | 전체 호스트 기능을 가진 컨테이너 (대부분의 격리 우회) |
| Docker 소켓 | Docker 데몬을 제어하는 Unix 소켓(/var/run/docker.sock) |
| 컨테이너 탈출 | 컨테이너 격리에서 호스트로 빠져나가는 기술 |
| 볼륨 마운트 | 컨테이너 내부에서 접근 가능한 호스트 파일 시스템 경로 |
| 이미지 히스토리 | 각 레이어를 빌드하는 데 사용된 Dockerfile 명령어 기록 |
도구 및 시스템
| 도구 | 목적 |
|---|---|
| docker inspect | 상세한 컨테이너 구성 및 상태 정보 |
| docker diff | 실행/중지된 컨테이너에서의 파일 시스템 변경 사항 표시 |
| dive | 대화형 Docker 이미지 레이어 분석 도구 |
| container-diff | Google의 컨테이너 이미지 내용 비교 도구 |
| Trivy | 컨테이너 이미지 및 파일 시스템 취약점 스캐너 |
| docker-explorer | 오프라인 Docker 아티팩트 분석을 위한 포렌식 도구 |
| Sysdig | 컨테이너 런타임 보안 모니터링 및 포렌식 |
| Falco | 컨테이너 및 쿠버네티스를 위한 런타임 위협 탐지 |
일반적인 시나리오
시나리오 1: 웹 애플리케이션 컨테이너 침해 컨테이너 파일 시스템을 내보내고, 웹 루트에서 웹쉘을 식별하고, 악용 시도에 대한 액세스 로그를 분석하고, 추가된 파일 및 수정된 구성을 확인하고, C2 통신을 위한 네트워크 연결을 검사하고, 권한 상승 경로에 대한 컨테이너 기능을 검토합니다.
시나리오 2: 악성 이미지를 통한 공급망 공격 dive로 이미지 레이어를 분석하여 어떤 레이어가 악성 콘텐츠를 추가했는지 식별하고, container-diff를 사용하여 공식 기본 이미지와 비교하고, 의심스러운 RUN 명령어에 대한 이미지 히스토리를 확인하고, 내장된 백도어 및 암호화폐 채굴기를 스캔하고, 레지스트리 로그에서 이미지 풀을 추적합니다.
시나리오 3: 컨테이너 탈출 조사 컨테이너가 특권 모드로 실행되었거나 위험한 Capabilities를 가지고 있는지 확인하고, 승인되지 않은 접근에 대한 호스트 파일 시스템 마운트 지점을 검사하고, Docker-in-Docker 남용을 가능하게 하는 Docker 소켓 마운트를 검토하고, 컨테이너 탈출 징후에 대한 호스트 시스템 로그를 분석하고, 커널 익스플로잇 아티팩트를 확인합니다.
시나리오 4: 컨테이너 환경의 크립토재킹 CPU 사용량이 높은 컨테이너를 식별하고, 컨테이너 이미지를 내보내고 채굴 바이너리를 분석하고, 레지스트리에서 승인되지 않은 이미지를 확인하고, rogue 배포에 대한 컨테이너 생성 이벤트를 검토하고, 마이닝 풀 통신을 위한 네트워크 연결을 검사합니다.
출력 형식
Docker 컨테이너 포렌식 요약:
컨테이너: abc123def456 (nginx-app)
이미지: company/web-app:v2.1
상태: 실행 중 (시작: 2024-01-10 09:00 UTC)
호스트: docker-host-01.corp.local
보안 구성:
특권 모드: 아니오
추가된 Capabilities: NET_ADMIN (경고)
볼륨 마운트: /var/log -> /host-logs (RW)
네트워크 모드: 브리지
사용자: root (경고)
파일 시스템 변경 사항:
추가됨: 23개 파일 (5개 의심)
변경됨: 12개 파일 (2개 의심)
삭제됨: 0개 파일
의심스러운 발견 사항:
/tmp/reverse.sh - 리버스 쉘 스크립트 (추가됨)
/var/www/html/.hidden/shell.php - PHP 웹쉘 (추가됨)
/etc/crontab - 수정됨 (지속성 cron 항목 추가)
/root/.ssh/authorized_keys - 수정됨 (승인되지 않은 키 추가)
취약점 스캔:
치명적: 3개 (기본 이미지의 CVE-2024-xxxx)
높음: 12개
중간: 34개
증거: /cases/case-2024-001/docker/