Skip to content
KitploitKITPLOIT
ToolsBlog
Submit
ToolsBlog
Submit

Hacking, PenTest, and Cybersecurity Tools for Your Security Arsenal!

Kitploit is a directory of hacking, cybersecurity, and pentesting tools. Discover the latest project updates to find vulnerabilities, analyze systems, automate testing, and strengthen your security.

··Feeds·Contact·Privacy·© 2026 Kitploit

Tool Directory

Categories

View all categories
Loading categories
cve-2023-20768 — Android kernel CVE analysis and PoC for a MediaTek ION allocator type confusion, covering root-cause diffing, unprivileged trigger, and exploitability assessment. | Kitploit
Tools/GitHubGitHub/murf-xd/cve-2023-20768
Android SecurityVulnerability AnalysisExploitationReverse EngineeringMobile SecurityBinary AnalysisBinary Exploitation
GitHubmurf-xd/cve-2023-20768

cve-2023-20768

Android kernel CVE analysis and PoC for a MediaTek ION allocator type confusion, covering root-cause diffing, unprivileged trigger, and exploitability assessment.

View Repository
31 month agoNot yet reviewed

Most Popular

View all →

Discover the most used tools by our community.

Explore all tools

Browse our collection of tools

View all tools →
Share

CVE-2023-20768 on the Samsung Galaxy M32 — a reachability study

Summary. CVE-2023-20768 is a type confusion (CWE-843) in MediaTek's ION allocator. I checked whether it is actually exploitable on a Samsung SM-M325F (Galaxy M32, Helio G80) running the July 2022 firmware. The vulnerable code is present, and I proved that it runs on this device when triggered by an unprivileged process. I was not able to weaponize it. The ioctl path is bounded by input validation, and the path that contains the real out-of-bounds read only ever processes genuine ION buffers, because a second check on the dma_buf_ops pointer rejects anything I could forge. This writeup covers how I got to that conclusion, including one intermediate conclusion that turned out to be wrong.

The CVE is public and patched. All testing was done on my own device, rooted with Magisk.

Why this device

The bug is in MediaTek's ION code, not in upstream Linux or in anything Samsung wrote. MediaTek ships its own fork of the Android ION allocator in the BSP that goes to every vendor building on their chips. The M32 uses a Helio G80, so it gets that code. The same phone with an Exynos SoC would not be affected at all.

Root cause

I pulled the 2022 (vulnerable) and 2023 (patched) vmlinux images and diffed them in IDA. Two ION functions changed:

Function20222023
ion_drv_file_to_bufferstrstr(name, "dmabuf")is_dma_buf_file()
_ion_ioctlstrcmp(name, "ion")is_dma_buf_file()

is_dma_buf_file does not exist in the 2022 image. It appears in the 2023 one. So both functions were deciding whether a struct file was a dma_buf by looking at a name, and the fix replaced that with a real type check. Confusing an object here means the kernel reads a non-dma_buf as if it were one.

Reaching the code from userspace

Of the two, _ion_ioctl is the one an unprivileged process can reach:

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")

The request is an ion_custom_data { u32 cmd = 0; u64 arg; } pointing at a 120 byte ion_sys_data:

spoof.c builds this.

Proving it dispatches

I could not just trace it. The device blocks kprobe_events, set_ftrace_filter and function_graph through Samsung's SELinux policy and kernel hardening, and ION's printks are debug-gated so dmesg stays quiet.

So I used return codes as an oracle instead. Four requests, and the pattern of what comes back tells you where execution went:

C returning success means the switch really is dispatching on sys_cmd. A and B differing means the VA is being processed, which puts execution inside find_vma. That is the vulnerable path, reached without root.

Where it stopped

Reaching the check is not the same as beating it. I tested what the strcmp actually accepts:

  • A real ION buffer mapped via ION_IOC_SHARE then mmap passes, returns 0.
  • A memfd named memfd:ion fails, -EFAULT.
  • An ordinary file literally named ion also fails, -EFAULT.

So the field being compared at vm_file+0x60 is not the filename. It is internal to dma_buf, almost certainly dma_buf->exp_name, which ION sets to "ion". The check is unsound by design, but nothing I can create from userspace can set that field.

I then fuzzed the path: sync_type 0 through 7, sizes {0, 1, 0x1000, 0x100000, 0xffffffff}, VAs {real ion buffer, memfd, 0}, 120 cases, plus a freed-handle probe for a use-after-free. No crash, device stayed up. Oversized sizes bail out before find_vma and return 0. sync types 3 to 5 hit the m4u path and return -EPERM. Anything above 5 returns -EINVAL. A freed handle returns -EINVAL, so ION validates it and there is no UAF there.

The other function, and a wrong turn

The actual out-of-bounds read is in ion_drv_file_to_buffer. It does ldr [private_data+0x28], reading a non-dma_buf's private_data as if it were a dma_buf. There is an ops == &ion_dma_buf_ops comparison (the table is at 0xFFFFFF800A097F18), but it happens after that read, so it does not prevent it. Downstream, __do_dump_share_fd reads fields at +0x28, +0x48, +0x50, +0xb8, +0xe4 off the returned buffer and prints them, and ldr x8, [buf+0x28]; ldr [x8+0x30] is a wild pointer deref for a confused object.

The trigger is ion_dump_all_share_fds, which uses iterate_fd to walk every ION client process's file descriptors. My first conclusion was that this only runs when you read an ION debugfs node, and this kernel has CONFIG_DEBUG_FS unset. I verified that three ways: /proc/config.gz, debugfs missing from /proc/filesystems, and mount -t debugfs returning ENODEV. I wrote the path off as structurally unreachable.

That was wrong. dump_header, the OOM killer's memory dump, contains a direct bl ion_mm_heap_memory_detail at 0xffffff8008204b9c, and dump_header is called from out_of_memory and oom_kill_process. No debugfs needed.

memcg_oom.c confirms it. It creates a cgroup under /dev/memcg, caps both memory.limit_in_bytes and memory.memsw.limit_in_bytes at 8MB (capping only the first lets the child escape into zram swap), and forks a child that allocates until it dies. dmesg then shows:

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

followed by the full ion_mm_heap_memory_detail and __do_dump_share_fd output resolving real gralloc dma_bufs. So the vulnerable function executes, during something any unprivileged process can cause. There is a third trigger too, ShowStatus from hang_detect_dump_thread in the MediaTek watchdog.

Why it still does not work

I held 32 memfds named memfd:dmabuf and triggered the same memcg OOM. If one of mine had been fed to ion_drv_file_to_buffer, the strstr would pass, private_data would be NULL, and the kernel would print [ION]ion_drv_file_to_buffer warnning, dmabuf is NULL at KERN_ERR. That line never appeared. Only graphics and gralloc clients got dumped. Either a plain /dev/ion client's fd table is not what iterate_fd walks here, or memfd fails silently before the print.

Either way the OOM dump only ever handles the system's legitimate dma_bufs, which pass cleanly. memfd is also the only fd type whose name I can control enough to contain "dmabuf", and it cannot fault.

Verdict

The vulnerability is present. The vulnerable code is reachable and does execute on this build, triggerable without root. It is not weaponizable from userspace here. The ioctl path is bounded by handle validation, a size check and access_ok. The dump path only sees real ION buffers, and forgery is blocked by ops == &ion_dma_buf_ops. Getting further would need a controllable non-ION object whose exp_name is "ion", or a different primitive such as an ION buffer UAF, or a TOCTOU race.

Files

  • spoof.c — cache-sync ioctl PoC and the differential oracle
  • memcg_oom.c — memcg OOM trigger for the dump path
  • trigger.c, oom_trigger.c — earlier trigger attempts
  • boot_images/ — extracted kernel images (2022 and 2023) and the IDA database for the 2022 build
Download Tool
Offset
Field
+0x00sys_cmd = 0
+0x08ion handle (allocate one first, heap_id_mask = 0x1 works)
+0x10user virtual address
+0x18low half = size, high half = sync_type in {0,1,2}
CaseRequestResult
Asys_cmd=0, spoofed VA-EFAULT
Bsys_cmd=0, VA = 00
Csys_cmd=40
Dsys_cmd=99-EFAULT