Cross-Cache Attack

이동화·2025년 7월 28일
post-thumbnail

Heap memory Allocation

사용자 프로세스 레벨에서는 malloc, free의 동작 방식만 알면 heap 기반 exploit (house of ... 시리즈)을 설계할 수 있었지만, 커널 영역에서는 slab/kmem_cahce 기반 heap 관리를 하기 때문에 다른 공격 기법이 필요하다. 거기다가 커널의 주소 공간도 유저 영역과도 다른데, Direct Mapped Memory라는 추가적인 section이 존재한다.

사용자 프로세스는 넓은 메모리 영역을 mmap , brk 명령어를 통해 확보하고 그 영역을 큰 상태로 관리하고 필요할 때 마다 작은 chunk로 쪼개 malloc/free 요청이 들어오면 인자로 들어온 크기만큼 list 자료구조를 활용하여 할당 및 해제한다. 이렇게 size-based allocation을 하면 공격자 입장에서 크기가 같은 영역이 list 내에서 비슷한 위치에 연속되어 배치될 가능성이 높아지기에, OOB overflow 시 target object가 어느 zone에 배치될 지 예측할 수 있게 된다.

kernel의 경우에는 크기가 작은 객체, 중간 크기 객체, 대형 페이지 단위 객체를 서로 다른 cache를 통해 할당한다. kmalloc-64, kmalloc-128이 크기가 작거나 중간 크기인 cache를 slab alloactor을 통해 할당하며, 페이지 단위는 buddy allocator가 아예 페이지 사이즈로 할당해준다.

즉 하나의 SLAB cache는 페이지 단위로 묶여 있고, CPU 별로 (perCPU) freelist, page-level freelist, node-level freelist로 분류되어 관리된다. 각 slab page는 할당된(A) 상태와 비어있는 (F) 상태 객체 리스트로 관리를 하며, 할당 요청이 들어오면 최적화를 위해 freelist->page freelist->partial freelist->buddy allocator 순으로 접근하여 할당을 시도한다.

Temporal Cross-Cache Attack

slab 할당자의 할당/해제 흐름을 조작하여 서로 다른 종류의 cache가 같은 물리 페이지를 공유하도록 유도한 뒤 하나의 object를 overflow하여 다른 object를 덮어 쓰는 기법이다.

skb, msg_oob같은 kmalloc-256 크기의 slab cache에 대해 할당과 해제를 반복해서 CPU-partial list를 가득 채우면, 해당 cache의 fastpath를 무력화시킬 수 있다. msg_oob 에서의 OOB (Out of band) 는 urgent data 전송을 위해 별도의 버퍼 취급을 받는 메시지로, 일반 데이터 스트림과는 분리된 방식으로 전송되어 스트림 중단 신호, 우선도가 높은 메시지 전송 등에 활용된다. skb (=sk_buff)는 커널 내부에서 패킷이나 UNIX domain socket data를 담는 버퍼 구조체이다. (urgent/normal 둘 다) 목표는, skb 구조체의 destructor 함수 포인터를 덮어 씌워 커널 실행 흐름을 장악하는 것이다. 아무래도 페이지를 해제한 이후에도 해당 페이지를 포인터를 참조하는 경우인 UAF 취약점이 존재해야 공격이 의미 있어 보인다.

CPU partial list가 넘치면 해제되는 객체는 pernode의 partial로 넘어가고, 또 pernode partial이 가득차면 페이지 단위로 buddy allocator에 반환된다. 이 과정을 반복하면 kmalloc-256의 모든 페이지가 buddy allocator로 반환되게 된다.

이제 다시 할당을 하게 되면, buddy allocator에서 페이지를 할당하는 slowpath가 강제된다. 해당 페이지는 완전히 비어 있는 물리 페이지로, 해당 페이지를 msg_oob object에 할당하고 다시 같은 페이지에 skb object를 할당하면 한 page 안에 두개의 다른 종류의 객체가 공존하게 되어, skb 객체를 덮어씌울 수 있게 된다. 이를 통해 core_pattern을 변경하거나, 권한 상승, AAW 등 exploit을 할 수 있게 된다.

fastpath를 배제함으로써 slab cache를 페이지 단위로 컨트롤하게 하여, 최소한의 객체 할당을 통해 원하는 페이지에 원하는 객체 배치를 달성할 수 있게 된다. cross-cache 단계에서 free된 page를 kmalloc-cg-256 cache의 msg_msg 객체로 재할당 하기 위해, msg_msg를 spray 대상으로 활용한다. UAF된 sk_buff가 있던 물리 페이지를 kmalloc-cg-256 cache로 그대로 넘어오게 하려면 동일한 chunk 크기를 가지고 있어야 하기도 하고, msg_msg 구조체 안의 m_text 필드라는 유저가 채울 수 있는 대형 buffer가 존재해서 버퍼 영역 뒤에 가짜 sk_buff 헤더를 넣어 UAF 이후 다시 할당된 메모리 자리의 필드 값을 마음껏 조작할 수 있다.

linux는 kmalloc(128) 같은 자주 사용되는 명령이 들어오면 kmalloc-128 전용 slab cache에서 미리 할당되어 있는 object를 반환한다. sk_buff를 여러 개 할당하고 해제하는 과정을 반복하여, cpu와 node의 partial list를 가득 채우게 되면, inuse=0안 slab page를 골라 discard_slab()을 통해 물리 페이지 단위로 buddy allocator에게 반납된다.

이 반환된 비어 있는 페이지에 대해서 kmalloc(64)와 같은 요청이 오면 바로 그 페이지를 꺼내와 slab page를 재활용하게 된다. 이후 OOB 취약점을 통해 free 되었던 kmalloc-128 slab cache 영역을 덮어 써서 권한 상승을 유발하는 것이다.

이는 linux의 SLUB allocator가 slab 하나를 CPU per partial list와 node per partial list로 나누어 관리하여 발생하는 취약점이다. CPU per는 각 CPU가 보유하는 slab page의 최대 개수가 있고 Min_partial은 node 전체가 보유하는 슬랩 페이지 최소 개수를 의미하는데, slab page가 CPU partial의 한계를 넘으면 Node partial로 넘어가게 된다. Node partial의 Min_partial 한도를 넘고 slab page의 inuse 값이 0안 패이지들을 discard_slab()를 호출하여 buddy allocator로 반환한다.

실제 동작 흐름은 sk_buff (128B)와 cred_jar(128B)를 같은 페이지에 연속으로 할당하고, sk_buff를 반복해서 free-alloc을 반복하거나 slab 내부의 free 개수를 의도적으로 늘려 CPU_partial의 한계점을 넘계 하면 해당 slab는 CPU partial에서 node partial list로 넘어가게 된다. 추가로 sk_buff를 모두 해제해서 slab의 inuse를 0으로 만들고, node_partial의 개수를 min_partial 한도를 넘게 하면 slab page전체가 discard_slab() 함수로 인해 buddy allocator에 페이지 단위로 반환이 되게 된다.

직후 kmalloc-64같은 다른 크기의 캐시에 대한 할당이 들어오면 buddy allocator는 방금 반납된 페이지를 그대로 재활용하여 새로운 slab page를 생성하는데, 그 안의 chunk로 cred_jar 메모리를 덮어쓸 수 있다. 최종적으로 공격자는 kmalloc-64 객체에 OOB를 시전하면 프로세스 권한 정보를 덮어 써서 권한 상승을 발생시킬 수 있다.

CVE-2024-36972

https://github.com/google/security-research/blob/master/pocs/linux/kernelctf/CVE-2024-36972_lts_cos/docs/exploit.md

해당 CVE는 IPC를 위해 local file system path를 주소로 사용하는 소켓은 AF_UNIX socket에서 발생하는 취약점이다. AF_UNIX는 kernel 내부에만 존재하는 소켓으로, 같은 host 내의 process 끼리 latency가 짧거나 대용량인 통신이 필요할 때 사용한다.

OOB data는 unix_sk(sk)->oob_skb라는 별도 포인터에 보관되는데, unix_gc() 함수가 해당 포인터를 별도의 lock 없이 해제하기 때문에 race condition이 발생할 수 있다. 이로 인해 포인터가 해제되었는데도 free된 메모리를 여전히 참조하는 UAF 취약점이 발생한다.

UAF 취약점이 발생하면, UAF된 sk_buff에 남은 포인터를 회수하고, 해당 물리 메모리 페이지를 공격자가 원하는 다른 객체로 채워넣는 cross cache attack이 가능하다. 하나의 페이지를 완전히 비우기 위해 4KiB를 kmalloc-256 cache로 채우기 때문에, cross cache 공격은 16개의 object를 free하여 buddy allocator로 페이지를 반환하고, 16개의 msg_msg를 sray하여 반환한 메모리를 재사용할 수 있도록 유도해야 한다.sk_buff를 대량으로 할당하여 fastpath를 고갈시키고, 비어 있는 페이지를 만든 후 다른 cache에 할당하도록 spray하면 IAF된 sk_buff 메모리 자리에 msg_msg가 올라와 덮어쓸 수 있게 되는 것이다. 즉 새로 채운 객체의 내부 필드를 조작할 수 있어, kernel code 흐름을 탈취할 수 있다.

Spatial Cross Cache Attack

동일한 사이즈(예: kmalloc‑256)로 할당된 두 페이지가 나란히 붙어 있고, 그 사이에 cred_jar 같은 민감 객체가 중간에 끼도록 초기 상태를 형성한다. 이후 앞의 kmalloc-256의 마지막 슬롯을 해제하고, 취약한 OOB 취약점이 있는 버퍼를 그 해제된 슬롯에 재할당해서 페이지 A 끝에 자리한 취약 버퍼에서 64B OOB 쓰기를 발생시키면, 바이트들이 경계를 넘어 페이지 B 첫 슬롯에 있는 cred_jar를 덮어쓴다. 결과적으로 cred_jar의 권한 정보 등을 마음대로 조작할 수 있게 된다.

단, 두 가지 필수 조건을 만족하고 있어야 한다.
1. 두 페이지가 물리적으로 연속(consecutive)이어야 하며,
2. 취약한 객체가 첫 번째 페이지의 “마지막 슬롯”에 배치돼 있어야 한다.

profile
notion이 나은듯

0개의 댓글