Copy Fail – CVE-2026-31431
(copy.fail)- 비특권 로컬 사용자가
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.confrmmod 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경로를 통과하지 않음
- 이들은 커널 암호화 API를 직접 사용하며
- 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
- Xint blog write-up
- 같은 스캔에서 다른 고위험 버그들도 발견됐고, 이들은 아직 coordinated disclosure 중임
Hacker News 의견들
-
Linux 커널 crypto 코드를 다루는 입장에서, 주기적으로 터지는 AF_ALG 익스플로잇은 정말 답답함
AF_ALG는 충분한 리뷰 없이 오래전에 커널에 들어갔고, 구조가 너무 복잡한 데다 비특권 userspace 프로그램에 거대한 공격 표면을 열어줌
게다가 거의 불필요함. userspace에는 이미 자체 암호화 코드가 있고, 커널 crypto 코드는 원래 dm-crypt 같은 커널 내부 사용처를 위한 것임
이번 익스플로잇의 authencesn도 사실상 IPsec 내부 구현 디테일인데, 이걸 범용 userspace 암복호화 API로 노출한 건 애초에 잘못됐다고 봄
Linux 커널 설정을 관리한다면 모든 CONFIG_CRYPTO_USER_API_* 옵션을 끄는 걸 강하게 권장함
그렇게만 했어도 이번 버그뿐 아니라 과거와 미래의 AF_ALG 버그 상당수는 악용 불가능했을 것임
혹시 userspace 프로그램이 깨진다면 userspace crypto 코드로 옮기도록 돕는 편이 맞고, 실제로 이미 그렇게 바뀐 것도 있음
애초에 AF_ALG는 익스플로잇 말고는 쓰임새가 많지 않았음
예전엔 이런 userspace API가 그럭저럭 용납됐을지 몰라도, syzbot이나 LLM 보조 버그 탐지가 있는 지금은 더는 버티기 어려움- AF_ALG가 뭔지 몰라서 찾아보니 https://www.chronox.de/libkcapi/html/ch01s02.html이 나오고, 거기엔 나름의 존재 이유가 적혀 있었음
커널 모드에서만 접근 가능한 하드웨어 가속기를 userspace에서 쓰게 해준다는 점, 키를 애플리케이션 메모리에 오래 남기지 않고 커널에 넘길 수 있다는 점, 임베디드처럼 메모리가 빠듯한 환경에서 userspace crypto 라이브러리보다 footprint를 줄일 수 있다는 점이 근거로 제시됨
이게 충분히 좋은 정당화인지는 판단 못 하겠지만, 최소한 이유 자체는 있긴 함 - 이게 대체 어떻게 커널에 들어갔는지 궁금함
Linus가 커널에 뭐 넣을지 꽤 까다로운 편으로 알려져 있어서, 이런 API가 들어간 배경은 흥미로운 이야기일 듯함 - AF_ALG는 커널 crypto API를 파일 디스크립터로 노출하는 Linux 소켓 인터페이스임
해시와 암호화를 일반적인read(2)/write(2)호출로 다루게 해줌 - 이 커널 옵션을 끄면 어떤 소프트웨어가 깨지는지가 가장 궁금함
- AF_ALG가 뭔지 몰라서 찾아보니 https://www.chronox.de/libkcapi/html/ch01s02.html이 나오고, 거기엔 나름의 존재 이유가 적혀 있었음
-
공개 과정에서 뭔가 혼선이 있었던 듯함
벤더들이 이 취약점을 심각하게 다루지 않고 있고, 그래서 여러 배포판에서 아직 패치가 안 된 상태로 남아 있음
https://access.redhat.com/security/cve/cve-2026-31431에는 "Moderate severity", "Fix deferred"로 적혀 있었고, Debian, Ubuntu, SUSE 추적 페이지도 비슷하게 보였음- 패치가 커널에 들어간 시점부터 이미 몇 주 전부터 공격자나 관찰자에게는 사실상 알려진 상태였음
그런데 upstream이 이걸 취약점으로 명확히 커뮤니케이션하지 않았고, Linus와 Greg는 커널에서 그런 분류 자체를 개념적으로 크게 중시하지 않는 편임 - 배포판들이 중간 위험도로 보는 이유는 원격 코드 실행이 아니라 로컬 접근이 필요하기 때문인 듯함
그래도 로컬에서 root 권한 상승이 가능하니 일반적으론 높은 우선순위로 보는 게 맞아 보임
https://ubuntu.com/security/cves/about#priority - RedHat은 지금은 Important severity와 Affected로 바꿔 둔 상태임
- Ubuntu 자체 가이드라인을 보면 이건 high priority로 가야 맞아 보이는데, 실제 표기는 medium이라 일관성이 없어 보임
- 패치가 커널에 들어간 시점부터 이미 몇 주 전부터 공격자나 관찰자에게는 사실상 알려진 상태였음
-
어느 커널 버전이 취약하고 어느 버전에서 패치됐는지가 본문에 바로 안 적혀 있는 건 아쉬움
특히 이건 builtin 모듈이라rmmod로 쉽게 뺄 수도 없어서 더 그럼
Fedora 44의 kernel 6.19.14가 취약한지 확인하려고 찾아보다가 linux-cve-announce 메일링리스트 글 https://lore.kernel.org/linux-cve-announce/2026042214-CVE-2026-31431-3d65@gregkh/T/#u를 찾았음
거기엔 6.18.22, 6.19.12, 7.0에서 각각 해당 커밋으로 수정됐다고 적혀 있어서 참고가 됨 -
권장된 완화책대로 커널 모듈
algif_aead를 modprobe 설정으로 막았는지 확인하고 싶다면, 난독화된 셸 코드를 통째로 돌릴 필요는 없음
아래처럼 Python 몇 줄로 모듈이 실제 로드되는지만 읽기 좋게 확인할 수 있음
python3 -c 'import socket; s = socket.socket(socket.AF_ALG, socket.SOCK_SEQPACKET, 0); s.bind(("aead","authencesn(hmac(sha256),cbc(aes))")); print("algif_aead probably successfully loaded, mitigation not effective; remove again with: rmmod algif_aead")'
완화가 제대로 적용됐다면modprobe algif_aead도 에러로 실패해야 함 -
영향을 받는 운영체제에서 완전 자율 AI 에이전트를 일반 사용자 권한으로 돌리는 사람은 아무도 없겠지
제로데이 프롬프트 인젝션과 결합되면 꽤 재앙적일 수 있음- 내 에이전트는 이미 root로 실행 중이라 문제를 못 보겠음
- 다행히 우리가
curl | sh로 설치하는 걸 업계 표준처럼 만들진 않았지
-
LPE는 local privilege escalation 뜻임
보안 쪽 약어가 너무 많고, 문맥으로 유추는 가능했지만 그래도 처음엔 풀어서 써줬으면 좋겠음- LPE는 보안 커뮤니티 안에서는 꽤 널리 알려진 약어라서, 안 풀어 썼다고 아주 심하다고 보긴 어려움
다만 더 넓은 독자를 상대로 쓴 글이라면 명시적으로 정의해 주는 편이 좋다는 데는 동의함
게다가 이 글 전체가 AI 생성물처럼 보이기도 함 - 넓은 독자를 위한 글쓰기라면 약어를 먼저 풀어 쓰는 게 기본인데, LLM들은 이런 가이드를 잘 따르지 않는 편임
- LPE는 보안 커뮤니티 안에서는 꽤 널리 알려진 약어라서, 안 풀어 썼다고 아주 심하다고 보긴 어려움
-
이건 좀 웃김
페이지엔 RHEL 14.3에서 동작한다고 적혀 있는데, 그런 버전은 존재하지 않음
현재 RHEL은 10.x라서 무슨 TARDIS라도 탄 줄 알았음- 14.3은 RHEL 버전이 아니라 Red Hat 쪽 GCC 버전 표기에서 온 것 같음
gcc (GCC) 14.3.1 20250617 (Red Hat 14.3.1-2)처럼 찍히는 경우가 있고, 아래 예시들에도 비슷한 흔적이 보임
https://github.com/anthropics/claude-code/issues/40741
https://docs.oracle.com/en/database/oracle/tuxedo/22/otxig/software-requirements-red-hat-enterprise-linux-10-64-bit.html - 같은 줄에
6.12.0-124.45.1.el10_1도 적혀 있는데, 그건 명백히 RHEL 10 커널임
이런 류의 오타는 오히려 사람이 흔히 냄
복붙한 긴 숫자는 정확한데, 쉬운 숫자는 손으로 치다가 틀리는 식임 - 미안하지만 그건 수정될 예정임
이슈를 설명하려고 정보를 급히 모으는 과정이 있었고, 맞음, 마케팅 성격도 있었음
그래서 사소한 실수가 좀 들어갔고, 짚어줘서 고맙다고 봄 - 맞음, "직접 검증한 배포판: RHEL 14.3"라는 문구를 보는 순간 적어도 릴리스 페이지는 AI 슬롭처럼 느껴졌음
https://access.redhat.com/articles/red-hat-enterprise-linux-release-dates
페이지 맨 아래의 "Talk to our security experts"까지 보고 나니, 그 보안 전문가 이름이 혹시 Claude 아닐까 싶은 기분도 듦
- 14.3은 RHEL 버전이 아니라 Red Hat 쪽 GCC 버전 표기에서 온 것 같음
-
RHEL 9/10에서는 algif_aead가 모듈이 아니라 builtin이라 unload할 수 없었음
그래서 차선책으로 systemd를 통해 AF_ALG를 막는 방법을 찾았고, 노출된 서비스마다 drop-in이 필요함
보통 핵심인sshd와user@를 다루는 Ansible 플레이북도 있음
https://gist.github.com/m3nu/c19269ef4fd6fa53b03eb388f77464da- RHEL 9/10에서는
initcall_blacklist=algif_aead_init를 커널 부트 옵션에 넣고 재부팅하는 방법도 가능했음
그렇게 하니 익스플로잇이 더는 동작하지 않았음 - 나도 비슷한 방향을 떠올렸지만, 서비스마다 막는 건 두더지 잡기처럼 느껴짐
cronjob,slurmjob같은 다른 실행 경로는 어쩔지 걱정되고, 개별 서비스마다 넣는 대신 systemd 차원에서 전체 프로세스가 상속받게 만드는 방법이 있으면 좋겠음
- RHEL 9/10에서는
-
이 익스플로잇은 SUID 바이너리를 바꿔치기해서 PID 0으로 실행되게 만드는 방식 같음
그런데 사이트는 Kubernetes / container clusters, CI runners & build farms 탈출까지 된다고 주장하면서도, 실제로 컨테이너나 특히 user namespace 탈출을 뒷받침하는 설명은 안 보임
rootless Podman에서 돌려보니 예상대로 컨테이너를 빠져나오지 못했음
또 "2017년 이후 출시된 모든 Linux 배포판을 root로 만든다"는 주장도 했지만, 실제 테스트는 네 개뿐이고 Alpine에서는 동작하지 않았음- 사이트 쪽에서도 자세한 설명은 곧 올라올 거라고 했으니, 아마 추가 단계나 수정사항이 part 2에서 나올 듯함
"Next: "From Pod to Host," how Copy Fail escapes every major cloud Kubernetes platform."라고 예고해 둠 - 이 취약점은 읽을 수 있는 파일의 메모리 바이트를 덮어쓸 수 있으니, 여러 환경을 탈출할 여지는 충분히 상상 가능함
- 2017 이후 전 배포판이라는 주장은 이 취약점이 2017년 하반기 커밋에서 들어왔다는 사실에 기반한 것 같음
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=72548b093ee3
다만 실제 악용 가능성은 최신 메이저 릴리스인지, 오래된 브랜치의 유지보수 커널인지에 따라 달라질 것임 - 이 글은 스스로 신뢰도를 꽤 깎아먹었음
그래도 PoC를 24.04 인스턴스에서 직접 돌려보니 취약점 자체는 진짜 같고, 충분히 큰 이슈처럼 보임
다만 영향받는 배포판 수는 주장보다 훨씬 적어 보이고, 2017년 이후 모든 배포판이라는 표현과는 거리가 큼
예를 들어 Ubuntu는 내 해석이 맞다면 16.04 EOL에도 약간 영향이 있고, 실제 주요 영향은 최근 배포하기 시작한 vendor 커널인linux-gcp,linux-oracle-6.7같은 6.17 계열 쪽으로 보임 - rootless 컨테이너 안에서라도 실제 UID 0까지 올라갈 수 있으면 그다음 탈출은 가능함
다만 추가 단계가 필요하고, Alpine도 근본 취약점은 있을 수 있지만 스크립트 조정이 필요한 것뿐일 수 있음
결국 이건 완성형 범용 익스플로잇이 아니라 PoC임
- 사이트 쪽에서도 자세한 설명은 곧 올라올 거라고 했으니, 아마 추가 단계나 수정사항이 part 2에서 나올 듯함
-
페이지 자체는 좀 vibecoded 같고 광고 티도 나지만, 취약점은 실제고 위험도도 높아 보임
오늘 큰 보안 업데이트가 왜 왔는지 설명이 되니, 업데이트 우선순위를 높여야겠음- 이건 꽤 노골적인 광고이긴 한데, 개인적으론 나쁘지 않은 광고라고 봄
실제 버그를 찾아내고 패치하는 식으로 OSS 생태계에 의미 있는 기여를 하면서, 동시에 자기네 보안 도구를 파는 구조임 - 이 사람들은 굳이 광고가 없어도 이미 일감이 넘칠 것 같음
다만 누가 요즘 웹페이지를 손으로 일일이 만드나 싶고, 특히 커널 개발자라면 더 그럴 듯함
- 이건 꽤 노골적인 광고이긴 한데, 개인적으론 나쁘지 않은 광고라고 봄