Android Binder Attack Matrix: Exploitation of CVE-2023–20938 (Article — 2)
This article will mostly be a diagram focused article since the exploitation steps are merely a proof-of-concept. Since these article series are targeted towards absolute beginners, it barely makes sense to discuss the exact steps, let alone describe the process textually. Hence, this article will mostly be a combination of diagrams and video recordings to allow beginners to obtain the information shared here efficiently.
Leak Primitive
static int binder_thread_read(...)
struct binder_transaction_data_secctx tr;
struct binder_transaction_data *trd = &tr.transaction_data;
...
trd->target.ptr = target_node->ptr; [1]
trd->cookie = target_node->cookie; [1]
...
if (copy_to_user(ptr, &tr, trsize)) { [2]
Step — 1: Exploit the vulnerability to free a binder_node.
Step — 2: Allocate a kernel object at the same memory location.
Step — 3: Leak values of a kernel object at offset 88 and 96.
Overlaying another kernel object, such as epitem
, onto a freed binder_node
in the Linux Kernel presents unique challenges. While userspace tasks are simpler—glibc's malloc
uses a common freelist—the Linux Kernel's SLUB allocator allocates different object types from distinct caches, complicating manipulation. Recent security mitigations include CONFIG_SLAB_FREELIST_HARDENED
, CONFIG_SLAB_FREELIST_RANDOM
, and GFP_KERNEL_ACCOUNT
(removed in version 5.9, reintroduced in 5.14). Additionally, eliminating cache aliasing for epitem
(SLAB_ACCOUNT
) and restricting unprivileged userfaultd
for heap spraying enhance kernel security.
Our target version, 5.10, incorporates these measures, making exploitation more challenging.
Arbitrary Read
Use Leak Primitive to leak a pointer to a struct file.
ioctl(fd, FIGETBSZ, &value); //&value == argp
static int do_vfs_ioctl(struct file *filp, ...) {
...
struct inode *inode = file_inode(filp)
...
case FIGETBSZ;
...
return put_user(inode->i_sb->s_blocksize, (int __user *)argp);
Unlink Primitive
Step — 1: Exploit the vulnerability to create a dangling Node.
Step — 2: Allocate a fake binder_node with sendmsg() syscall (heap spray).
Step — 3: Use-after-free when decrementing the refcount of a binder_node.
static bool binder_dec_node_nilocked(struct binder_node *node, ...) {
...
// If binder_node's refcount reaches 0
// and satisfy all checks
...
hlist_del(&node->dead_node);
...
// *pprev = next
// *(next +8) = pprev
}
Root Privilege Escalation
Step — 1: Obtain an address to struct cred
Step — 2: Set uids & gids to 0
Step — 3: Set selinux_state.enforcing to 0 to disable SELinux
Step — 4: Seccomp and Linux capabilities (not covered)
Conclusion
And with that Ladies & Gentlemen, it’s a wrap! This article was diagram intensive to illustrate the exploit description allowing the Security Actors to understand and implement their own exploit codes to test these vulnerabilities on Android Binder. Feel free to reach out to me for clarifications and discussions. Until next time, Folks. Cheers!