오픈소스 팀 프로젝트 · purpleBPF/purplebpf · 담당 파트는 탐지 룰
공격을 자동으로 생성해 실제 커널에서 실행하고, 방어가 그중 얼마를 잡는지 측정한 뒤, 놓친 것(FN)만 골라 다시 그쪽을 겨냥하는 폐쇄 루프다.
| |
라벨링 비용이 0이 되는 지점
탐지 커버리지를 측정하려면 “무엇을 쐈는지"에 대한 정답이 있어야 한다. 보통은 이걸 사람이 붙인다.
여기서는 공격을 직접 생성하고 직접 실행하기 때문에 정답을 이미 알고 있다. 생성한 체인이 그대로 라벨이다. 그래서 재현율을 추정이 아니라 계산으로 얻는다.
타깃은 보안 전담 인력이 없는 1인 스타트업·중소기업이다. 이쪽의 클라우드 네이티브 전환이 활발하다는 데 착안했다.
축 세 개를 다시 판정한 결과
처음 잡은 3축은 T1611(컨테이너 탈출), T1548(권한 상승), io_uring이었다. 근거를 다시 훑고 나서 셋 다 손봤다.
T1611 — 유지, 단 빈도로 팔지 않기
컨테이너 탈출은 정의상 커널이 강제하는 경계(마운트·PID 네임스페이스, 능력집합, cgroup)를 넘는 행위다. 그래서 “커널이 못 보는 탈출"이라는 게 존재할 수 없다. MITRE의 탐지 분석도 unshare, keyctl, mount 시스템콜을 직접 지목한다. 커널 관측 가능성 게이트를 통과하는 정도가 아니라 게이트 그 자체다.
다만 실제 공격자는 탈출하지 않고 컨테이너에 머문 채 훔친 아이덴티티로 피벗하는 쪽이 지배적이다. T1611은 고심각도 꼬리이지 헤드라인이 아니다. 그리고 CVE가 아니라 오설정 경로(privileged, hostPID+nsenter, docker.sock, hostPath, core_pattern)로 구성해야 한다. MITRE 자신의 절차 예시 다섯 건이 전부 오설정 구동이고 CVE 구동이 하나도 없다.
T1548 — 축에서 제거
T1548은 ATT&CK Containers 매트릭스에 아예 없다. 부모 기법의 플랫폼 목록에 Containers가 빠져 있고, Containers 권한상승 열은 T1068·T1611·T1612·T1053.007·T1078로 구성된다. MITRE가 이미 “컨테이너 권한상승 기법이 아니다"라고 큐레이션을 끝낸 것이다.
더 결정적인 건 측정 불가 구간이 섞여 들어온다는 점이다. 하위기법 .005(Temporary Elevated Cloud Access)는 탐지 근거가 전부 CloudTrail 계열 감사 이벤트라 커널 아티팩트가 0이다. 부모 기법 단위로 축을 잡으면 이게 생성 공간에 들어오고, 그때 나오는 FN은 “방어가 놓쳤다"가 아니라 “관측기가 물리적으로 볼 수 없었다"가 된다. 그 FN이 다음 세대 생성 목표로 재투입되면 루프가 조용히 오염된다.
남는 .001(setuid/setgid)만 T1611 체인의 탈출 이후 단계로 회수했다. 호스트 파일시스템에 chmod 4755를 걸고 execve하는 건 현실적이고 관측도 풍부하다.
io_uring — 축이 아니라 채널
io_uring에는 exec 옵코드가 없다. 프로세스 실행이 불가능하니 io_uring만으로는 탈출도 권한 상승도 완결되지 않고, 트리거는 항상 평범한 execve로 노출된다. 축으로 세우면 완결되지 않는 축이 된다.
대신 채널로 두면 이 프로젝트에서 제일 값어치가 크다. Tetragon 공식 문서가 io_uring과 aio를 명시하면서 “모든 시스템콜을 감시하는 대신 security_file_permission 훅에 걸겠다"고 스스로 적어놨기 때문이다. 같은 ground truth 공격을 syscall 정책 아래에서는 FN으로, LSM 정책 아래에서는 TP로 만들 수 있다.
대표 실험 — 같은 공격, 두 채널
| |
동일 y_true, 두 실행 채널, 두 정책군. 네 칸짜리 커버리지 행렬이 나온다. 탐지 여부의 이진 주장이 아니라 커버리지 델타라서 발표 가능한 결과가 된다.
단, 도달성을 전제조건으로 게이트해야 한다. Docker 25.0.0+와 containerd 2.0+는 기본 seccomp에서 io_uring 시스템콜을 뺐지만 containerd 1.7.x에는 백포트되지 않았고, Kubernetes는 seccompDefault를 켜지 않으면 프로파일이 아예 안 붙는다. 파드 안에서 grep Seccomp /proc/1/status로 확인하고 0이 아니면 그 레그는 건너뛰고 런 메타에 기록한다.
담당한 것 — 탐지 룰
관측판과 차단판을 분리해서 쓴다. 같은 기법이라도 랩에서 재는 것과 실제로 막는 건 요구가 다르다.
| |
rule_mapping.yaml은 Tetragon 이벤트를 ATT&CK 번호로 태깅하는 매핑이다. 탐지 출처가 두 갈래라 섹션이 나뉜다.
| |
stream_rules를 따로 둔 이유가 있다. 기본 센서가 모든 exec 이벤트를 이미 내보내는데 여기에 TracingPolicy를 또 얹으면 같은 이벤트가 두 번 올라와서 비용만 는다. 유저스페이스에서 판정하는 게 맞다.
기법 귀속 키로는 policy_name과 tags를 쓴다. function_name을 쓰면 안 된다. ProcessTracepoint에는 그 필드가 없고 subsys/event를 대신 쓰기 때문에, Kprobe·Tracepoint·Lsm 세 이벤트 타입에 다 존재하는 필드로 통일해야 한다.
측정이 무효가 되는 조건
FN을 세기 전에 그 FN이 진짜인지부터 확인해야 한다.
- 이벤트 손실 — 링버퍼, 유저스페이스 큐, export 레이트리밋 등 최소 5개 경로에서 이벤트가 사라진다. 전부 Prometheus 카운터로 잡히므로 공격 실행 전후로 전량 스크레이프하고, 델타가 하나라도 0이 아니면 그 런은 INVALID로 표시한다. FN으로 세지 않는다.
- LSM 팔의 무성 실패 —
CONFIG_BPF_LSM=y와 부팅 파라미터가 맞아도 정책 로드가 실패하는 사례가 보고돼 있다. 설정 확인이 아니라 실제로 이벤트가 나오는지로 검증한다. - 자격증명 필드의 시점 오류 — Tetragon은 UID와 capability를 exec 시점에만 기록한다. 임의 kprobe 이벤트에 붙은 uid/cap은 그 이벤트 시점의 값이 아니다. 권한상승 판정은
commit_creds,security_capset같은 명시 훅으로만 한다. - 측정기 자체의 신뢰 경계 — root 등가 공격자는 BPF 프로그램을 언로드할 수 있다. 탈출 성공 이후 시점의 측정치는 신뢰 구간에서 뺀다. T1611 체인의 y_pred는 탈출 완료 직전까지만 유효로 본다.
- FP 베이스라인 부재 — 공격을 안 쏘는 정온 구간 런을 먼저 돌려 배경 이벤트 프로파일을 만들지 않으면 FP를 정의할 수 없다. 토큰 read는 kubelet과 SDK가 상시 하는 행위다.
범위를 얼버무리면 안 되는 지점
이 프로젝트는 “클라우드 공격 탐지 커버리지"를 측정한다고 말하면 안 된다.
빈도로 보면 클라우드 침해의 초기 침투는 압도적으로 아이덴티티 계열이고, 그쪽은 노드 커널 활동이 0이다. 측정할 수 있는 건 초기 침투 이후 워크로드 내부에서 벌어지는 행위다. 명시하면 강점이고, 얼버무리면 리뷰에서 제일 먼저 뚫린다.