
LID — Linux Integrity Drift: eBPF 경로명 재작성을 통한 AppArmor 우회. 감사(Audit) 흔적이 전혀 없는 Pre-LSM 시스템콜 인자 조작. "리눅스는 죽어가고 있다"
```
██╗ ██╗██████╗
██║ ██║██╔══██╗
██║ ██║██║ ██║
██║ ██║██║ ██║
███████╗██║██████╔╝
╚══════╝╚═╝╚═════╝
```
— "리눅스는 죽어가고 있다" —
LSM 보안 보장을 우회하는 커널 코드 경로의 체계적 발견
성문은 부서진 적이 없다. 그저 돌아서 지나갔을 뿐이다.
Linux Security Module 프레임워크에는 20년 이상 유지되어 온 하나의 핵심 보장이 있습니다:
보안 모듈은 제한을 추가할 수만 있습니다. 제한을 제거할 수는 없습니다.
이 보장은 정확합니다. LID는 이를 깨뜨리지 않습니다.
LID는 LSM 후크를 완전히 우회하는 커널 코드 경로를 찾습니다 — LSM 프레임워크에 묻지 않고 보안에 민감한 작업을 수행하는 서브시스템입니다. 보안 검사 자체는 정확합니다. 문제는 커널이 결코 묻지 않는다는 것입니다.
각 발견에는 혼동해서는 안 되는 두 가지 별개의 차원이 있습니다:
구조적 사각지대입니다. 핵심 질문은 "공격자가 이를 악용할 수 있는가?"가 아니라 다음과 같습니다:
보안에 민감한 작업이 발생했는데 강제 계층이 이를 아예 평가조차 하지 않는다면, 오늘날 공격자가 실제로 악용할 수 있는지 여부와 무관하게 가시성 격차가 존재합니다. 이는 규정 준수, 포렌식, 심층 방어 가정에 중요합니다.
실제 환경에서의 악용 가능성에 대한 질문입니다:
이 둘은 서로 다릅니다. 어떤 발견은 실질적 권한 상승(공격자에게 이미 root 필요)이 아니더라도 심각한 가시성 격차(모니터링이 맹목적임)일 수 있습니다. 반대로, 어떤 발견은 최소한의 전제 조건으로 직접적인 상승 경로가 될 수 있습니다.
| 발견 | 가시성 격차 | 실질적 상승 경로 |
|---|---|---|
| LID-001 | 심각 — AppArmor가 아무것도 보지 못함, 감사 로그 비어 있음, 포렌식 추적 0건 | 제한적 — root 또는 CAP_BPF+CAP_PERFMON 필요(이미 권한 보유). 권한 상승은 아님. 영향: 정책 우회 + 감사 맹목성. |
| LID-002 | 높음 — security_file_receive()가 결코 호출되지 않음, fd 전송이 모든 LSM에 보이지 않음 | 높음 — io_uring을 통해 비특권 사용자 공간에서 작동. 어떤 권한도 없이 LSM 강제 경계를 넘음. |
| LID-003 | 높음 — security_sb_mount() 우회됨, AppArmor 마운트 정책은 죽은 코드 | 중간 — 마운트 네임스페이스 접근 필요(사용자 네임스페이스의 CAP_SYS_ADMIN). 많은 컨테이너 구성에서 가능. |
| LID-004 | 심각 — AppArmor가 아무것도 보지 못함, BPF 후크 0개(0/9), BPF 토큰 작업에 대한 감사 추적 없음 | 중간 — 호스트의 bpffs 위임 + 사용자 네임스페이스의 CAP_BPF 필요. BPF 위임이 있는 컨테이너 런타임(LXD/Incus)에서 가능. |
| LID-005 | 중간 — 컨테이너 인터페이스의 tc egress 분류기가 AF_XDP 트래픽을 결코 평가하지 않음 | 제한적 — 컨테이너 eth0의 tc egress 우회(검증됨). Cilium은 우회되지 않음 — 노드 측 veth ingress에서 강제 + 소스 IP 검증(테스트됨). 영향은 tc 전용 egress 필터링을 사용하는 순수 Docker 구성으로 제한됨. |
각 발견에는 특정 커널/구성/권한 요구 사항이 있습니다. 환경이 일치하지 않으면 해당 발견은 재현되지 않습니다.
| 조건 | 요구 사항 | 비고 |
|---|---|---|
| 커널 버전 | 5.x+ | 5.15, 6.1, 6.6, 6.8에서 테스트됨 |
CONFIG_BPF_SYSCALL | =y | 모든 주요 배포판에서 기본값 |
CONFIG_BPF_KPROBE_OVERRIDE | =y | Ubuntu/Debian은 활성화하지만 RHEL은 아님 |
CONFIG_SECURITY_APPARMOR | =y | 대상 LSM은 AppArmor여야 함 |
| AppArmor 프로필 | Enforcing, 대상 경로에 대한 deny 규칙 | 모든 경로 기반 deny 규칙에서 작동 |
| 권한 | root 또는 CAP_BPF + CAP_PERFMON | 비특권으로 실행 불가 |
kernel.lockdown | none 또는 integrity | confidentiality 모드는 kprobe 연결을 차단함 |
kernel.unprivileged_bpf_disabled | 무관 | 어차피 CAP_BPF 필요 |
fs.protected_hardlinks | 교차 사용자 링크의 경우 0 | 1(기본값)은 동일 사용자 하드 링크는 여전히 허용 |
| AppArmor 대신 SELinux | 작동하지 않음 | SELinux는 경로명 기반이 아닌 inode 기반 |
| 조건 | 요구 사항 | 비고 |
|---|---|---|
| 커널 버전 | 6.0+ | IORING_MSG_SEND_FD는 6.0에서 추가됨 |
CONFIG_IO_URING | =y | 모든 주요 배포판에서 기본값 |
| 권한 | 없음 | 비특권 사용자 공간에서 작동 |
io_uring_disabled sysctl | 0 (기본값) | 2는 비특권을 차단, 1은 모두 차단 |
| 대상 LSM | 모두 (SELinux, AppArmor, Smack) | security_file_receive()는 범용 LSM 후크 |
kernel.lockdown | 무관 | BPF 미사용 |
| 조건 | 요구 사항 | 비고 |
|---|---|---|
| 커널 버전 | 6.9+ | BPF 토큰은 6.9에서 도입됨 |
CONFIG_BPF_SYSCALL | =y | 모든 주요 배포판에서 기본값 |
CONFIG_SECURITY_APPARMOR | =y | Ubuntu/Debian 기본값 |
| 위임이 있는 bpffs | 예 | 호스트가 delegate_* 옵션으로 마운트해야 함 |
| 권한 | 사용자 네임스페이스의 CAP_BPF | userns root가 손쉽게 보유 가능 |
| AppArmor 대신 SELinux | 영향 없음 | SELinux는 9개의 BPF 후크를 모두 구현 |
| 조건 | 요구 사항 | 비고 |
|---|---|---|
| 커널 버전 | 5.2+ | fsopen/fsmount는 5.2에서 도입됨 |
CONFIG_SECURITY_APPARMOR | =y | AppArmor만 영향받음 |
| 권한 | 사용자 네임스페이스의 CAP_SYS_ADMIN | unshare -m으로 가능 |
| AppArmor 대신 SELinux | 작동하지 않음 | SELinux는 security_sb_kern_mount()를 구현 |
| 컨테이너 런타임 | seccomp 필터에 따라 다름 | Docker 기본 seccomp는 fsopen을 차단 — Podman/LXC는 차단하지 않을 수 있음 |
| 조건 | 요구 사항 | 비고 |
|---|---|---|
| 커널 버전 | 4.18+ | AF_XDP는 4.18에서 도입됨 |
CONFIG_XDP_SOCKETS | =y | 모든 주요 배포판에서 기본값 |
| 권한 | CAP_NET_RAW만 | Docker, Kubernetes 파드에서 기본값 |
| 컨테이너 런타임 | Docker, K8s, LXC | 기본 capability 집합 |
| tc 기반 네트워크 정책 | 예 | Cilium eBPF, Calico, tc u32/flower |
| 환경 | LID-001 | LID-002 | LID-003 | LID-004 | LID-005 |
|---|---|---|---|---|---|
| Ubuntu 22.04+ (AppArmor, 기본값) | 작동 | 작동 | 작동 | 작동 (6.9+) | 작동 |
| Debian 12+ (AppArmor) | 작동 | 작동 | 작동 | 작동 (6.9+) | 작동 |
| RHEL/Fedora (SELinux) | 아니요 | 작동 | 아니요 | 아니요 | 작동 |
lockdown=confidentiality | 아니요 | 작동 | 작동 | 부분적으로 | 작동 |
| 비특권 사용자 | 아니요 | 작동 | 사용자 네임스페이스에 따라 다름 | bpffs 위임에 따라 다름 | 아니요 |
| 컨테이너 (CAP_BPF 없음) | 아니요 | io_uring에 따라 다름 | seccomp에 따라 다름 | 아니요 | 작동 |
| 컨테이너 (CAP_NET_RAW 제거됨) | 아니요 | 상황에 따라 다름 | 상황에 따라 다름 | 아니요 | 아니요 |
| ID | 벡터 | 대상 | 발생하는 일 |
|---|---|---|---|
| LID-001 | eBPF kprobe 경로명 재작성 | AppArmor | kprobe가 copy_from_user 이전에 파일명을 재작성 → AppArmor가 잘못된 경로를 검사함 |
| LID-002 | io_uring MSG_RING SEND_FD | SELinux, AppArmor, Smack | fd 전송이 security_file_receive()를 건너뜀 — 다른 모든 fd 전송은 이를 호출함 |
| LID-003 | 새 마운트 API (fsopen/fsmount) | AppArmor | security_sb_mount()가 결코 호출되지 않음 — AppArmor의 유일한 마운트 후크가 우회됨 |
| LID-004 | BPF 토큰 위임 | AppArmor, Smack | BPF 후크 0개 — 토큰 생성, 사용, capability 위임이 완전히 보이지 않음 |
| LID-005 | AF_XDP __dev_direct_xmit | tc egress, Cilium, Calico | 기본 Docker의 copy-mode TX가 tc 분류기를 우회 — 스푸핑된 패킷이 브리지에 도달함 |