Copy Fail – CVE-2026-31431
6 hours ago
4
- 비특권 로컬 사용자가 authencesn, AF_ALG, splice()를 연결해 읽기 가능한 파일의 페이지 캐시 4바이트 쓰기를 만들고, 이를 통해 root 권한까지 올릴 수 있음
- 커널별 오프셋이나 레이스 조건 없이 732바이트 Python 스크립트 하나로 여러 Linux 배포판에서 그대로 동작하며, 같은 익스플로잇으로 root shell 획득이 가능함
- 영향 범위는 패치 전까지의 주요 Linux 배포판 대부분이며, 기본 설정에서 활성화된 AF_ALG 때문에 2017년부터 패치 시점까지 넓게 노출돼 있음
- 멀티테넌트 호스트, Kubernetes / 컨테이너 클러스터, CI 러너, 사용자 코드 실행형 Cloud SaaS에서는 일반 계정이나 pod 하나가 호스트 root로 이어질 수 있어 우선 패치가 필요함
- 대응은 메인라인 커밋 a664bf3d603d가 포함된 커널 패치가 우선이며, 패치 전에는 algif_aead 비활성화와 신뢰되지 않은 워크로드에 대한 AF_ALG 차단이 중요함
취약점 개요
- 직선형 로직 결함 하나를 authencesn, AF_ALG, splice()로 연결해 페이지 캐시 4바이트 쓰기까지 이어지는 로컬 권한 상승이 가능함
- 커널별 오프셋이나 레이스 윈도 없이 732바이트 Python 스크립트 하나로 2017년 이후 배포된 Linux 배포판 전반에서 동일하게 동작함
- 같은 익스플로잇 바이너리가 수정 없이 여러 배포판에서 root shell을 획득함
- 비특권 로컬 계정만 있으면 되고, 네트워크 접근이나 커널 디버깅 기능, 사전 설치된 다른 프리미티브는 필요하지 않음
영향 범위
- 패치 전까지의 커널을 쓰는 주요 Linux 배포판 대부분이 범위에 들어감
- 기본 설정에서 커널 암호화 API의 AF_ALG 가 사실상 모든 메인스트림 배포판에 활성화돼 있어 2017년부터 패치 시점까지 바로 노출됨
- 직접 검증한 배포판은 Ubuntu 24.04 LTS, Amazon Linux 2023, RHEL 14.3, SUSE 16임
- Debian, Arch, Fedora, Rocky, Alma, Oracle, 임베디드 계열도 영향을 받는 커널을 쓰면 동일하게 동작함
우선 패치가 필요한 환경
- 멀티테넌트 Linux 호스트에서는 여러 사용자가 같은 커널을 공유하므로 임의 사용자 계정이 곧바로 root가 될 수 있음
- Kubernetes / 컨테이너 클러스터에서는 페이지 캐시가 호스트 전체에 공유돼 pod 하나가 노드를 장악하고 테넌트 경계를 넘을 수 있음
- CI 러너와 빌드 팜에서는 일반 사용자 권한으로 실행된 신뢰되지 않은 PR 코드가 러너에서 root가 될 수 있음
- 사용자 코드 실행형 Cloud SaaS에서는 테넌트가 올린 컨테이너나 스크립트가 호스트 root로 이어질 수 있음
- 단일 테넌트 서버는 내부 LPE 성격이 강하고 웹 RCE나 탈취된 자격증명과 연결될 수 있음
- 단일 사용자 노트북과 워크스테이션은 긴급도가 더 낮지만, 로컬 코드 실행이 곧 root 승격으로 이어질 수 있음
공개된 PoC와 사용 방식
- PoC는 방어 측이 시스템 검증과 벤더 패치 확인에 활용할 수 있도록 공개돼 있음
- copy_fail_exp.py 는 Python 3.10+ 표준 라이브러리 os, socket, zlib만 사용함
- 기본 대상은 /usr/bin/su 이며 argv[1]로 다른 setuid 바이너리를 넘길 수 있음
- 페이지 캐시 안의 setuid 바이너리를 수정하며, 변경은 재부팅 후 유지되지 않지만 획득한 root shell은 실제로 동작함
- Issue tracker
완화 방법
- 가장 먼저 커널 패치가 필요하며, 메인라인 커밋 a664bf3d603d 가 포함된 배포판 커널로 업데이트해야 함
- 이 패치는 2017년 도입된 algif_aead의 in-place 최적화를 되돌려 페이지 캐시 페이지가 writable destination scatterlist에 들어가지 않게 만듦
- 패치 전에는 algif_aead 모듈 비활성화가 권장됨
- echo "install algif_aead /bin/false" > /etc/modprobe.d/disable-algif.conf
- rmmod algif_aead 2>/dev/null || true
- 신뢰되지 않은 워크로드를 돌리는 컨테이너, 샌드박스, CI에서는 패치 여부와 무관하게 seccomp로 AF_ALG 소켓 생성 차단이 필요함
비활성화 시 영향
- 대부분의 시스템에서는 측정 가능한 영향이 거의 없음
- dm-crypt / LUKS, kTLS, IPsec/XFRM, in-kernel TLS, OpenSSL/GnuTLS/NSS 기본 빌드, SSH, kernel keyring crypto는 영향을 받지 않음
- 이들은 커널 암호화 API를 직접 사용하며 AF_ALG 경로를 통과하지 않음
- OpenSSL에서 afalg 엔진을 명시적으로 켠 경우, 일부 임베디드 암호화 오프로딩 경로, aead/skcipher/hash 소켓을 직접 바인딩하는 애플리케이션은 영향을 받을 수 있음
- lsof | grep AF_ALG 또는 ss -xa 로 확인 가능함
- AF_ALG를 꺼도 원래 그것을 호출하지 않던 대상은 느려지지 않으며, 사용하던 대상은 일반적인 userspace 암호화 라이브러리로 되돌아감
일반적인 Linux LPE와 다른 점
- 보통의 Linux LPE와 달리 레이스 조건이 없고 배포판별 오프셋도 필요하지 않음
- 신뢰성도 단발 100% 로 제시되며, 좁은 커널 버전 범위가 아니라 2017년부터 2026년까지의 긴 기간을 덮음
- Python 표준 라이브러리만 쓰는 732바이트 크기라서 컴파일된 페이로드나 별도 의존성이 없음
- 쓰기 경로가 VFS를 우회하고 손상된 페이지가 dirty로 표시되지 않아 디스크에는 아무것도 기록되지 않음
- 페이지 캐시가 호스트 전체에 공유되므로 단순 LPE를 넘어 컨테이너 탈출 프리미티브로도 작동함
동작 원리와 탐지 특성
- 비특권 로컬 사용자가 Linux 시스템의 읽기 가능한 파일 페이지 캐시에 제어 가능한 4바이트를 쓸 수 있고, 이를 root 획득에 활용할 수 있음
- 디스크의 실제 파일이 아니라 메모리 내 페이지 캐시가 표적이며, /usr/bin/su의 캐시 복사본을 바꾸면 execve 관점에서는 바이너리 자체를 바꾼 것과 같아짐
- 디스크에 변화가 없으므로 inotify가 울리지 않고, 재부팅이나 캐시 축출 후에는 원본 파일이 다시 로드됨
- sha256sum, AIDE, Tripwire 같은 일반 해시 도구는 read()를 통해 페이지 캐시를 읽으므로 캐시에 손상이 남아 있는 동안에는 기준 해시와 달라질 수 있음
- 캐시가 축출되거나 재부팅되면 해시가 다시 원래와 일치하고, 디스크 포렌식 이미지에도 수정되지 않은 파일만 남음
- IMA appraisal enforcing mode 에서 every-read measurement가 켜져 있으면 손상된 바이너리가 실행되기 전 execve 시점에 잡을 수 있음
다른 취약점과의 차이
- Dirty Pipe 와 같은 계열로, 비특권 userspace에서 페이지 캐시를 손상시키고 디스크 변경 없이 setuid 바이너리에 써서 root를 얻는다는 점은 같음
- 메커니즘은 다르며 Dirty Pipe는 pipe buffer flags 를 악용했고, Copy Fail은 chained scatterlist 경계를 넘는 AEAD scratch write 를 악용함
- Dirty Pipe는 특정 패치가 들어간 커널 5.8 이상이 필요했지만, Copy Fail은 2017년부터 2026년까지의 창을 덮음
- Dirty Cow 와 달리 TOCTOU 기반 COW 레이스를 이길 필요가 없고, 여러 차례 시도하거나 시스템을 불안정하게 만들지 않음
추가 세부 사항
- /usr/bin/su 가 필수는 아니며, 사용자가 읽을 수 있는 setuid-root 바이너리 라면 passwd, chsh, chfn, mount, sudo, pkexec도 가능함
- 원격 취약점은 아니고, 일반 사용자 권한의 로컬 코드 실행이 먼저 필요함
- 웹 RCE가 비특권 서비스 계정으로 떨어지는 경우, SSH foothold, CI 러너의 악성 PR 같은 경로와 결합하면 root로 이어질 수 있음
- 패치는 algif_aead의 in-place 최적화를 되돌려 req->src와 req->dst를 다시 분리된 scatterlist로 만듦
- 페이지 캐시 페이지는 읽기 전용 source에만 남고, 암호화가 쓸 수 있는 대상은 사용자 버퍼만 남게 됨
공개 일정과 후속 자료
- 2026-03-23 Linux 커널 보안팀에 보고됨
- 2026-03-24 초기 확인이 이뤄짐
- 2026-03-25 패치가 제안되고 리뷰됨
- 2026-04-01 메인라인에 패치가 커밋됨
- 2026-04-22 CVE-2026-31431 이 할당됨
- 2026-04-29 https://copy.fail/ 에서 공개됨
- Xint blog 기술 분석
- root cause, scatterlist 다이어그램, 2011→2015→2017 연혁, 익스플로잇 워크스루를 담고 있음
- Kubernetes 컨테이너 탈출을 다루는 Part 2는 추후 공개될 예정임
Xint Code 관련 정보
- AI-assisted 방식으로 찾았으며, 출발점은 splice()가 페이지 캐시 페이지를 crypto subsystem에 넘긴다는 점과 scatterlist의 페이지 출처가 덜 탐색된 버그 클래스일 수 있다는 인간 연구였음
- 이후 Xint Code 가 Linux crypto/ subsystem 전체 감사를 약 1시간 규모로 확장했고, 그 결과 중 가장 심각한 항목이 Copy Fail이었음
- Xint Code
-
Homepage
-
Tech blog
- Copy Fail – CVE-2026-31431