Empezamos con un poco de historia... eBPF viene de BPF (Berkeley Packet Filter), desarrollado en los años 90 para Linux y BSD. La idea original era filtrar paquetes de red de manera eficiente, permitiendo que los programas inspeccionaran headers de red sin salir del kernel y sin escribir código kernel complejo. Antes de BPF los filtros de paquetes eran lentos y monolíticos, con BPF disponíamos de un pequeño lenguaje de bytecode, ejecutado por una máquina virtual ligera en el kernel y nos permitía escribir “programas” que analizaban paquetes en tiempo real y solo pasaban al userland lo que te interesaba. Era seguro, rápido y muy flexible.
Posteriormente alrededor de 2014-2015 surge eBPF (Extended BPF) como una extensión masiva de BPF clásico. Sus mejoras clave:
- Hooks genéricos: no solo red, ahora puedes engancharte a syscalls, tracepoints, funciones del kernel (kprobes), funciones de programas userland (uprobes), sockets, etc.
- Estructuras de datos avanzadas (maps): hash, arrays, stacks, bloom filters… para compartir datos entre kernel y userland.
- Verificación de seguridad: el bytecode pasa un verificador que garantiza que no habrá bucles infinitos ni accesos ilegales.
Y 10 años después, eBPF (Extended Berkeley Packet Filter) se ha convertido en una de las piezas más potentes e innovadoras del kernel Linux. Es decir, lo que comenzó como un simple mecanismo para filtrar paquetes de red ha evolucionado en un motor de ejecución de bytecode dentro del kernel, con aplicaciones en observabilidad, seguridad, tracing y rendimiento.
Debido a ello, los equipos de seguridad defensiva han abrazado eBPF para monitorizar llamadas al sistema, detectar anomalías en tiempo real y reducir la superficie de ataque. Pero como suele suceder, lo que potencia la defensa también puede inspirar al ataque. Investigadores han comenzado a explorar el lado oscuro de eBPF: rootkits invisibles, técnicas de evasión de EDR, persistencia en kernel space y manipulación sigilosa de syscalls.
Este artículo vamos a jugar un poco con eBPF para entender cómo un atacante podría abusar de esta tecnología con varias PoCs sencillas. Primero empezamos con la instalación:
🔹 Ubuntu/Debian (kernel moderno recomendado)
🔹 Fedora
🔹 Arch Linux
🔹 Verificar
-
Comprueba que el kernel soporta eBPF:
(idealmente 5.x o 6.x).
-
Comprueba que el pseudo-filesystem está montado:
Si no aparece, móntalo con:
-
Prueba que funciona con un one-liner de
bpftrace
:
Deberías ver en tiempo real qué procesos invocan execve
.
Ahora que lo tenemos en nuestro sistema, vamos con las PoCs:
PoC 1: Ocultando procesos con eBPF y kprobes
Un clásico rootkit en Linux manipula la syscall getdents
para que ls
o ps
no muestren ciertos procesos. Con eBPF podemos hacer algo similar sin modificar el kernel ni cargar módulos sospechosos.
Código eBPF (C):
// hide_pid.bpf.c
#include <uapi/linux/ptrace.h>
#include <linux/sched.h>
#include <linux/fs.h>
#include <linux/dirent.h>
#include <linux/uaccess.h>
#define HIDE_PID 1337 // PID a ocultar
SEC("kprobe/getdents64")
int bpf_prog(struct pt_regs *ctx) {
struct linux_dirent64 __user *dirent = (struct linux_dirent64 *)PT_REGS_PARM2(ctx);
// Aquí podríamos parsear la lista de entradas y filtrar la que coincida con HIDE_PID.
// Por simplicidad este PoC sólo demuestra el hook.
bpf_printk("Interceptada llamada a getdents64!\n");
return 0;
}
char _license[] SEC("license") = "GPL";
Carga con bpftool:
Ahora cada vez que un proceso llame a getdents64
, nuestro programa eBPF se ejecutará.
La versión completa podría inspeccionar los nombres de directorio y eliminar la entrada que corresponde al PID a ocultar.
PoC 2: Persistencia avanzada: mapas “pinneados” en /sys/fs/bpf/
Normalmente, cuando un proceso userland muere, cualquier estructura de memoria que haya creado también desaparece. Pero los maps eBPF pueden “pinnearse” en el pseudo-filesystem /sys/fs/bpf/
, sobreviviendo al proceso. Esto permite a un atacante almacenar datos o estados que persisten sin depender de un demonio activo.
Ejemplo conceptual seguro:
-
Crear un map y pinnearlo:
-
Incluso si cierras el proceso que creó el map, puedes volver a leerlo:
Ejemplos de mapas persistentes y agregación avanzada
a) Map persistente “pineado”
-
Este mapa sobrevive al cierre del proceso que lo creó.
-
Ideal para almacenar contadores agregados de syscalls o eventos en laboratorio.
b) Actualización y lectura segura
-
Permite experimentar con agregación de datos de múltiples procesos sin interferir con el kernel o userland.
Limitaciones y detección
- Cargar programas eBPF requiere privilegios (
CAP_BPF
oCAP_SYS_ADMIN
en kernels antiguos). - Blue Teams pueden monitorizar
/sys/fs/bpf/
,bpftool
y syscalls relacionadas. - Herramientas como Falco y BPF LSM comienzan a detectar usos anómalos.
Aun así, el blind spot es considerable, ya que muchos EDRs todavía no inspeccionan eBPF.
Comentarios
Publicar un comentario