← Back to Feed CACHED · 2026-10-17 04:59:54 · CACHE_KEY CVE-2023-20938
CVE-2023-20938 · CWE-416 · Disclosed 2023-02-28

Android Binder Use-After-Free in binder_transaction_buffer_release Leading to Local Privilege Escalation

ASSESSED — NOISGATE
Vendor
—
—
—
Reassessed
—
—
—
Verdict: —
Do you agree?
01 · The Real Story

A janitor cleaning up a room that was never used accidentally throws away the master key that's still in someone's pocket

CVE-2023-20938 is a use-after-free in the Linux kernel's binder IPC driver, specifically in binder_transaction_buffer_release() within binder.c. When a binder transaction is submitted with an unaligned offsets_size, the kernel skips the object-processing loop but then erroneously calls the cleanup function as if objects *were* processed — decrementing reference counts on binder_node structures that haven't been touched. This drops local_strong_refs to zero, freeing the node while dangling pointers remain. An attacker can reclaim that freed memory via sendmsg heap spray, build arbitrary read/write primitives through cross-cache techniques targeting eventpoll_epi structures, and ultimately overwrite their own process credentials to UID 0 while dropping SELinux to permissive. Affected versions are Android GKI kernels 5.4 and 5.10 (pre-February 2023 security patch level), with the mainline Linux kernel also carrying the binder code in distros like Debian (fixed in 5.10.158-1, 5.17.6-1, 6.1.176-1+) and Ubuntu.

Google's vendor severity of HIGH 7.8 is appropriate for Android devices, where binder is the foundational IPC mechanism and every installed app has direct access to /dev/binder. The CVSS vector AV:L/AC:L/PR:L/UI:N accurately reflects that any low-privilege local process can trigger this without user interaction. Critically, the initial February 2023 patch was *incomplete* — the Android Red Team demonstrated a full root exploit chain on devices that had applied it — requiring a second fix under CVE-2023-21255 in July 2023. For enterprise Linux server fleets, however, the binder module is almost never loaded (it exists in the kernel source tree but is not part of standard server configs), making this effectively a non-issue on that platform. The vendor score is honest for Android but slightly overstates the risk for mixed enterprise environments.

"Proven kernel root from any app, but local-only and 3 years patched — HIGH holds."
02 · The Attack Path

5 steps from start to impact.

STEP 01

Attacker obtains local code execution on target device

On Android, the attacker delivers a malicious app — either sideloaded or theoretically via the Play Store if it passes review. The app runs as a normal unprivileged Android process with standard app sandbox permissions. On a Linux host, the attacker needs an existing local shell account. No elevated privileges are required at this stage.
Conditions required:
  • Local code execution on a device running an affected kernel (GKI 5.4/5.10 pre-July 2023 patch, or unpatched mainline Linux with binder module loaded)
Where this breaks in practice:
  • On Android, Google Play Protect and app review reduce (but don't eliminate) malicious app delivery
  • On Linux servers, binder kernel module is typically not loaded — lsmod | grep binder returns nothing on >95% of enterprise Linux hosts
  • MDM policies may block sideloading on managed Android devices
Detection/coverage: Mobile Threat Defense (MTD) agents can flag apps attempting binder ioctls with anomalous parameters; Linux audit rules on /dev/binder access would catch this on servers where binder is loaded.
STEP 02

Trigger UAF via malformed binder transaction

The attacker opens /dev/binder (or /dev/hwbinder) and submits a BC_TRANSACTION with a deliberately unaligned offsets_size. The kernel's binder_transaction() skips the object translation loop but still calls binder_transaction_buffer_release() with failed_at == 0, causing it to iterate over and decrement refcounts for objects that were never processed. A binder_node with local_strong_refs == 1 is freed while pointers to it persist in the transaction's binder_buffer.
Conditions required:
  • Access to /dev/binder or /dev/hwbinder (available to all Android apps; requires binder module on Linux)
  • Kernel version in affected range without the July 2023 complete fix
Where this breaks in practice:
  • The February 2023 partial fix changed some code paths, but the Android Red Team bypassed it — only the July 2023 patch (CVE-2023-21255) fully closes the bug
  • SELinux on Android restricts which binder interfaces unprivileged apps can use, but /dev/binder itself is accessible
Detection/coverage: Kernel KASAN (if enabled) will flag the UAF immediately in dmesg; production kernels rarely have KASAN enabled.
STEP 03

Reclaim freed memory via cross-cache heap spray

The attacker uses sendmsg() to spray controlled data into the kmalloc-128 slab cache where the freed binder_node lived. On kernel 5.10+, the SLAB_ACCOUNT flag on eventpoll_epi forces a cross-cache technique: the exploit allocates 36×32 binder_node objects, frees them simultaneously to release the backing page, then forces the page allocator to hand that page to the eventpoll_epi cache. This gives the attacker a fake binder_node overlapping with an epitem structure.
Conditions required:
  • Precise heap layout control — the PoC achieves this reliably on Cuttlefish/Goldfish AVD x86_64 with kernel 5.10.107
  • Access to epoll_create/epoll_ctl syscalls (universally available)
Where this breaks in practice:
  • Heap layout randomization (SLAB randomization, CONFIG_SLAB_FREELIST_RANDOM) can reduce reliability but the demonstrated exploit handles this
  • Different device SoCs and OEM kernel configs may require tuning the spray parameters
STEP 04

Build leak and arbitrary read/write primitives

By reading the ptr and cookie fields of the corrupted binder_node during subsequent transaction processing, the attacker leaks two 8-byte kernel heap values. Using the unlink primitive on struct file and struct epitem structures, the exploit modifies kernel pointers to enable arbitrary 4-byte reads via the FIGETBSZ ioctl, then chains this into a full arbitrary write primitive.
Conditions required:
  • Successful heap reclamation from step 3
  • Knowledge of kernel structure offsets (publicly documented for GKI 5.4/5.10)
Where this breaks in practice:
  • Kernel Address Space Layout Randomization (KASLR) adds entropy but the leak primitive in step 3 defeats it
  • CONFIG_CFI_CLANG (Control Flow Integrity) can make some pointer overwrites crash instead of succeeding
Detection/coverage: Runtime kernel integrity monitors (e.g., Samsung RKP, Google's hypervisor-based protection on Pixel) may detect credential structure tampering.
STEP 05

Overwrite credentials and disable SELinux for root shell

The attacker locates their own struct cred in kernel memory using the arbitrary read primitive, then overwrites the uid, gid, euid, egid, suid, sgid fields to 0 (root). SELinux enforcement is disabled by toggling the selinux_enforcing global to 0 (permissive mode). Seccomp filters are cleared. The attacker now has an unrestricted root shell with full kernel-level access.
Conditions required:
  • Successful arbitrary read/write from step 4
Where this breaks in practice:
  • On Pixel devices with Trusty TEE and hypervisor-based protection, credential modifications may be detected and the device rebooted
  • Samsung Knox real-time kernel protection (RKP) traps credential modifications
Detection/coverage: Android Verified Boot (AVB) will detect persistent rootkit modifications on next boot; MTD agents checking SELinux status will flag permissive mode.
03 · Compensating Control

1
HIGH 7.0→IGNORE 0.0
SEVERITY REDUCED
Enforce Android security patch level ≥ 2023-07-05 via MDM policy — The complete fix for both CVE-2023-20938 and the bypass (CVE-2023-21255) shipped in the July 2023 ASB. Configure your MDM (Intune, Workspace ONE, JAMF, etc.) to quarantine or block corporate resource access for devices below this patch level. Per the noisgate mitigation SLA for HIGH, deploy this policy within 30 days. This is the single most effective control — it removes the vulnerability entirely on compliant devices.
2
HIGH 7.0→MEDIUM 5.5
SEVERITY REDUCED
Block sideloading and restrict app sources on managed Android devices — Disable 'Install from Unknown Sources' via MDM policy and restrict app installation to managed Google Play or your enterprise app catalog. This eliminates the primary delivery mechanism for a malicious app exploiting this bug. Does not protect against a compromised Play Store app, but dramatically raises the bar.
3
HIGH 7.0→IGNORE 0.0
SEVERITY REDUCED
Verify binder module is not loaded on Linux servers — Run lsmod | grep binder across your Linux fleet. If no output, the host is not exposed. If binder IS loaded (rare — typically only Android emulators or specific container runtimes), either unload it (rmmod binder_linux) or patch the kernel. Add a compliance check to your configuration management (Ansible, Puppet, Chef) to flag hosts with binder loaded.
4
HIGH 7.0→MEDIUM 5.0
SEVERITY REDUCED
Deploy Mobile Threat Defense (MTD) on corporate Android devices — MTD agents (Lookout, Zimperium, Microsoft Defender for Endpoint on Android) can detect kernel exploit attempts, anomalous binder ioctls, and SELinux status changes. This provides a detection-and-response layer even on devices with lagging OEM patches. Deploy within the 30-day noisgate mitigation SLA for HIGH.
5
HIGH 7.0→IGNORE 0.0
SEVERITY REDUCED
Retire devices that cannot receive July 2023 or later security patches — Android devices past their OEM support window (typically 3-4 years for Samsung/Google, shorter for others) will never receive this fix. Identify these devices in your MDM inventory and plan hardware refresh or restrict them to non-sensitive use cases only.
What doesn't work
  • Network segmentation / firewall rules — This is a local kernel exploit, not network-based. No amount of network controls affects the attack path.
  • WAF or IDS/IPS signatures — There is no network traffic to inspect; the exploit operates entirely through local binder ioctls within the kernel.
  • Android Verified Boot (AVB) — AVB protects boot-time integrity but does not prevent runtime kernel exploitation. It will detect persistent modifications on *next* boot, but the attacker already has root during the current session.
  • Google Play Protect scanning alone — While Play Protect may flag known exploit payloads, a custom-compiled PoC not in Google's signature database will bypass it. Play Protect is a useful layer but not sufficient as a sole control.
04 · Intelligence Metadata

The supporting signals.

In-the-Wild StatusNo confirmed ITW exploitation. Not listed in CISA KEV. No threat intelligence reports link this CVE to active campaigns or APT activity. Google's own Android Red Team demonstrated the full chain internally/at OffensiveCon 2024, but this was research, not adversary use.
Proof-of-ConceptPublic, weaponized PoC available. byt3quester/CVE-2023-20938-poc on GitHub (C source, created 2026-06-22) implements the full chain: UAF trigger → cross-cache heap spray → arbitrary kernel R/W → root + SELinux bypass. Targets Cuttlefish/Goldfish AVD x86_64 kernel 5.10.107. Also jaf0rk/CVE-2023-20938 on GitHub. Google Android Red Team (Zi Fan Tan, Gulshan Singh, Eugene Rodionov) presented the full exploitation at OffensiveCon 2024 with slides and video.
EPSS0.00337 (25.5th percentile) — in the bottom quartile of all CVEs for predicted exploitation probability. Low signal, consistent with no observed ITW activity.
KEV StatusNot listed. No CISA Known Exploited Vulnerabilities catalog entry.
CVSS VectorCVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H (7.8 HIGH) — Local attack vector with low complexity and low privileges. No user interaction. Complete CIA impact but scope unchanged (no sandbox escape beyond the OS itself). The vector accurately reflects the Android threat model.
Affected VersionsAndroid GKI kernels 5.4 and 5.10 before the 2023-02-05 security patch level (partial fix) and before 2023-07-05 (complete fix via CVE-2023-21255). Mainline Linux kernel binder subsystem prior to upstream commit series. Debian fixed in 5.10.158-1 (bullseye), 5.17.6-1 (unstable), 6.1.176-1 (bookworm), 6.12.107-1 (trixie), 7.2.9-1 (forky).
Fixed VersionsAndroid: security patch level 2023-07-05 or later (complete fix). AOSP kernel commits: baa23246e93f, 3d213a626d2d, 9d1efccf5ec3, b83173bf86a9, aaf236971732. Note: the February 2023 patch was incomplete — the July 2023 patch under CVE-2023-21255 is required for full remediation.
Incomplete Patch HistoryThe Feb 2023 ASB back-ported a fix that changed binder object processing, but the Android Red Team found an alternate path to reach the same UAF. CVE-2023-21255 was assigned for the bypass and fully patched in July 2023. Devices on patch levels between 2023-02-05 and 2023-07-04 remain vulnerable through the alternate path.
Scanning/ExposureBinder is a local kernel interface — not network-exposed, so Shodan/Censys/GreyNoise have no visibility. Exposure assessment must be done host-by-host: check lsmod | grep binder on Linux or Android security patch level via MDM. On standard enterprise Linux servers, binder is almost never loaded.
Reporting ResearcherDiscovered via fuzzing on android13-5.10 kernel. Full exploit chain developed and presented by Google Android Red Team: Zi Fan Tan, Gulshan Singh, Eugene Rodionov. Detailed write-up on Android Offensive Security Blog.

Sources.

  1. Android Offensive Security Blog — Full Exploitation Write-up
  2. NVD — CVE-2023-20938 Detail
  3. Android Security Bulletin — February 2023
  4. Debian Security Tracker — CVE-2023-20938
  5. GitHub Advisory — GHSA-2grw-x2mc-x63f
  6. OffensiveCon 2024 Presentation (YouTube)
  7. FIRST EPSS API — CVE-2023-20938
  8. Binder Attack Matrix — Medium (r1971d3)
05 · The Call

Final Verdict
= UNCHANGED to HIGH (7.0/10)

Why this verdict

  • Proven full-chain exploit to kernel root: The Google Android Red Team demonstrated a complete, reliable exploit from unprivileged app to UID 0 + SELinux permissive on GKI 5.4/5.10 devices. A public PoC (byt3quester) implements this chain in C. This is not theoretical — it is weaponized. This anchors the floor at HIGH.
  • Local access requirement is real but surmountable on Android: AV:L means no remote trigger, but on Android the attack surface is any installed app. Sideloading, third-party app stores, or a Play Store review bypass could deliver the payload. On enterprise MDM-managed fleets that block sideloading, friction is higher. On BYOD fleets, it is lower.
  • Enterprise Linux servers are largely immune: The binder kernel module exists in the source tree but is not loaded on standard server deployments. Unless you run Android emulators (Anbox, Cuttlefish) or specific LXC/LXD configurations, /dev/binder does not exist. This dramatically limits the blast radius for server-centric estates.
  • Patch age and gap: The complete fix shipped in July 2023 — over 3 years ago. Any Android device on security patch level 2023-07-05 or later is safe. Any Debian/Ubuntu system with current LTS kernels is patched. The residual vulnerable population shrinks monthly as devices age out.
  • No ITW exploitation and bottom-quartile EPSS: EPSS at 0.00337 (25.5th percentile) with no KEV listing and no threat intel linking this to active campaigns. The exploit exists in research and PoC form but has not been weaponized by threat actors in observed incidents.
  • Role multiplier: On *Android devices* (the canonical deployment for binder), a successful exploit yields full kernel compromise of that device — root shell, SELinux bypass, credential theft, MDM bypass, access to corporate email/VPN tokens. However, each device must be exploited individually; there is no lateral propagation mechanism inherent to this bug. On *managed corporate Android fleets* (~10-30% of enterprise mobile estates), this is HIGH impact. On *Linux servers* (the likely majority of a 10,000-host fleet), the binder module is not loaded on >95% of hosts — impact is effectively zero. The floor remains HIGH due to the Android mobile fleet exposure, but the blended enterprise risk is lower than a pure-Android assessment would suggest.
  • Minor downgrade from 7.8 to 7.0: The vendor's 7.8 is fair for a fresh Android-only assessment. We shave 0.8 points for: (1) 3+ years of patch availability, (2) no ITW exploitation, (3) incomplete initial fix means the Feb 2023 window was still exploitable — but the July 2023 fix is ancient by now, (4) on mixed enterprise fleets, the non-Android majority is unexposed.

Why not higher?

This stays below CRITICAL because the attack vector is strictly local — no remote exploitation path exists, and there is no network propagation mechanism. The binder subsystem, while kernel-mode, is not canonically a fleet-scale infrastructure component like Active Directory, a hypervisor, or a CI/CD pipeline. Each Android device must be individually targeted with a malicious app, and the blast radius is bounded to that single device. Additionally, 3+ years of patch availability and zero ITW exploitation indicate this is not under active adversary pressure.

Why not lower?

Despite the age and low EPSS, a fully weaponized public PoC achieving kernel root from an unprivileged app with low complexity and no user interaction cannot be rated MEDIUM. The binder subsystem is present on every Android device ever shipped, representing billions of devices globally. Enterprise mobile fleets with lagging OEM patch cadences (common on Samsung, LG, and carrier-locked devices) may still have a material vulnerable population. The proven reliability of the exploit chain and the complete security posture bypass (root + SELinux off) mean that any remaining unpatched device is trivially compromisable by a local attacker.

06 · Verification

Crowdsourced verification payload.

Run this script on each target host — Android devices via adb shell (requires USB debugging or MDM remote shell), Linux servers via SSH. No root privileges required. Invoke: bash check_cve_2023_20938.sh. The script checks Android security patch level or Linux kernel version and binder module status, then prints VULNERABLE / PATCHED / UNKNOWN.

noisgate-verify.sh
BASHREAD-ONLYSAFE
#!/usr/bin/env bash
# check_cve_2023_20938.sh — Detect exposure to CVE-2023-20938 (binder UAF)
# Run on target host: Android via adb shell, Linux via SSH
# Exit codes: 0=PATCHED, 1=VULNERABLE, 2=UNKNOWN

set -euo pipefail

RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; NC='\033[0m'

# Detect platform
if [ -f /system/build.prop ] || getprop ro.build.version.sdk >/dev/null 2>&1; then
  # Android device
  PATCH_LEVEL=$(getprop ro.build.version.security_patch 2>/dev/null || echo "")
  if [ -z "$PATCH_LEVEL" ]; then
    echo -e "${YELLOW}[UNKNOWN]${NC} Cannot read Android security patch level."
    exit 2
  fi
  # Complete fix requires 2023-07-05 or later
  REQUIRED="2023-07-05"
  if [[ "$PATCH_LEVEL" > "$REQUIRED" ]] || [[ "$PATCH_LEVEL" == "$REQUIRED" ]]; then
    echo -e "${GREEN}[PATCHED]${NC} Android security patch level: $PATCH_LEVEL (>= $REQUIRED)"
    echo "CVE-2023-20938 and CVE-2023-21255 are both addressed."
    exit 0
  else
    echo -e "${RED}[VULNERABLE]${NC} Android security patch level: $PATCH_LEVEL (< $REQUIRED)"
    echo "Device is missing the complete fix for CVE-2023-20938."
    # Check if partial fix is present
    PARTIAL="2023-02-05"
    if [[ "$PATCH_LEVEL" > "$PARTIAL" ]] || [[ "$PATCH_LEVEL" == "$PARTIAL" ]]; then
      echo "NOTE: Partial fix (Feb 2023) is applied, but bypass via CVE-2023-21255 exists."
    fi
    exit 1
  fi
else
  # Linux server/workstation
  KERNEL=$(uname -r)
  echo "Kernel version: $KERNEL"

  # Check if binder module is loaded or built-in
  BINDER_LOADED=0
  if lsmod 2>/dev/null | grep -qi binder; then
    BINDER_LOADED=1
    echo "Binder module: LOADED (host is potentially exposed)"
  elif [ -e /dev/binder ] || [ -e /dev/hwbinder ] || [ -e /dev/vndbinder ]; then
    BINDER_LOADED=1
    echo "Binder device node: FOUND (binder may be built-in)"
  else
    echo -e "${GREEN}[PATCHED]${NC} Binder module is NOT loaded and /dev/binder does not exist."
    echo "This host is not exposed to CVE-2023-20938 (binder subsystem inactive)."
    exit 0
  fi

  if [ "$BINDER_LOADED" -eq 1 ]; then
    # Check known-fixed kernel versions for major distros
    # Debian bullseye: fixed in 5.10.158-1
    # Debian bookworm: fixed in 6.1.176-1
    # Upstream: fixed in 5.17.6+
    echo -e "${YELLOW}[UNKNOWN]${NC} Binder is active on kernel $KERNEL."
    echo "Manual verification required:"
    echo "  - Debian bullseye: need >= 5.10.158-1"
    echo "  - Debian bookworm: need >= 6.1.176-1"
    echo "  - Upstream: need >= 5.17.6 or backported patch"
    echo "  - Check: apt list --installed 2>/dev/null | grep linux-image"
    echo "  - Check: rpm -q kernel 2>/dev/null"
    exit 2
  fi
fi