- La seguridad de memoria y el sandboxing son conceptos de seguridad independientes entre sí, y se necesita contar con ambos para formar una defensa sólida
- Fil-C es una implementación con seguridad de memoria para C/C++, que garantiza seguridad hasta el nivel de llamadas al sistema de Linux y puede usarse incluso en componentes del sistema como OpenSSH
- Durante el proceso de portar el sandbox basado en seccomp-BPF de OpenSSH a Fil-C, los desafíos clave fueron la limitación en la creación de hilos y el ajuste de los filtros de seccomp
- Para la gestión de hilos en segundo plano del runtime de Fil-C, se agregó la API
zlock_runtime_threads(), que controla el comportamiento de los hilos dentro del sandbox
- Fil-C implementa la aplicación sincronizada de las llamadas a
prctl en todos los hilos del runtime, para que no_new_privs y los filtros seccomp se apliquen de forma consistente a todo el proceso
Relación entre seguridad de memoria y sandboxing
- La seguridad de memoria y el sandboxing son capas de seguridad distintas, y una sola no proporciona protección completa
- Ejemplo de algo seguro en memoria pero sin sandbox: un programa en Java que puede sobrescribir archivos a través de la entrada del usuario
- Ejemplo de algo con sandbox pero sin seguridad de memoria: un programa escrito en ensamblador con privilegios restringidos
- Los sandboxes reales tienen puntos débiles estructurales, como permitir la comunicación con un proceso broker
- Por eso, combinar seguridad de memoria y sandboxing es la mejor estrategia de defensa
Integración de Fil-C con el sandbox de OpenSSH
- Fil-C es una implementación con seguridad de memoria para C/C++ que mantiene esa seguridad al nivel de llamadas al sistema de Linux
- El runtime de Fil-C puede funcionar incluso en componentes de sistema de bajo nivel como
init y udevd
- OpenSSH funciona correctamente sobre Fil-C y aprovecha el sandbox seccomp-BPF
- En Linux, OpenSSH construye su sandbox con las siguientes herramientas
chroot para restringir el acceso al sistema de archivos
- ejecución con el usuario/grupo
sshd
setrlimit para limitar la apertura de archivos y la creación de procesos
- filtros seccomp-BPF para permitir solo las llamadas al sistema autorizadas
- Fil-C admite por defecto
chroot y el cambio de usuario, pero setrlimit y seccomp-BPF pueden entrar en conflicto con el comportamiento del runtime, por lo que se requirieron ajustes adicionales
Control de hilos en el runtime de Fil-C
- El runtime de Fil-C usa hilos en segundo plano para el recolector de basura y los detiene o reinicia automáticamente cuando es necesario
- El sandbox de
setrlimit de OpenSSH busca prohibir la creación de nuevos procesos, por lo que la creación de hilos por parte del runtime puede violar esa restricción
- Para resolverlo, se agregó la API
zlock_runtime_threads() en <stdfil.h>
- El runtime crea de inmediato los hilos que necesita y luego desactiva su finalización automática
- Se ejecuta en la función
ssh_sandbox_child de OpenSSH antes de llamar a setrlimit o seccomp-BPF
Ajustes al filtro seccomp de OpenSSH
- Después de aplicar
zlock_runtime_threads(), la mayor parte de las funciones del sandbox siguieron operando igual
- En el filtro seccomp se hicieron los siguientes cambios
- En caso de violación, se configuró
SECCOMP_RET_KILL_PROCESS para terminar también los hilos en segundo plano de Fil-C
- Se agregó
MAP_NORESERVE a la lista permitida para soportar el uso del asignador de memoria de Fil-C
- Se permitió la llamada a
sched_yield, que se usa en la implementación de locks de Fil-C
- Las llamadas
futex de Fil-C para sincronización ya estaban permitidas, así que no hicieron falta más cambios
Implementación de prctl en Fil-C
- OpenSSH usa dos llamadas a
prctl al instalar el filtro seccomp
PR_SET_NO_NEW_PRIVS para bloquear la obtención de privilegios adicionales
PR_SET_SECCOMP, SECCOMP_MODE_FILTER para activar el filtro
- El problema es que
prctl solo se aplica al hilo que lo invoca, lo que deja el riesgo de que otros hilos del runtime de Fil-C queden sin filtro
- Para evitarlo, Fil-C usa la API
filc_runtime_threads_handshake() para aplicar la configuración de forma sincronizada a todos los hilos del runtime
- Garantiza que cada hilo ejecute la misma llamada a
prctl
- Si existen varios hilos de usuario, genera un error de seguridad de Fil-C para reforzar la protección
Conclusión
- Combinar seguridad de memoria y sandboxing es la combinación de seguridad más sólida
- Fil-C integra por completo el sandbox basado en seccomp de OpenSSH sin reducir el nivel de protección y manteniendo la seguridad de memoria
- En entornos Linux, usar Fil-C permite asegurar al mismo tiempo la seguridad a nivel de sistema y la seguridad a nivel de lenguaje
1 comentarios
Comentarios en Hacker News
Se preguntan por qué no hay ninguna mención de landlock
Existe un enfoque de compilación híbrido que va de C → WASM → C
Esto permite controlar por completo la interacción con el SO y, al mismo tiempo, sandboxear el acceso a memoria como en WASM, aunque técnicamente siga siendo código C
Se puede ver material relacionado en RLBox
Pueden corromper la memoria, pero el alcance queda limitado al interior del sandbox
Sistemas como SECCOMP son burocráticos porque obligan a definir con mucho detalle todas las políticas de interacción
En cambio, Fil-C adopta un enfoque donde el propio lenguaje y el runtime buscan garantizar el comportamiento correcto del programa
Los binarios de Fil-C son ejecutables normales, así que también pueden usarse junto con sandboxes como SECCOMP
A Linux le tomó 20 años crear la interfaz
prctl, así que probablemente habrá que esperar 10 años para ver algo parecido en WASIIncluso dentro del sandbox se pueden generar flujos de ejecución extraños
El autor de Fil-C suele hacer inventos técnicamente interesantes, pero preocupa si la implementación ha sido suficientemente validada
Dijo que se pueden compilar programas setuid con Fil-C, pero la parte modificada de
ld.sopodría ser riesgosaLas apps setuid tienen que escribirse de forma muy defensiva respecto a variables de entorno, descriptores de archivo,
rlimit, señales y másEsas partes todavía parecen incompletas, así que usarlo en infraestructura real implica riesgo
Aun así, probar una base de código con Fil-C podría descubrir bugs interesantes
La modificación de
ld.soes pequeña y básicamente sirve para enseñarle el layout de libcEl bug de
getenvrelacionado con setuid ya se corrigió usandosecure_getenvEn lo señalado hay algo de verdad y algo de FUD
En situaciones de data races, en Fil-C podrían desalinearse los punteros y las capabilities
Eso podría causar una violación de seguridad de memoria
El autor lo niega, pero compararlo con Java no es apropiado
La tecnología es excelente, pero la actitud del autor reduce la confianza
WASM es a la vez sandbox y entorno de ejecución, y según cómo se use puede ofrecer cierto grado de seguridad de memoria
Si se compila C a WASM, los bugs siguen existiendo, pero su alcance queda limitado
Por eso tiene sentido clasificar a WASM como tecnología de sandboxing, aunque como entorno de ejecución ofrece más posibilidades
Un bug en el módulo B puede permitir leer datos del módulo A
O sea, WASM no es más que un reemplazo de los sandboxes de procesos livianos
Porque también podría decirse que C “es seguro dependiendo de cómo se use”
WASM evita escapar del runtime, pero no impide escapar de la memoria del programa interno
Piden una comparación entre Fil-C y Rust
Fil-C sirve para reforzar programas C existentes priorizando compatibilidad y seguridad
Rust es mejor para crear codebases nuevas con seguridad estática y rendimiento
Fil-C tiene una pequeña pérdida de rendimiento, pero es útil para código C existente (
ffmpeg,nginx,sudo, etc.)Rust destaca por el multithreading y su sistema de tipos
Su objetivo no es mejorar el diseño del lenguaje, sino asegurar la seguridad de memoria
Sus competidores se parecen más a D, Nim y Go que a Rust
Rust los previene en tiempo de compilación
Ambos enfoques son ortogonales, y a Rust también se le podrían añadir verificaciones en runtime al estilo Fil-C
Los MicroVM se están volviendo cada vez más populares
Hay curiosidad por cómo Fil-C podría aprovechar eso
Ojalá este proyecto reciba más atención
Sería bueno que herramientas clave como sudo o polkit se distribuyeran con seguridad de memoria
También quisieran ver más uso de sandboxing incluso en lenguajes con seguridad de memoria
Da pena que ni en Rust ni en Go se usen mucho los sandboxes a nivel de SO
Es difícil configurarlo a nivel de librería y depende bastante de la versión del kernel o de la implementación de libc
Además, tiene límites porque no puede filtrar entradas detrás de punteros, como rutas de archivo
Por eso normalmente hay que configurarlo de forma manual a nivel de aplicación
En cambio, Go tiene un runtime grande y por eso es más difícil volverlo seguro como Fil-C
Preguntan en qué se diferencia Fil-C del Address Sanitizer (ASan) de clang
Si solo produce un pánico en runtime, dicen, cuesta llamarlo “seguridad de memoria”
Algunos bugs no los detecta
Funciona dejando “red zones” alrededor de la memoria, así que a veces los detecta por suerte
Seguridad de memoria no significa “no se cae”, sino “un acceso inválido no logra producir efectos”
Preguntan por qué no usar una VM completa como sandbox
Un proceso analiza la entrada sin privilegios, y otro funciona con privilegios altos
Ambos procesos se comunican por IPC
Usar una VM mejora la seguridad, pero añade mucho overhead y complica cosas como el acceso a GPU o archivos
Por eso, en general, el sandboxing a nivel de SO suele ser más limpio
Hay que asignar la GPU de forma dedicada, y Qubes también se conecta solo por reenvío X11, así que no tiene aceleración