LinuxParty

NUESTRO SITIO necesita la publicidad para costear hosting y el dominio. Por favor considera deshabilitar tu AdBlock en nuestro sitio. También puedes hacernos una donación entrando en linuxparty.es, en la columna de la derecha.
Inicio desactivadoInicio desactivadoInicio desactivadoInicio desactivadoInicio desactivado
 
Durante años, desde que los ordenadores han estado al alcance de las más calenturientas mentes de los hackers de antaño, multitud de extrañas ideas han pasado por sus cabezas, muchas de las cuales fueron llevadas a la práctica y de entre todas ellas, una que creó escuela (sobre todo allá por los años ochenta y principios de los noventa) fue la de infección de binarios. Escribir un programa que puede inocularse a otro, pegársele como un parásito y extenderse por ahí, suena casi a ciencia ficción, ¿cómo resistirse? lo largo de la historia de la computación, fueron múltiples las implementaciones prácticas de la infección de archivos ejecutables. Algunos veteranos recordarán las historias del paquistaní Brain, el Virdem, el Vienna, el WHALE y el más moderno CIH.

Todos estos son diversos tipos de virus de DOS/Windows que infectaban ejecutables tipo COM y EXE, y hay mucha literatura acerca de infección sobre este tipo de binarios. Sin embargo, ya no hay tanto (y menos en español) acerca de infección bajo Unix. Lo bueno -o malo, según se mire- de Unix es que de aquí a hace unos años existe un estándar de facto en lo que al tema de formato de ejecutables se refiere. Hablo, por supuesto del Executable and Linkable Format (ELF) cuyo éxito motiva el siguiente artículo.

Y es a partir de aquí cuando debo introducir el DISCLAIMER de rigor para que los geos no me hagan una desagradable visita a la puerta de mi casa. La única finalidad de esta publicación es ilustrar por cuántos sitios un ELF puede ser explotado para a) securizar nuestras aplicaciones incluyendo diversas comprobaciones al arranque o b) aplicar lo expuesto como alguna retorcida técnica a utilizar en algún CTF o como simple curiosidad para aquellos a los que nos gusta jugar con cosas así de raras. El hacer algo más allá ya no es que me limite a decir que es responsabilidad del que lo usa, que cae de cajón, si no que afirmo categóricamente que es mala idea. No es la primera vez que un bicho de estos se escapa de la cámara de incubación para la que ha sido diseñado, y sus desafortunadas leyendas perduran.

Y aclarado esto, saquemos nuestras herramientas de doctor Frankenstein. Vamos a pensar cómo se podría crear un monstruo.

Como este artículo toca tantas cosas, y probablemente me enfrento a un conjunto de lectores con muy diversos grados de conocimiento, he decidido dividirlo en 5 entregas para que cada cual salte a la que le interese:
  1. Introducción al formato ELF y el código PIC
  2. Técnicas de inyección.
  3. Técnicas de activación.
  4. Técnicas de armouring y antidebug.
  5. Securización.

Estas entregas van a ser en general bastante técnicas, por lo que sería bueno que el lector estuviese familiarizado con:
  • El lenguaje C y el compilador de C de GNU
  • Unix en general, y Linux en particular.
  • Programación en ensamblador para x86 (32 bits)
  • El inline assembly de GCC.
Hay mucho que escribir sobre este tema, y dedicar el humilde espacio que una entrada de blog puede darme me obligará a saltarme muchas cosas. Aún así, espero poder aproximarme a la realidad de la explotación de un binario ELF.

El formato ELF
El formato ELF es un estándar que nació en el seno de los Unix System Laboratories de AT&T Bell allá por el 1997 con la intención de superar las limitaciones del antiguo formato a.out. A día de hoy es usado por la mayoría de sistemas *nix como Linux, BSD o Solaris sobre procesadores tanto de 32 como de 64 bits, little endian o big endian, dando como resultado la existencia de dos grandes subconjuntos de formatos: El ELF de 32 bits y el ELF de 64 bits, cuyas cabeceras son ligeramente diferentes debido a las diferencias en el ancho de palabra.

El formato ha sido pensado tanto para ejecutables como para bibliotecas dinámicas, pasando por ficheros objeto y volcados de memoria, aunque este artículo va a estar enfocado sobre todo hacia el primero.

En cualquier caso, se suele decir que un fichero ELF se compone de una cabecera al principio del archivo con unos números mágicos apropiados e información general (del tipo "yo soy un ELF ejecutable para tal procesador y me ejecuto a partir de aquí") y de (hasta) dos tablas de cabeceras que sirven para dotar al fichero ELF de dos "vistas": una vista de segmentos (la cual es indexada mediante un array de estructuras llamado tabla de cabeceras de programa) y una vista de secciones (indexada por otro tipo de array de estructuras llamado tabla de cabeceras de sección). Si bien las dos sirven para definir de algún modo el tipo de información que alberga nuestro fichero ELF, la primera es necesaria para decirle al kernel (o al enlazador si nuestro binario es dinámico) qué porciones de nuestro fichero se cargan en qué partes del espacio de direcciones virtuales y con qué permisos (lectura / escritura / ejecución), y la segunda tiene como finalidad darle al enlazador información sobre qué bibliotecas necesita cargar, qué símbolos debe importar, qué símbolos exporta, etc.

Lo importante de ambas tablas es que no cubren todo el binario, y en la tabla de segmentos habrá zonas que incluso se solapen. Esto tiene como consecuencia que existirán zonas no referenciadas por ninguna de las dos tablas, existirán espacios vacíos o gaps que podríamos modificar a nuestro antojo de forma totalmente asintomática de cara al funcionamiento del binario (aquí probablemente ya haya alguno al que se le pongan los dientes largos).

Este estudio se centrará en el ELF ejecutable para x86, o sea que trabajaremos con ELFs de 32 bits y little endian. En teoría, esto se podría extrapolar a otros formatos (de hecho, me habría gustado probar esto en un UltraSPARC IIe a 64 bits, pero el router que lo saca a Internet ha muerto :( espero poder hacer un anexo más adelante cuando tenga el cacharro de nuevo en mis manos), pero de momento nos conformaremos con esta arquitectura, a día de hoy todavía muy extendida y bien conocida por muchos.

A lo largo de esta serie de entregas voy a referirme constantemente a las diversas estructuras que son contenidas por los ficheros ELF, así que a modo de referencia rápida incluiré una pequeña referencia de las mismas. Para trabajar con ellas desde C bastará con incluir el fichero de cabecera < elf.h >. La cabecera del ELF32 es tal que así:

Leer aquí el resto del artículo.

Pin It

Escribir un comentario


Código de seguridad
Refescar



Redes:



 

Suscribete / Newsletter

Suscribete a nuestras Newsletter y periódicamente recibirás un resumen de las noticias publicadas.

Donar a LinuxParty

Probablemente te niegues, pero.. ¿Podrías ayudarnos con una donación?


Tutorial de Linux

Filtro por Categorías