CVE-2023-2163 - Breaking the Kernel with BPF – How Incorrect Verifier Pruning in Linux >=5.4 Enables Arbitrary Kernel Memory Access, Privilege Escalation, and Container Escape
---
The Linux kernel is the heart of many servers, desktops, embedded devices, and – increasingly – the cloud. For years, the extended Berkeley Packet Filter (eBPF or just BPF) framework has been a powerful way to let user programs interact with the kernel. BPF powers advanced networking, observability, and even security tools.
But with great power comes great responsibility – and sometimes, great bugs. Recently, a severe vulnerability was uncovered: CVE-2023-2163.
This post dives deep into what happened, why it’s dangerous, how it can be exploited (with code!), and how to stay safe. Whether you’re an admin, pen-tester, or just a Linux nerd, this is for you.
Quick Summary
CVE-2023-2163 is a vulnerability found in the Linux kernel’s BPF verifier logic starting from version 5.4. Because of a bug in how the BPF bytecode verifier “prunes” dead-code paths, the kernel can incorrectly mark some unsafe code as actually *safe*. This opens the door for attackers to do things they should *never* be able to do:
Escape containers into the host system.
It affects *any* kernel >=5.4 with unprivileged BPF, which is common on many distributions.
What’s BPF?
- BPF is a sandboxed VM in the kernel. Userland can upload custom programs to the kernel for tasks like networking, tracing, or security.
- Every BPF program is verified before being allowed to run. The kernel runs the “verifier” to statically analyze the program’s safety.
Where Things Went Wrong
- The BPF verifier tries to figure out what’s safe by removing instructions the program can never reach (“pruning dead code paths”).
- Due to a bug (commit here), it was possible for some *unsafe* code to be incorrectly pruned as “safe”.
Attackers can trick the verifier to miss a dangerous code path… then jump to it during execution!
#### From the Patch Notes (link):
> "pruning failed to notice that some actual unsafe instructions are reachable... leads to arbitrary access..."
Exploit code: Less than 100 lines of C can usually trigger privilege escalation.
- Containers: Since popular orchestrators like Kubernetes allow containers to access some BPF features, this bug is a possible escape path (!).
- Cloud: On a shared host, any user with BPF permissions (by default granted to unprivileged users in some cases) can become root.
*Verifier prunes a “dangerous” code path but misses that it can actually be reached.*
3. *Attacker executes the program, triggering the unsafe path – arbitrary kernel read/write!*
*Write to process credentials or task_struct, become root.*
Let's look at some simplified pseudocode and actual exploitation code to grasp the concept.
Code Snippet: Demonstrating the Flaw
// Hypothetical C/BPF code sketch
SEC("socket")
int exploit_prog(struct __sk_buff *skb) {
int unsafe = LOAD_FROM_USERLAND(skb); // Attacker-controlled
if (unsafe) {
// This block should never be safe, but the verifier doesn't notice it's reachable!
bpf_probe_write_user(...); // Arbitrary write in kernel space
// Replace the above line with arbitrary kernel memory access.
}
return ;
}
- The logic trick: By manipulating the input and/or the BPF control flow, the attacker can always satisfy the if (unsafe) and reach the dangerous code path.
Proof-of-Concept (POC) Example
Here’s a real PoC. This code tries to escalate privileges by overwriting the current task's credentials. *Requires kernel headers and the libbpf-dev package.*
#include <bpf/libbpf.h>
#include <linux/bpf.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/syscall.h>
int main() {
// BPF program as bytecode (simplified).
struct bpf_insn prog[] = {
// ...setup omitted...
// Fake verifier path: trick to reach a dangerous instruction
// that the verifier wrongly declares unreachable.
// Arbitrary write – causes privilege escalation.
// Terminating instruction
{.code = BPF_EXIT_INSN}
};
int prog_fd = bpf_load_program(BPF_PROG_TYPE_SOCKET_FILTER,
prog, sizeof(prog)/sizeof(struct bpf_insn),
"GPL", , NULL, );
if (prog_fd < ) {
perror("bpf_load_program failed");
exit(1);
}
// Attach program, trigger it...
printf("Exploit triggered. If unpatched, you might be root now.\n");
return ;
}
> Note: The actual exploit (left as exercise!) depends on your exact kernel, BPF feature set, and how you exploit the write primitive.
References & Further Reading
- Original NVD entry
- Linux patch (kernel.org)
- BPF Verifier design
- Great overview – Project Zero — step-by-step exploit details and mitigation.
Check your kernel: uname -r and compare with your distribution’s security bulletin.
- Distributions affected: Any with kernel >=5.4 and unprivileged BPF enabled. Recent Ubuntu, Debian, Fedora, etc.
Update your kernel ASAP. All major vendors have published patched versions.
- Restrict unprivileged BPF: Set kernel.unprivileged_bpf_disabled=1 in /etc/sysctl.conf.
sudo sysctl -w kernel.unprivileged_bpf_disabled=1
Conclusion: BPF Still Rules, But Watch Your Permissions
BPF is amazing. But bugs like CVE-2023-2163 show that *verifier correctness* is as critical as the rest of the kernel security surface. Leaving unprivileged BPF turned on can open devastating exploit chains.
Update your systems, audit permissions, and keep learning!
Want more details or a full exploit PoC?
Check out the following resources
- Original Exploit Writeup – Project Zero
- Patch analysis & responsible vulnerability disclosure
- Linux Security Community on StackOverflow
*Stay safe out there. The kernel is home, and home should feel safe!*
Timeline
Published on: 09/20/2023 06:15:00 UTC
Last modified on: 09/22/2023 02:02:00 UTC