# eBPF helpers tampering with user memory

Rare eBPF helpers writing user buffers or spoofing syscall returns, hidden behind tracepoint hooks

_Area: Memory corruption and native code. Seen in 3 disclosed reports; latest 2026-07-27. Updated 2026-11-03._

## How it works
An eBPF program runs in kernel context but can reach into user memory and syscall results through a small set of helpers: `bpf_probe_write_user` writes into a user buffer the kernel has already filled, `bpf_override_return` replaces a hooked function's return value, and `bpf_send_signal` kills processes from kernel context [E1](https://bugbountylibrary.com/r/blog%3Asecuritylabs-datadoghq-com-articles-detection-primitives-for-ebpf-rootkits.md). The broken assumption is that userland sees what the kernel produced. A tracepoint on syscall exit runs after the kernel populated the buffer but before the caller reads it, so the program can rewrite the data in place [E3](https://bugbountylibrary.com/r/blog%3Aembracethered-com-blog-posts-2021-offensive-bpf-libbpf-bpf-probe-write-user.md). These helpers have almost no legitimate production-tracing use, which is what makes them a fingerprint [E1](https://bugbountylibrary.com/r/blog%3Asecuritylabs-datadoghq-com-articles-detection-primitives-for-ebpf-rootkits.md).

## Where it shows up
- Programs attached to `sys_exit_*` tracepoints that pair with `sys_enter_*` via a per-tid BPF map to stash a buffer pointer, then read or write it on exit [E2](https://bugbountylibrary.com/r/blog%3Aembracethered-com-blog-posts-2021-offensive-bpf-bpftrace-message-based.md)[E3](https://bugbountylibrary.com/r/blog%3Aembracethered-com-blog-posts-2021-offensive-bpf-libbpf-bpf-probe-write-user.md).
- Use of `bpf_probe_write_user`, `bpf_override_return`, or `bpf_send_signal` in any program not clearly a debugger or fault-injection tool [E1](https://bugbountylibrary.com/r/blog%3Asecuritylabs-datadoghq-com-articles-detection-primitives-for-ebpf-rootkits.md).
- Narrow program types combined with rare helpers at load time [E1](https://bugbountylibrary.com/r/blog%3Asecuritylabs-datadoghq-com-articles-detection-primitives-for-ebpf-rootkits.md).
- Hooks on `getdents64`, `read`, `recvfrom`, `accept`, `ptrace`, or `bpf` syscalls [E1](https://bugbountylibrary.com/r/blog%3Asecuritylabs-datadoghq-com-articles-detection-primitives-for-ebpf-rootkits.md)[E2](https://bugbountylibrary.com/r/blog%3Aembracethered-com-blog-posts-2021-offensive-bpf-bpftrace-message-based.md)[E3](https://bugbountylibrary.com/r/blog%3Aembracethered-com-blog-posts-2021-offensive-bpf-libbpf-bpf-probe-write-user.md).
- Directory listings or `/proc` output that disagree with raw syscall results [E3](https://bugbountylibrary.com/r/blog%3Aembracethered-com-blog-posts-2021-offensive-bpf-libbpf-bpf-probe-write-user.md).

## How to review
The invariant: no eBPF program outside an approved debugger/fault-injection set may call a helper that writes user memory or overrides a return value, and every loaded program's helper set must be visible and match its declared purpose. Check the raw helper bitmap at `BPF_PROG_LOAD` time, before `do_misc_fixups` rewrites helper IDs into `__bpf_call_base` offsets, by hooking `check_helper_call` and reading `insn->imm` [E1](https://bugbountylibrary.com/r/blog%3Asecuritylabs-datadoghq-com-articles-detection-primitives-for-ebpf-rootkits.md). Confirm the program type is consistent with the helpers used. For syscall hooks, verify enter/exit pairing cannot stash and rewrite a caller's buffer [E2](https://bugbountylibrary.com/r/blog%3Aembracethered-com-blog-posts-2021-offensive-bpf-bpftrace-message-based.md)[E3](https://bugbountylibrary.com/r/blog%3Aembracethered-com-blog-posts-2021-offensive-bpf-libbpf-bpf-probe-write-user.md). Treat any `bpf_probe_write_user` call as suspicious unless justified, and note that its warning log can itself be suppressed by hooking `syslog` [E3](https://bugbountylibrary.com/r/blog%3Aembracethered-com-blog-posts-2021-offensive-bpf-libbpf-bpf-probe-write-user.md).

## How finders spotted it
- Read helper semantics and the verifier flow (`check_helper_call`, `do_misc_fixups`, offset encoding), reasoning that rare helpers plus narrow program types form a load-time fingerprint before the program acts [E1](https://bugbountylibrary.com/r/blog%3Asecuritylabs-datadoghq-com-articles-detection-primitives-for-ebpf-rootkits.md).
- Wanted a message-based trigger, saw the buffer empty on `sys_enter_read`, and paired enter/exit with a map; `strace` on nginx showed `recvfrom` instead of `read`, prompting a parallel hook [E2](https://bugbountylibrary.com/r/blog%3Aembracethered-com-blog-posts-2021-offensive-bpf-bpftrace-message-based.md).
- Hit bpftrace limits (no `bpf_probe_write_user`), dropped to libbpf C, and reused `getdents64` tracepoints to rewrite directory entries [E3](https://bugbountylibrary.com/r/blog%3Aembracethered-com-blog-posts-2021-offensive-bpf-libbpf-bpf-probe-write-user.md).

## Variants seen
- Overwriting `getdents64` `d_name` fields to hide or spoof files from all userland tools [E3](https://bugbountylibrary.com/r/blog%3Aembracethered-com-blog-posts-2021-offensive-bpf-libbpf-bpf-probe-write-user.md).
- Overriding syscall returns to truncate enumeration at a hidden ID, hiding all higher-ID programs [E1](https://bugbountylibrary.com/r/blog%3Asecuritylabs-datadoghq-com-articles-detection-primitives-for-ebpf-rootkits.md).
- Killing debuggers via `bpf_send_signal` from kernel context [E1](https://bugbountylibrary.com/r/blog%3Asecuritylabs-datadoghq-com-articles-detection-primitives-for-ebpf-rootkits.md).
- Trigger-and-execute backdoor keyed on a magic string in `read`/`recvfrom` buffers [E2](https://bugbountylibrary.com/r/blog%3Aembracethered-com-blog-posts-2021-offensive-bpf-bpftrace-message-based.md).

## Impact and severity
Full concealment of sockets, BPF programs, files, and processes from userland, plus debugger termination, from a root-installed tracepoint hook [E1](https://bugbountylibrary.com/r/blog%3Asecuritylabs-datadoghq-com-articles-detection-primitives-for-ebpf-rootkits.md)[E2](https://bugbountylibrary.com/r/blog%3Aembracethered-com-blog-posts-2021-offensive-bpf-bpftrace-message-based.md)[E3](https://bugbountylibrary.com/r/blog%3Aembracethered-com-blog-posts-2021-offensive-bpf-libbpf-bpf-probe-write-user.md). Severity is high: the tampering is invisible to normal tooling, and a single override can hide an entire class of objects, not just one [E1](https://bugbountylibrary.com/r/blog%3Asecuritylabs-datadoghq-com-articles-detection-primitives-for-ebpf-rootkits.md). Detection depends on load-time ground truth the running rootkit cannot corrupt [E1](https://bugbountylibrary.com/r/blog%3Asecuritylabs-datadoghq-com-articles-detection-primitives-for-ebpf-rootkits.md).

## Seen in real software
Open-source packages and projects, and products, where this pattern was found. If the target runs one, compare the version it runs with the fix.
- **bpftrace** (version not stated): Offensive BPF: Using bpftrace to host backdoors. Report: https://bugbountylibrary.com/r/blog%3Aembracethered-com-blog-posts-2021-offensive-bpf-bpftrace-message-based.md

## Evidence
- E1: https://bugbountylibrary.com/r/blog%3Asecuritylabs-datadoghq-com-articles-detection-primitives-for-ebpf-rootkits.md
- E2: https://bugbountylibrary.com/r/blog%3Aembracethered-com-blog-posts-2021-offensive-bpf-bpftrace-message-based.md
- E3: https://bugbountylibrary.com/r/blog%3Aembracethered-com-blog-posts-2021-offensive-bpf-libbpf-bpf-probe-write-user.md
