
Linux cifs.ko DACL 힙 오버플로(CVE-2026-97555)에 대한 분석, POC 및 arm64 익스플로잇 체인으로, 참조 빌드에서 uid-0 자격 증명 덮어쓰기까지 권한을 상승시킵니다.
한국어 | 简体中文
SMB 클라이언트 DACL 재작성에서 발생하는 커널 힙 오버플로. id_mode_to_cifs_acl()은
서버가 반환한 DACL을 기반으로 출력 보안 디스크립터의 크기를 산정하지만,
replace_sids_and_copy_aces()는 모든 ACE를 새로운 소유자/그룹 SID(최대 68바이트)로
재작성한다. 따라서 짧은 SID DACL을 반환하는 서버는 재작성 결과가 할당 크기를
초과하게 만들어 kmalloc 객체 경계를 넘어 쓰기를 발생시킨다.
이 저장소는 분석 자료, 교차 객체 쓰기를 재현하는 POC, 그리고 이 오버플로를 참조 빌드에서 통제된 종단 간 체인(힙 누출, 페이지 테이블 회수, 임의 PTE 쓰기, uid 0으로의 자체 자격 증명 덮어쓰기)으로 전환하는 EXP를 제공한다.
| 항목 | 값 |
|---|---|
| CVE | CVE-2026-97555 |
| 분류 | id_mode_to_cifs_acl()의 힙 버퍼 오버플로(쓰기, CWE-122) |
| 구성 요소 | cifs.ko (SMB 클라이언트) |
| 영향 받음 | 수정 사항이 없는 Linux cifs.ko (6.12 GKI 기반 Android 커널에서 검증됨) |
| 패치 | 업스트림 0ee150794c75 (스테이블 fb736ab4fb8c) |
| 참조 빌드 | Android 16 GKI 기반 6.12 커널, arm64, 4 KiB 페이지, 64바이트 struct page |
| POC | 교차 객체 쓰기 재현됨 (696바이트의 SET_INFO 보안 디스크립터) |
| EXP | 참조 빌드에서 uid-0 셸까지의 전체 체인 |
/proc/kpageflags를 읽는다. 마운트 권한을 얻는 것은
테스트 픽스처이며 버그의 일부가 아니고, 이 저장소의 범위 밖이다.uid/suid/euid/fsuid 필드만 재작성하여 다시 상승한다. capability나
securebits 필드는 건드리지 않는다. 정리는 재부팅으로 수행한다.src/exp.c의 상수(심볼 주소, vmemmap 구조, BTF 오프셋)는 참조 빌드에
속한다. 다른 빌드에서 실행하기 전에 다시 도출할 것; scripts/derive_offsets.sh 참조.id_mode_to_cifs_acl()은 dacl_ptr->size 또는
num_aces * sizeof(struct smb_ace)로 할당 크기를 계산한 뒤,
replace_sids_and_copy_aces()가 각 ACE를 새로운 소유자/그룹 SID로 재구성한다.
서버의 SID가 짧고(0-subauth) 새 SID가 68바이트일 때, 재작성된 DACL은 할당
크기보다 길어지고 build_sec_desc()가 슬랩 객체 경계를 넘어 쓰기를 수행한다.
커널의 동적 FORTIFY 계측은 인라인된 ACE 헤더 저장을 검사하지만, SID 복사는
아웃오브라인 cifs_copy_sid()를 거치므로 계측되지 않는다. 모든 ACE 헤더가
nsecdesclen - 28 안에 유지되도록 정렬된 DACL은 취약한 복사 지점에 도달한다.
cross7 서버 레이아웃(7개 ACE, 소스 크기 76/76/76/44/44/16/16)은 할당
크기를 정확히 512바이트로 만든다; 7번째 ACE의 68바이트 SID 복사는 오프셋
492에서 시작하여 객체 경계를 넘고, 재작성된 디스크립터는 696바이트로
증가한다 — 객체 경계를 184바이트 초과한다. 확장된 디스크립터는 여전히
SET_INFO로 전달되므로, 쓰기는 서버 측에서 관찰 가능하다.
fb736ab4fb8c 없이 빌드된 6.12 GKI 기반 커널을 사용하는
arm64 Android 기기.kpageflags, mount, chown)과 cifs idmap
업콜을 위한 SELinux permissive 구간(또는 동등한 정책 허용).src/poc_smb2_acl.py가 제공하는 공유를 표준 커널 클라이언트
(scripts/mount_cifs.sh)를 통해 cifsacl/modefromsid로 마운트. 마운트
자체는 권한이 필요한 픽스처이며, 테스터가 그 권한을 얻는 방법은 이
저장소의 범위 밖이다.cifs.idmap 키: 참조 환경에는
/sbin/cifs.idmap 업콜 헬퍼가 없다(src/poc.c와 src/exp.c가 이를
미리 로드하며, src/acl_key_preload.c는 단독으로 수행한다).# host
python3 src/poc_smb2_acl.py # ACL_LAYOUT=cross7 for the overflow
adb reverse tcp:1468 tcp:1468
# device (root)
sh /data/local/tmp/e2e/mount_cifs.sh 127.0.0.1 share /mnt/cifs
make && make push
/data/local/tmp/poc /mnt/cifs
예상 증거: 서버 로그에
SET_INFO security: len=696 ...(cross7)가 출력되는데, 이는 512바이트 할당보다
긴 디스크립터를 의미하며, scripts/trace_acl02.sh는 chown 전후의
id_mode_to_cifs_acl 진입/반환을 보여준다.
POC는 취약한 경로만 트리거하며, groom, leak 또는 권한 상승을 수행하지 않는다. 트리거는 커널 경계를 벗어난 쓰기를 수행한다: 레이아웃이 동적 FORTIFY 검사에 걸리는 빌드는 패닉이 발생할 수 있으므로, 재부팅할 수 있는 테스트 기기에서만 실행하라.
EXP는 참조 빌드에서 처음부터 끝까지 이어지는 전체 체인이다:
cross7 오버플로가 인접한 키 페이로드의
datalen을 덮어쓴다; keyctl_read가 라이브 pipe_buffer
배열을 포함한 64 KiB를 덤프한다. 유출된 anon_pipe_buf_ops 포인터가 KASLR 슬라이드를 제공하고
버퍼 페이지 P가 실제 struct page 타깃을 제공한다.buffer[0]이
page=P, len=0x1234, ops=default_pipe_buf_ops로 덮어써진다.P가 해제된다; base에서의
폴트가 이를 페이지 테이블 페이지로 재점유하며, 이는 __alloc_pages_noprof가
__pte_alloc에서 P를 반환했음을 증명하는 kretprobe로 게이트된다.P+16(PTE[2])에서 시작된다; 첫 8바이트는 P 자체에 대한
셀프맵 디스크립터이다. 그런 다음 유저랜드가 base+8 KiB를 통해
페이지 테이블 페이지를 읽고 쓴다.KPF_SLAB 페이지 배치가
새 PTE를 통해 매핑되고 이 태스크의 comm을 스캔한다; pid/tgid, 선형 맵
윈도우 및 cred->uid/euid가 히트를 교차 검증하며, 새로 사용된 적 없는
VA가 물리 페이지를 재검증한다.setresuid(2000)을 수행하고(커널이
새 cred를 할당함), 셀프맵을 통해 task->cred를 읽고,
uid/suid/euid/fsuid만 0으로 만들고, getuid()==0을 확인하고, 셀프맵을
해제한 뒤 /system/bin/sh를 execve한다. 셸은 uid 0으로 실행된다.make exp
make push
# on the device, as root:
sh /data/local/tmp/e2e/launch_exp.sh /mnt/cifs 10 1 r1 4000 400 2400 7 0 0 cred
# then read /data/local/tmp/e2e/e2e_status.txt and
# /data/local/tmp/e2e/root_shell.txt
cred 단계는 마지막 인자(cred)를 통해 선택적으로 활성화됩니다. scout는 읽기 전용 검증 후에 중지됩니다. EXP는 PTE를 프로세스 내부에 유지하며 정리는 재부팅입니다.
요구 사항: Android NDK r29 (NDK=로 재정의), GNU make, adb.
make # POC (arm64, API 24)
make exp # EXP
make build-all # both
make check # push and run the read-only pre-check (poc --check)
make push # push binaries and helper scripts
make run # push and run the POC against $(MNT) (default /mnt/cifs; destructive)
make host # static POC build for qemu-aarch64
make emu # host build and run
make logs # dump logcat/dmesg into logs/
make help
.
├── Makefile
├── README.md / README.zh-CN.md
├── LICENSE
├── src/
│ ├── poc.c # POC: idmap preload + chown trigger (no escalation)
│ ├── exp.c # EXP: end-to-end chain to the cred overwrite
│ ├── vuln_precheck.h # profile/environment gate
│ ├── poc_smb2_acl.py # malicious SMB2/3 ACL server (DACL layouts)
│ ├── acl_key_preload.c # standalone cifs.idmap key preload
│ └── cifs_keyring_dump.c # keyring inspection helper
├── scripts/
│ ├── mount_cifs.sh # standard-client mount of the POC share
│ ├── unmount_cifs.sh
│ ├── launch_exp.sh # device-side EXP launcher
│ ├── trace_acl02.sh # kprobe on id_mode_to_cifs_acl
│ └── derive_offsets.sh # BTF offsets for src/exp.c
└── docs/analysis.md # analysis, primitives, validation records
vmlinux/BTF와 오프셋, 심볼 주소 및 vmemmap 공식의 재도출이 필요합니다.cifsacl 마운트, 미리 로드된 idmap 키, 그리고 관대한 윈도우가 필요합니다. 이는 원시적(primitive) 데모이며, 신뢰할 수 없는 앱에서 시작하는 익스플로잇 경로가 아닙니다.cifsacl/modefromsid를 활성화하지 마십시오.승인된 보안 연구 및 교육 목적으로만 제공됩니다. 저자는 오용에 대해 책임지지 않습니다. 소유한 기기 또는 명시적 테스트 허가를 받은 기기에서만 테스트하십시오.
MIT — LICENSE 참조.