The GPU bouncer checks your VIP wristband but never looks at your face, so anyone who flashes the right color walks into the vault
CVE-2022-22706 is a local privilege escalation in Arm's Mali GPU kernel driver — the graphics component shipping inside roughly 40% of all Android devices. The bug lives in kbase_jd_user_buf_pin_pages(), which pins user-supplied memory pages for GPU operations. When deciding whether a page should be writable, the function checks only the KBASE_REG_GPU_WR flag and ignores KBASE_REG_CPU_WR. An unprivileged app can import a read-only mapping (e.g. /etc/passwd or a shared library) through the Mali ioctl interface, trigger the page-pinning path, and silently promote that mapping to writable. From there, the attacker overwrites the page cache of sensitive files — blanking root's password hash or injecting code into a privileged process's loaded library — and escalates to root. Affected driver ranges are enormous: Midgard r26p0–r31p0, Bifrost r0p0–r35p0, and Valhall r19p0–r35p0. Fixed versions are Midgard r32p0 and Bifrost/Valhall r36p0, but the Android ecosystem didn't ship the fix until the June 2023 security patch level (2023-06-05), leaving a gap of over a year after Arm's own fix.
Arm assigned this HIGH / 7.8 and CVSS agrees the local-access prerequisite keeps it out of CRITICAL on paper. In practice the vendor score *understates* the operational risk. Google TAG confirmed it was weaponized as an n-day in a spyware campaign linked to Variston, delivered via SMS to Samsung handsets in the UAE in December 2022 — months before Android OEMs shipped the patch. CISA added it to the KEV catalog in March 2023. A compiled C proof-of-concept is public on GitHub (byt3quester/CVE-2022-22706-poc), and STAR Labs published a full exploit walkthrough in 2025. The sheer scale of the unpatched population — billions of devices, many of which will *never* receive a security update — combined with confirmed exploitation and a public, weaponizable PoC makes the 7.8 label feel generous. We push this to 8.0 to reflect the real-world exposure, while acknowledging that the local-code-execution prerequisite is a genuine gate that keeps it from CRITICAL.
6 steps from start to impact.
Initial code execution on-device
- Target device runs a vulnerable Mali GPU kernel driver (Bifrost ≤r35p0, Midgard ≤r31p0, or Valhall ≤r35p0)
- Attacker achieves local code execution via browser exploit, malicious app, or other app-level vector
- Requires a separate vulnerability or social-engineering step to get code running on the device
- Modern Android sandboxing (seccomp, SELinux) restricts available syscalls from app context, though Mali ioctls are typically permitted
Open Mali GPU device and create imported memory region
/dev/mali0 (or the equivalent character device) and uses KBASE_IOCTL_MEM_IMPORT to create a KBASE_MEM_TYPE_IMPORTED_USER_BUF memory object. The attacker deliberately sets the KBASE_REG_CPU_WR flag *without* KBASE_REG_GPU_WR. Because the vulnerable code path only gates on the GPU write flag, this combination is accepted without raising an error.- App has file-descriptor access to the Mali device node (standard on stock Android — not blocked by SELinux for apps using GPU)
- Attacker controls the ioctl parameters
- Some hardened ROM builds (GrapheneOS) restrict direct GPU device access from untrusted contexts, but stock Android and Samsung OneUI do not
Map target read-only file into the import buffer
mmaps a read-only file — the PoC uses /etc/passwd; a weaponized version targets a shared .so loaded by a privileged process — and feeds that mapping into the Mali imported-buffer region. The kernel pins the underlying physical pages via pin_user_pages_remote() with write=1 because of the missing KBASE_REG_CPU_WR check, even though the file VMA is read-only.- Target file is readable by the app (e.g., world-readable shared libraries,
/etc/passwd) - The Mali driver's page-pinning path calls pin_user_pages_remote with write=1
- If the target file is only readable by root (e.g. on hardened builds), this step fails — but most system libraries and /etc/passwd are world-readable on Android
Write to pinned pages via GPU job submission
/etc/passwd.- Successful page pinning from Step 3
- Ability to submit GPU jobs (normal app permission on Android)
- None — once pages are pinned writable, the GPU job completes reliably
Hijack privileged process or escalate to root
su root after blanking root's password. The STAR Labs weaponized chain goes further: it overwrites a shared library used by vold (Android's volume daemon running as root), triggering code execution in that context. From vold, the chain pivots through init → hal_neuralnetworks_armnn → loads a malicious kernel module (pktgen.ko) → disables SELinux → spawns a root reverse shell.- Modified page cache is consumed by a privileged process before being evicted
- SELinux not blocking the final payload execution (disabled in last stage of STAR Labs chain)
- dm-verity on A/B partition devices may prevent loading tampered system binaries on the read-only system partition — but the attack targets page cache of files on writable partitions or in-memory library mappings
- Samsung KNOX Integrity Measurement Architecture (IMA) provides some defence, but it was bypassed in the documented Variston chain
Full device compromise and data exfiltration
- Root access achieved in Step 5
- Google Play Protect on-device scanning may detect known implant signatures
- Device attestation (SafetyNet/Play Integrity) fails, potentially alerting MDM solutions
- Network-level IDS/IPS — This is a local kernel exploit triggered via GPU ioctls on the device itself. No network traffic is generated during exploitation. Snort/Suricata/Zeek rules cannot detect it.
- VPN or ZTNA alone — Encrypting or segmenting network traffic does not prevent a local privilege escalation on the endpoint. The attacker is already on the device.
- Android app-level sandboxing — While Android's app sandbox restricts inter-app access, the Mali device node (
/dev/mali0) is accessible from within the sandbox on stock Android. The entire point of this vuln is escaping the sandbox to root. - dm-verity — Protects the read-only system partition from modification, but the exploit targets page cache of files on writable partitions or in-memory library mappings. dm-verity does not cover
/data,/etc, or runtime library caches.
The supporting signals.
| In-the-Wild Status | Confirmed exploited. Google TAG documented use in a December 2022 spyware campaign linked to Variston, targeting Samsung devices in the UAE via SMS-delivered exploit chains. CISA added to KEV on 2023-03-30 with remediation due 2023-04-20. |
|---|---|
| Exploit Chain Context | Used as the kernel LPE stage in a four-bug chain: CVE-2022-4262 (Chrome type confusion, 0-day) → CVE-2022-3038 (Chrome sandbox escape, n-day) → CVE-2022-22706 (Mali LPE, n-day) → CVE-2023-0266 (ALSA sound subsystem, 0-day) + multiple kernel info-leak 0-days. |
| PoC Availability | Public. byt3quester/CVE-2022-22706-poc on GitHub — a compilable C exploit that overwrites /etc/passwd page cache and sus to root. STAR Labs published a full exploit walkthrough in February 2025 with multi-stage LPE to root reverse shell. |
| EPSS | 0.01062 (~top 10% of scored CVEs). Low automated-exploitation probability reflects the local-access prerequisite, but EPSS systematically underweights mobile/embedded chains where initial access comes from a separate bug. |
| CVSS Vector | CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H — 7.8 HIGH. Local vector, low complexity once on-device, low privilege required. The Scope:Unchanged is correct — the vuln stays within the kernel boundary even though the practical impact crosses process boundaries. |
| Affected Versions | Midgard r26p0–r31p0, Bifrost r0p0–r35p0, Valhall r19p0–r35p0. Essentially every Mali GPU driver shipped between ~2016 and January 2022. This covers Samsung Exynos, MediaTek Dimensity/Helio, HiSilicon Kirin, and other SoC families. |
| Fixed Versions | Arm driver: Midgard r32p0, Bifrost/Valhall r36p0 (released January 2022). Android AOSP: patch level 2023-06-05. Samsung: May 2023 SMR. Google Pixel: June 2023. Many budget/mid-range Android devices from 2018-2021 will never receive this fix. |
| Installed Base | Mali GPUs ship in ~40% of Android devices and >70% of smart TVs. Over 12 billion Arm GPUs shipped to date. The vulnerable driver range spans roughly 6 years of silicon. |
| Reporting Researcher | Reported to Arm with indication of in-the-wild exploitation. Root cause analysis published by Google Project Zero (as CVE-2021-39793 duplicate analysis). Full exploitation research by STAR Labs SG (Billy Jheng Bing-Jhong, Muhammad Alifa Ramdhan). |
| Disclosure Timeline | 2022-01: Arm releases fix in r36p0/r32p0. 2022-03-03: CVE published. 2022-12: Exploited in Variston-linked campaign. 2023-03-30: CISA KEV listing. 2023-06: Android AOSP incorporates patch. 2025-02: STAR Labs full exploit writeup published. |
Sources.
- Google TAG Blog – Spyware vendors use 0-days and n-days
- Google Project Zero – Root Cause Analysis (CVE-2021-39793 / CVE-2022-22706)
- STAR Labs – Mali-cious Intent: Exploiting GPU Vulnerabilities
- BleepingComputer – Android security update fixes Mali GPU bug exploited as zero-day
- Android Security Bulletin – June 2023
- NVD – CVE-2022-22706
- CVE Details – CVE-2022-22706
- byt3quester PoC – CVE-2022-22706-poc (via Sploitus)
Why this verdict
- Confirmed exploitation in sophisticated spyware campaign. Google TAG documented a Variston-linked operator using CVE-2022-22706 as the kernel-LPE stage in a four-bug chain delivered via SMS to Samsung devices in the UAE. This isn't theoretical — it was weaponized against real targets, and CISA added it to KEV in March 2023.
- Public, compilable PoC eliminates the skill barrier. The
byt3quester/CVE-2022-22706-pocGitHub repo is a self-contained C exploit that achieves root. STAR Labs' 2025 writeup provides a fully documented multi-stage escalation path. Any actor who can deliver code to a device can use this. - Massive, permanently unpatched installed base. Mali GPUs are in ~40% of Android devices — billions of handsets. The fix requires driver-level updates from OEMs. Budget and mid-range devices from 2018–2021 are overwhelmingly end-of-life and will never receive the June 2023 patch level. This creates a permanent population of exploitable devices.
- Local-access gate is real but routinely chained. AV:L means you need code on the device first. On mobile, this is supplied by browser exploits (as in the documented chain), malicious apps, or compromised SDKs. The prerequisite is not a hard barrier for any actor operating above script-kiddie level — it's the normal cost of doing business in mobile exploitation.
- Role multiplier: The canonical deployment role for Mali GPU drivers is *consumer and enterprise mobile devices*. In the low-value role (personal phone), compromise yields personal data — bad, but bounded. In the high-value role — executive/government/journalist phones — compromise yields corporate email, VPN credentials, MFA tokens, encrypted messaging plaintext, and real-time location. The documented Variston campaign specifically targeted this high-value role. However, this component is not canonically a fleet-management or identity-tier component (it's not a DC, hypervisor, or PKI system), so the blast radius is per-device rather than fleet-scale. This keeps the ceiling at HIGH rather than triggering the CRITICAL floor.
Why not higher?
CRITICAL would require either a network-facing attack vector (this is local-only) or a component whose compromise inherently cascades to fleet/domain/supply-chain scope. The Mali GPU driver runs on individual endpoints. Even full device compromise of one phone does not directly propagate to other devices or enterprise infrastructure without additional manual attacker effort. The local-access prerequisite means this bug is always the *second* step in a chain, never the initial entry point.
Why not lower?
The KEV listing and confirmed exploitation by a commercial spyware vendor immediately rule out anything below HIGH. The public PoC, the six-year window of affected driver versions, the billions of permanently unpatched devices, and the reliable root primitive make this one of the most impactful mobile LPE bugs disclosed in recent years. Downgrading to MEDIUM would require the attack to be unreliable or the population to be small — neither is true.
Crowdsourced verification payload.
Run this script on the target Android device via adb shell or a terminal emulator app. It checks the Mali GPU driver version reported by the kernel. No root required. Example: adb shell 'bash -s' < check_mali_cve_2022_22706.sh
#!/bin/bash
# check_mali_cve_2022_22706.sh
# Checks whether the Mali GPU kernel driver is vulnerable to CVE-2022-22706
# Run on the target Android/Linux device via: adb shell 'bash -s' < check_mali_cve_2022_22706.sh
# No root required. Exit codes: 0=PATCHED, 1=VULNERABLE, 2=UNKNOWN
VULNERABLE=1
PATCHED=0
UNKNOWN=2
# Try multiple known paths for Mali driver version
MALI_VERSION=""
for path in /sys/module/mali_kbase/version \
/sys/module/mali/version \
/sys/module/mali_kbase/parameters/mali_debug_level \
/proc/driver/mali/version; do
if [ -r "$path" ]; then
MALI_VERSION=$(cat "$path" 2>/dev/null)
break
fi
done
# Fallback: check Android security patch level
SPL=$(getprop ro.build.version.security_patch 2>/dev/null)
if [ -z "$MALI_VERSION" ] && [ -z "$SPL" ]; then
echo "UNKNOWN - Could not determine Mali driver version or Android patch level."
echo "Device may not have a Mali GPU or paths are non-standard."
exit $UNKNOWN
fi
# Check Android security patch level (fix shipped in 2023-06-05)
if [ -n "$SPL" ]; then
# Compare dates: if SPL >= 2023-06-05, patched at OS level
SPL_NUM=$(echo "$SPL" | tr -d '-')
FIXED_NUM="20230605"
if [ "$SPL_NUM" -ge "$FIXED_NUM" ] 2>/dev/null; then
echo "PATCHED - Android security patch level $SPL >= 2023-06-05."
echo "CVE-2022-22706 fix included in this patch level."
exit $PATCHED
fi
fi
# Parse Mali driver version string (e.g., "r36p0" or "r35p0")
if [ -n "$MALI_VERSION" ]; then
# Extract rXXpY format
DRIVER_VER=$(echo "$MALI_VERSION" | grep -oE 'r[0-9]+p[0-9]+')
if [ -n "$DRIVER_VER" ]; then
MAJOR=$(echo "$DRIVER_VER" | sed 's/r\([0-9]*\)p.*/\1/')
MINOR=$(echo "$DRIVER_VER" | sed 's/r[0-9]*p\([0-9]*\)/\1/')
# Bifrost/Valhall fixed in r36p0, Midgard fixed in r32p0
# Conservative check: r36p0+ is safe for all architectures
if [ "$MAJOR" -ge 36 ] 2>/dev/null; then
echo "PATCHED - Mali driver version $DRIVER_VER >= r36p0."
exit $PATCHED
else
echo "VULNERABLE - Mali driver version $DRIVER_VER is in the affected range."
echo "CVE-2022-22706 affects Midgard r26p0-r31p0, Bifrost r0p0-r35p0, Valhall r19p0-r35p0."
echo "Fixed in Bifrost/Valhall r36p0, Midgard r32p0."
exit $VULNERABLE
fi
fi
fi
# If we only have SPL and it's old
if [ -n "$SPL" ]; then
echo "VULNERABLE - Android security patch level $SPL is older than 2023-06-05."
echo "Device likely lacks the CVE-2022-22706 fix. Verify Mali driver version manually."
exit $VULNERABLE
fi
echo "UNKNOWN - Could not parse Mali driver version from: $MALI_VERSION"
exit $UNKNOWN