23 abril, 2021

LD_PRELOAD (Vector de ataque)

Las librerias compartidas (shared libraries) se cargan en tiempo de ejecución, logrando binarios más pequeños, pero esto puede darnos pequeños sustos. Las maneras en que se buscan y cargas las librerías tenemos:

  • LD_PRELOAD: Objetos compartidos que se cargaran antes de cualquier libreria.
  • LD_LIBRARY_PATH: Camino a los directorios de librerias.
  • /etc/ld.so.conf

Hoy hablaremos de LD_PRELOAD, tanto como funciona y como nos puede afectar en nuestra seguridad como por ejemplo el cómodo y a veces temido sudo cuando no tenemos una configuración correcta.

Lo primero sera ver como funciona LD_PRELOAD, para luego contar las cosas divertidas y problemas de seguridad con los que nos podemos encontrar.

Al ejecutar un programa con librerías dinámicas, no todo el código esta en el ejecutable, para localizar las funciones que no se encuentran en el código realiza los siguientes pasos:

  1. El programa se carga en memoria.
  2. El enlazador dinamico determina las bibliotecas (.so) necesarias y las carga en memoria
  3. Si esta definida la variable LD_PRELOAD le indica que esta biblioteca es la primera donde deberá buscar primero.

Esto que nos puede ser muy útil si estamos rediseñando una libreríay queremos probar una nueva versión sin instalarla definitivamente, puede ser un problema de seguridad como veremos. Y como lo mejor es con un ejemplo.

Como funciona LD_PRELOAD

Primero crearemos nuestro programa simple que nos dice la hora y un número aleatorio

y lo compilamos simplemente

gcc ej1.c -o ej1.bin

También crearemos una librería, con la función rand, que modificaremos y llamaremos «sinrand.c» y la libreria «sinrand.so».

gcc -shared -fPIC sinrand.c -o sinrand.so

Y podremos comprobar las diferencias de ejecución sin cargar este objeto antes de las librerías (LD_PRELOAD) ya sea solo para esta ejecución o si lo fijamos para todas como se puede ver en la figura.

Podemos ver dos formas de cargar el objeto generado «sinrand.so», uno anteponiendo la variable LD_PRELOAD y otra dejándola fija. Ojo eliminarla después de las pruebas para evitar errores en otros programas (ya vemos el problema de precargar funciones estandard modificadas.

También podríamos analizar las librerías cargadas con el comando «ldd», viendo que se añade ./sinrand.so delante de todas. El primer objeto «linux-vdso.» es un obejto virtual perteneciente al kernel.

Podemos listar los simbolos con la instrucción «nm» tanto de las librerías que carga el programa

nm -D /lib/x86_64-linux-gnu/libc.so.6

Trabajando con LD_PRELOAD

Ya hemos visto como funciona LD_PRELOAD y ha llegado el momento de ver que cosas podemos hacer. Se ha visto que es muy sencillo modificar el funcionamiento de una función, pero si lo que deseamos es interceptarla para analizar algo y que luego se ejecute de forma normal.

Lo que parecería tan simple como ejecutar la función, como nos podemos imaginar no valdría, ya que entraríamos en una recursividad infinita. Para esto debemos realizar unos pequeños cambios en nuestro código anterior.

Añadimos la directiva _GNU_SOURCE y se incluye «dlfcn.h».

Definimos, con typedef, el alias a la función puntero de rand original.

Se recoge el puntero a la función original con «dlsym(RTLD_NEXT,<funcion>)» y se llama al rand original con el puntero. compilar con libreria dl (-ldl)

gcc -shared -fPIC intercepta_rand.c -o intercepta_rand.so -ldl

Esto ha sido un ejemplo sencillo con una función simple sin parámetros. Si usáramos una función más compleja, como printf por ejemplo, con parámetros tampoco se complicaría ya que lo único que se cambiaría es el prototipo de la función puntero y se añadirían los parámetros como podemos ver en el ejemplo en el que se realiza una captura de sprintf pero añadiendo a la cadena a devolver «***Interceptado***<texto original>***FIN***2020».

Lo primero es conocer la definición exacta de la función sprinf, para así crear el prototipo.

int sprintf (char *cadena, const char *formato, ...)
El prototipo seria typedef int(*original)(char *cadena, const char *formato, ...);

Con esto podemos ver que lo que podemos hacer con LD_PRELOAD, dependera de nuestra imaginación: capturas para depuración y análisis de código, interceptar datos(imagina si se define un nuevo fopen) ya sea para espiar o modificar, solucionar errores mientras el desarrollador crea una versión nueva, etc.

Alertas de seguridad con LD_PRELOAD

Ya hemos podido ver que se pueden realizar muchas cosas, ahora trataremos el tema de la seguridad para evitarnos problemas.

sudo

«sudo» (Super User Do) que es hacer de super usuario, o lo que es lo mismo, permitirá ejecutar dicho comando como root sin cambiar su usuario. Linux confirmará si ese usuario en particular está en el archivo «sudoers» para saber si puede ejecutar o no. No solo verifica el usuario sino también si puede ejecutar ese programa o no y si hereda variables de entorno.

En el caso que hoy nos aplica de la variable «LD_PRELOAD», ya que las escaladas con «sudo» son muchas, la variable de configuración de «sudoers» que debemos tener en cuenta es «Defaults env_keep+=LD_PRELOAD». Si tenemos esta opción habilitada la probabilidad de un ataque de escalada de privilegios esta muy garantizada, tengamos en cuenta que una ejecución con sudo nos lleva a ataques similares a los de SUID.

Lo que deberemos realizar es comentar esta linea, para evitar que un usuario ejecutando «sudo» pueda usar la variable «LD_PRELOAD». En realizad y saliendo un poco del tema del que estamos hablando, el consejo es que se limite al máximo el uso en «sudo» por ejemplo no «ALL=(ALL:ALL) ALL» evitarlo y definir solo esos programas que realmente usamos muy continuamente con necesidad de root ya que de esta forma evitaremos que un simple «perl» con permiso de sudo nos permitir tener un shell con los permisos de super usuario, que siempre nos pida la clave(ojo a la palabra clave NOPASSWD) así de esta forma si se obtiene acceso al usuario sin conocimiento de la clave no se podrá usar «sudo». Es una cuestión de poner las máximas barreras y que nos permitan claro un uso amigable de nuestro sistema, en nosotros esta esa balanza, en mi caso para un servidor publico (no mi Desktop) eliminaría el «sudo«.

Con esto terminamos con SUDO, que daría para otra entrada dedicada solo a sus seguridad.

Sustitución de librerías

Si conocemos llamadas, que están en librerías dinámicas del ejecutable, podremos crear una librería con esa llamada y luego cargar la nuestra con LD_PRELOAD. Por eso lo mejor en programa ya en uso es quitar información de depuración para que se conozca lo mínimo de el. Ya vimos en el principio de la explicación de las librerías como capturar funciones y que ademas luego se ejecutase la original de forma que no solo podemos escalar privilegios, sino que podemos dejar un espía en un ejecutable totalmente inocente y sin necesidad de tocarlo.

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Información básica sobre protección de datos Ver más

  • Responsable Javier.
  • Finalidad  Moderar los comentarios. Responder las consultas.
  • Legitimación Su consentimiento.
  • Destinatarios  Hostinger.es.
  • Derechos Acceder, rectificar y suprimir los datos.
  • Información Adicional Puede consultar la información detallada en la Política de Privacidad.

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de sus datos para estos propósitos. Ver
Privacidad