Skip to content
KitploitKITPLOIT
도구블로그
제출
도구블로그
제출

해킹, 침투 테스트 및 사이버 보안 도구를 당신의 보안 무기고에!

Kitploit은 해킹, 사이버 보안 및 침투 테스트 도구 디렉토리입니다. 최신 프로젝트 업데이트를 발견하여 취약점을 찾고, 시스템을 분석하고, 테스트를 자동화하고, 보안을 강화하세요.

··피드·문의·개인정보·© 2026 Kitploit

도구 디렉토리

카테고리

모든 카테고리 보기
Loading categories
cve-2023-20768 — MediaTek ION 할당자 타입 혼동에 대한 Android 커널 CVE 분석 및 PoC로, 근본 원인 diffing, 비특권 트리거, 익스플로잇 가능성 평가를 다룹니다. | Kitploit
도구/GitHubGitHub/murf-xd/cve-2023-20768
Android SecurityVulnerability AnalysisExploitationReverse EngineeringMobile SecurityBinary AnalysisBinary Exploitation
GitHubmurf-xd/cve-2023-20768

cve-2023-20768

MediaTek ION 할당자 타입 혼동에 대한 Android 커널 CVE 분석 및 PoC로, 근본 원인 diffing, 비특권 트리거, 익스플로잇 가능성 평가를 다룹니다.

저장소 보기
31개월 전아직 검토되지 않음

인기

모두 보기 →

커뮤니티에서 가장 많이 사용되는 도구를 찾아보세요.

모든 도구 탐색

도구 컬렉션을 둘러보세요

모든 도구 보기 →
공유

삼성 갤럭시 M32에서의 CVE-2023-20768 — 도달 가능성 연구

요약. CVE-2023-20768은 MediaTek ION 할당기의 타입 혼동(CWE-843) 취약점입니다. 저는 2022년 7월 펌웨어를 실행하는 삼성 SM-M325F(갤럭시 M32, Helio G80)에서 이 취약점이 실제로 악용 가능한지 확인했습니다. 취약한 코드는 존재하며, 권한이 없는 프로세스가 트리거하면 이 기기에서 실행된다는 것을 입증했습니다. 다만 무기화에는 실패했습니다. ioctl 경로는 입력 검증에 의해 제한되고, 실제 out-of-bounds 읽기가 있는 경로는 dma_buf_ops 포인터에 대한 두 번째 검사가 제가 위조할 수 있는 모든 것을 거부하기 때문에 진짜 ION 버퍼만 처리합니다. 이 문서는 그 결론에 도달한 과정을 다루며, 그중 하나는 결국 틀린 것으로 판명된 중간 결론도 포함합니다.

이 CVE는 공개되었고 패치되었습니다. 모든 테스트는 Magisk로 루팅한 제 기기에서 수행되었습니다.

이 기기를 선택한 이유

이 버그는 upstream Linux나 삼성이 작성한 코드가 아니라 MediaTek의 ION 코드에 있습니다. MediaTek은 자사 칩 기반으로 제품을 만드는 모든 벤더에게 제공하는 BSP에 Android ION 할당기의 자체 포크를 포함합니다. M32는 Helio G80을 사용하므로 해당 코드가 적용됩니다. Exynos SoC를 탑재한 같은 폰은 전혀 영향을 받지 않습니다.

근본 원인

2022년(취약) 및 2023년(패치) vmlinux 이미지를 추출하여 IDA에서 diff했습니다. 두 ION 함수가 변경되었습니다:

함수20222023
ion_drv_file_to_bufferstrstr(name, "dmabuf")is_dma_buf_file()
_ion_ioctlstrcmp(name, "ion")is_dma_buf_file()

is_dma_buf_file는 2022 이미지에는 존재하지 않습니다. 2023 이미지에 나타납니다. 따라서 두 함수 모두 struct file이 dma_buf인지 이름을 보고 판단하고 있었으며, 패치는 이를 실제 타입 검사로 대체했습니다. 여기서 객체를 혼동하면 커널이 dma_buf가 아닌 객체를 dma_buf인 것처럼 읽습니다.

사용자 공간에서 코드에 도달하기

두 함수 중 권한이 없는 프로세스가 도달할 수 있는 것은 _ion_ioctl입니다:

root@kitploit:~
open("/dev/ion")
  -> ion_ioctl                      (.unlocked_ioctl)
  -> ION_IOC_CUSTOM  (0xC0104906)
  -> ion_custom_ioctl
  -> _ion_ioctl
  -> case 0: ION_SYS_CACHE_SYNC
  -> find_vma(user_VA)              (call site at _ion_ioctl+0x9f0)
  -> strcmp(vma->vm_file...name, "ion")

요청은 120바이트 ion_sys_data를 가리키는 ion_custom_data { u32 cmd = 0; u64 arg; }입니다:

오프셋필드

spoof.c가 이를 구성합니다.

디스패치가 일어남을 입증하기

단순히 추적(trace)할 수는 없었습니다. 이 기기는 삼성의 SELinux 정책과 커널 하드닝을 통해 kprobe_events, set_ftrace_filter, function_graph를 차단하며, ION의 printk는 디버그 게이트가 적용되어 dmesg는 조용히 유지됩니다.

그래서 대신 리턴 코드를 오라클로 사용했습니다. 네 가지 요청을 보내고, 돌아오는 결과의 패턴을 보면 실행이 어디로 흘렀는지 알 수 있습니다:

C가 성공을 반환한다는 것은 switch가 실제로 sys_cmd를 기준으로 디스패치한다는 뜻입니다. A와 B의 결과가 다르다는 것은 VA가 처리되고 있다는 뜻이며, 이는 실행이 find_vma 내부로 들어갔다는 것을 의미합니다. 이것이 루트 없이 도달하는 취약 경로입니다.

어디서 막혔는가

검사에 도달하는 것과 검사를 우회하는 것은 다릅니다. strcmp가 실제로 무엇을 통과시키는지 테스트했습니다:

  • ION_IOC_SHARE로 매핑한 후 mmap한 실제 ION 버퍼는 통과하며 0을 반환합니다.
  • 이름이 memfd:ion인 memfd는 실패하며 -EFAULT를 반환합니다.
  • 말 그대로 이름이 ion인 일반 파일도 실패하며 -EFAULT를 반환합니다.

따라서 vm_file+0x60에서 비교되는 필드는 파일 이름이 아닙니다. 이 필드는 dma_buf 내부의 것으로, 거의 확실히 ION이 "ion"으로 설정하는 dma_buf->exp_name입니다. 이 검사는 설계상 불완전하지만, 사용자 공간에서 제가 만들 수 있는 어떤 것으로도 이 필드를 설정할 수 없습니다.

그런 다음 이 경로를 퍼징했습니다: sync_type 07, 크기 {0, 1, 0x1000, 0x100000, 0xffffffff}, VA {실제 ion 버퍼, memfd, 0}로 총 120개 사례와 use-after-free를 확인하기 위한 해제된 핸들 프로브까지 테스트했습니다. 크래시는 없었고 기기는 계속 정상 작동했습니다. 과도한 크기는 find_vma 이전에 빠져나가 0을 반환합니다. sync 타입 35는 m4u 경로에 도달하여 -EPERM을 반환합니다. 5보다 큰 값은 -EINVAL을 반환합니다. 해제된 핸들은 -EINVAL을 반환하므로 ION이 이를 검증하며, 여기에는 UAF가 없습니다.

다른 함수와 잘못된 판단

실제 out-of-bounds 읽기는 ion_drv_file_to_buffer에 있습니다. 이 함수는 ldr [private_data+0x28]을 수행하여 dma_buf가 아닌 객체의 private_data를 dma_buf인 것처럼 읽습니다. ops == &ion_dma_buf_ops 비교(테이블은 0xFFFFFF800A097F18에 있음)가 존재하지만, 이 비교는 그 읽기 이후에 발생하므로 읽기를 막지 못합니다. 이후 단계에서 __do_dump_share_fd는 반환된 버퍼의 +0x28, +0x48, +0x50, +0xb8, +0xe4 필드를 읽어 출력하며, ldr x8, [buf+0x28]; ldr [x8+0x30]은 혼동된 객체에 대한 와일드 포인터 역참조입니다.

트리거는 ion_dump_all_share_fds이며, 이 함수는 iterate_fd를 사용하여 모든 ION 클라이언트 프로세스의 파일 디스크립터를 순회합니다. 제 첫 번째 결론은 이것이 ION debugfs 노드를 읽을 때만 실행되고, 이 커널에는 CONFIG_DEBUG_FS가 설정되어 있지 않다는 것이었습니다. 세 가지 방법으로 확인했습니다: /proc/config.gz, /proc/filesystems에 debugfs가 없는 것, mount -t debugfs가 ENODEV를 반환하는 것. 그래서 이 경로를 구조적으로 도달 불가능하다고 판단했습니다.

그것은 틀렸습니다. OOM 킬러의 메모리 덤프인 dump_header에는 0xffffff8008204b9c 위치에 직접적인 bl ion_mm_heap_memory_detail 호출이 포함되어 있으며, dump_header는 out_of_memory와 oom_kill_process에서 호출됩니다. debugfs는 필요 없습니다.

memcg_oom.c가 이를 확인해 줍니다. 이 코드는 /dev/memcg 아래에 cgroup을 만들고 memory.limit_in_bytes와 memory.memsw.limit_in_bytes를 모두 8MB로 제한하며(첫 번째만 제한하면 자식이 zram 스왑으로 빠져나갈 수 있음), 죽을 때까지 메모리를 할당하는 자식 프로세스를 포크합니다. 그러면 dmesg에 다음이 표시됩니다:

root@kitploit:~
dump_header <- oom_kill_process <- out_of_memory <- mem_cgroup_oom_synchronize

그 뒤에는 실제 gralloc dma_buf를 해석한 전체 ion_mm_heap_memory_detail 및 __do_dump_share_fd 출력이 이어집니다. 따라서 권한이 없는 프로세스가 유발할 수 있는 상황에서 취약 함수가 실행됩니다. 세 번째 트리거도 있습니다. MediaTek 워치독의 hang_detect_dump_thread에서 오는 ShowStatus입니다.

그래도 작동하지 않는 이유

이름이 memfd:dmabuf인 memfd 32개를 보유한 상태에서 동일한 memcg OOM을 트리거했습니다. 제 memfd 중 하나가 ion_drv_file_to_buffer에 전달되었다면 strstr은 통과하고 private_data는 NULL이 되어 커널이 KERN_ERR 레벨로 [ION]ion_drv_file_to_buffer warnning, dmabuf is NULL을 출력했을 것입니다. 그 줄은 결코 나타나지 않았습니다. 그래픽 및 gralloc 클라이언트만 덤프되었습니다. 일반 /dev/ion 클라이언트의 fd 테이블이 여기서 iterate_fd가 순회하는 대상이 아니거나, memfd가 출력 이전에 조용히 실패하는 것입니다.

어느 쪽이든 OOM 덤프는 시스템의 정상적인 dma_buf만 처리하며, 이들은 문제없이 통과합니다. 또한 memfd는 이름에 "dmabuf"가 포함되도록 제가 충분히 제어할 수 있는 유일한 fd 유형이며, memfd는 폴트를 일으킬 수 없습니다.

결론

취약점은 존재합니다. 취약한 코드는 이 빌드에서 도달 가능하고 실제로 실행되며, 루트 없이 트리거할 수 있습니다. 그러나 이 환경에서는 사용자 공간에서 무기화할 수 없습니다. ioctl 경로는 핸들 검증, 크기 검사 및 access_ok에 의해 제한됩니다. 덤프 경로는 실제 ION 버퍼만 볼 수 있으며, 위조는 ops == &ion_dma_buf_ops에 의해 차단됩니다. 더 나아가려면 exp_name이 "ion"인 제어 가능한 비-ION 객체, ION 버퍼 UAF와 같은 다른 프리미티브, 또는 TOCTOU 경합이 필요합니다.

파일

  • spoof.c — cache-sync ioctl PoC 및 차등 오라클
  • memcg_oom.c — 덤프 경로용 memcg OOM 트리거
  • trigger.c, oom_trigger.c — 초기 트리거 시도
  • boot_images/ — 추출된 커널 이미지(2022년, 2023년) 및 2022년 빌드용 IDA 데이터베이스
도구 다운로드
+0x00sys_cmd = 0
+0x08ion 핸들(먼저 하나를 할당할 것, heap_id_mask = 0x1로 충분함)
+0x10사용자 가상 주소
+0x18하위 절반 = 크기, 상위 절반 = sync_type({0,1,2})
사례요청결과
Asys_cmd=0, 스푸핑된 VA-EFAULT
Bsys_cmd=0, VA = 00
Csys_cmd=40
Dsys_cmd=99-EFAULT