Análisis de Ficheros ELF: Desentrañando Binarios de Linux para Hacking Ético 🕵️♀️
Este tutorial te sumergirá en el mundo del análisis de ficheros ELF (Executable and Linkable Format), el formato estándar para ejecutables, librerías compartidas y archivos objeto en sistemas operativos tipo Unix como Linux. Aprenderás a utilizar herramientas esenciales para inspeccionar la estructura interna de estos binarios, comprender su funcionamiento y descubrir posibles vulnerabilidades que pueden ser explotadas en un contexto de hacking ético. Desde las cabeceras hasta los símbolos y las tablas de reubicación, desvelaremos los secretos de los binarios ELF.
🚀 Introducción al Mundo ELF en Hacking Ético
En el fascinante universo de la ciberseguridad, y específicamente en el hacking ético, comprender cómo funcionan los programas a un nivel bajo es una habilidad invaluable. Los sistemas operativos basados en Unix, como Linux, utilizan el formato de fichero ELF (Executable and Linkable Format) para sus ejecutables, bibliotecas compartidas y archivos objeto. Analizar estos ficheros es crucial para detectar vulnerabilidades, ingeniería inversa y desarrollo de exploits.
Este tutorial te guiará a través de las herramientas y técnicas fundamentales para diseccionar binarios ELF. Aprenderás a inspeccionar su estructura interna, identificar componentes clave y extraer información valiosa que puede ser utilizada en auditorías de seguridad.
📖 ¿Qué es un Fichero ELF? Una Visión General
El formato ELF es un estándar flexible y extensible diseñado para optimizar la carga y ejecución de programas. En esencia, un fichero ELF es un contenedor que organiza diferentes tipos de datos y código de manera estructurada. Cada fichero ELF consta de una cabecera ELF, que describe la estructura general del archivo, seguida de diferentes secciones y segmentos.
Tipos de Ficheros ELF 🏷️
Existen varios tipos de ficheros ELF, cada uno con un propósito específico:
- Relocatable File (Archivo Relocatable): Contiene código y datos adecuados para ser enlazados con otros archivos objeto o bibliotecas estáticas para crear un ejecutable o una biblioteca compartida. No es directamente ejecutable.
- Executable File (Archivo Ejecutable): Contiene un programa que puede ser ejecutado directamente por el sistema operativo. Especifica cómo el programa debe ser cargado en memoria.
- Shared Object File (Archivo de Objeto Compartido): Generalmente son bibliotecas compartidas (librerías
*.soen Linux). Pueden ser enlazadas dinámicamente en tiempo de ejecución por múltiples programas. - Core File (Archivo Core): Un volcado de memoria de un proceso en el momento en que terminó anormalmente (colapsó). Útil para depuración post-mortem.
Estructura de un Fichero ELF 🗺️
Un fichero ELF se puede ver desde dos perspectivas principales:
- Vista de Enlace (Linking View): Utilizada por el enlazador. Organiza el archivo en secciones (sections), que agrupan datos y código por tipo (ej.
.textpara código,.datapara datos inicializados,.rodatapara datos de solo lectura). - Vista de Ejecución (Execution View): Utilizada por el cargador de programas. Organiza el archivo en segmentos (segments), que son regiones de memoria a las que se cargan las secciones. Por ejemplo, un segmento de código
LOADpuede contener las secciones.texty.rodata.
🛠️ Herramientas Esenciales para el Análisis ELF
Para desentrañar los secretos de los binarios ELF, necesitaremos un conjunto de herramientas poderosas y omnipresentes en cualquier distribución de Linux. Estas utilidades forman parte del paquete GNU Binutils y son la base de nuestro análisis.
1. file - Identificando el Tipo de Fichero 🔍
La primera herramienta en nuestro arsenal es file. Es sencilla pero indispensable para obtener una descripción general del tipo de archivo con el que estamos tratando.
file /bin/ls
Salida esperada:
/bin/ls: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=..., stripped
Esta salida nos indica que /bin/ls es un fichero ELF de 64 bits, de tipo shared object (aunque se ejecuta como un programa, es en realidad un objeto compartido en sistemas modernos), para arquitectura x86-64, enlazado dinámicamente y con el intérprete (ld-linux-x86-64.so.2) especificado. La etiqueta stripped indica que los símbolos de depuración han sido eliminados.
2. readelf - El Microscopio ELF 🔬
readelf es, sin duda, la herramienta más completa para inspeccionar archivos ELF. Permite ver casi todos los aspectos de la estructura del binario, desde las cabeceras hasta las tablas de símbolos y reubicación.
Inspeccionando la Cabecera ELF
La cabecera ELF es lo primero que el sistema lee. Contiene información vital como la arquitectura, el tipo de archivo, el punto de entrada y la ubicación de las tablas de cabeceras de programa y de sección.
readelf -h /bin/ls
Salida (extracto):
ELF Header:
Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
Class: ELF64
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: DYN (Shared object file)
Machine: AMD x86-64
Version: 0x1
Entry point address: 0x1a80
Start of program headers: 64 (bytes into file)
Start of section headers: 24840 (bytes into file)
Flags: 0x0
Size of this header: 64 (bytes)
Size of program headers: 56 (bytes)
Number of program headers: 13
Size of section headers: 64 (bytes)
Number of section headers: 30
Section header string table index: 29
Aquí podemos ver que es un ELF64, little endian, de tipo DYN (Shared Object), para AMD x86-64. El Entry point address es crucial, ya que indica dónde comenzará la ejecución del programa. Las ubicaciones y tamaños de las tablas de cabeceras de programa y sección también son vitales para el cargador y el enlazador.
Viendo los Segmentos de Programa
Los segmentos describen cómo se cargará el programa en memoria. readelf -l muestra la tabla de cabeceras de programa.
readelf -l /bin/ls
Salida (extracto):
Elf file type is DYN (Shared object file)
Entry point 0x1a80
There are 13 program headers, starting at offset 64
Program Headers:
Type Offset VirtAddr PhysAddr
FileSiz MemSiz Flags Align
LOAD 0x0000000000000000 0x0000000000000000 0x0000000000000000
0x00000000000008fc 0x00000000000008fc R 0x1000
LOAD 0x0000000000001000 0x0000000000001000 0x0000000000001000
0x0000000000009664 0x0000000000009664 R E 0x1000
LOAD 0x000000000000b000 0x000000000000c000 0x000000000000c000
0x0000000000001f30 0x0000000000001f40 R W 0x1000
DYNAMIC 0x000000000000b0d8 0x000000000000c0d8 0x000000000000c0d8
0x00000000000001e0 0x00000000000001e0 R W 0x8
... (otras cabeceras de programa)
Cada línea LOAD indica una región de memoria que será cargada. Las Flags (R, W, E) indican permisos de lectura, escritura y ejecución. Es particularmente interesante buscar segmentos LOAD con permisos W (escritura), ya que podrían ser objetivos para corrupción de memoria o inyección de código.
Explorando las Secciones
Las secciones contienen los datos y el código organizados lógicamente. readelf -S nos muestra la tabla de secciones.
r eadelf -S /bin/ls
Salida (extracto):
There are 30 section headers, starting at offset 0x60d8:
Section Headers:
[Nr] Name Type Address Offset
Size EntSize Flags Link Info Align
[ 0] NULL 0000000000000000 00000000
0000000000000000 0000000000000000 0 0 0
[ 1] .note.gnu.build-id NOTE 0000000000000280 00000280
0000000000000024 0000000000000000 A 0 0 4
[ 2] .rela.plt RELA 0000000000000330 00000330
00000000000000c0 0000000000000018 I 7 17 8
[ 3] .init PROGBITS 0000000000001000 00001000
0000000000000017 0000000000000000 AX 0 0 4
[ 4] .plt PROGBITS 0000000000001020 00001020
0000000000000160 0000000000000010 AX 0 0 16
[ 5] .plt.got PROGBITS 0000000000001180 00001180
0000000000000008 0000000000000008 AX 0 0 8
[ 6] .text PROGBITS 0000000000001190 00001190
000000000000787e 0000000000000000 AX 0 0 16
... (otras secciones)
Secciones clave para el análisis:
.text: Contiene el código ejecutable del programa..rodata: Datos de solo lectura (cadenas de texto, constantes)..data: Datos inicializados (variables globales con valores iniciales)..bss: Datos no inicializados (variables globales que se inicializan a cero)..init,.fini: Código ejecutado al inicio y al final del programa, respectivamente..plt,.got: Tablas utilizadas para enlazar funciones dinámicamente. Cruciales para técnicas de ataque como GOT Hooking..dynsym,.symtab: Tablas de símbolos dinámicos y estáticos.
Listando Símbolos
Los símbolos son nombres de funciones y variables. readelf -s o readelf --symbols muestra la tabla de símbolos. Si el binario está stripped, muchos símbolos se habrán eliminado, dificultando la ingeniería inversa.
readelf -s /bin/ls
Salida (extracto):
Symbol table '.dynsym' contains 102 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __libc_start_main@GLIBC_2.34 (2)
2: 0000000000000000 0 NOTYPE WEAK DEFAULT UND __gmon_start__
3: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __cxa_finalize@GLIBC_2.2.5 (3)
4: 0000000000000000 0 FUNC GLOBAL DEFAULT UND readdir@GLIBC_2.34 (2)
5: 0000000000000000 0 FUNC GLOBAL DEFAULT UND free@GLIBC_2.34 (2)
... (más símbolos)
Los símbolos con UND (undefined) son funciones que el programa espera encontrar en una biblioteca externa. Los símbolos GLOBAL son exportados para que otros módulos puedan usarlos.
Tablas de Reubicación
Cuando un programa usa funciones de bibliotecas compartidas, necesita que el enlazador dinámico (Dynamic Linker) resuelva sus direcciones en tiempo de ejecución. Las tablas de reubicación (.rela.dyn, .rela.plt) le indican al enlazador dónde y cómo modificar el código o los datos para apuntar a las direcciones correctas.
readelf -r /bin/ls
Salida (extracto):
Relocation section '.rela.dyn' at offset 0x3d8 contains 22 entries:
Offset Info Type Sym. Value Sym. Name + Addend
000000000000e000 0000000000000008 R_X86_64_RELATIVE 0000000000000000
000000000000e2b0 0000000000000008 R_X86_64_RELATIVE 0000000000000000
... (más reubicaciones)
Relocation section '.rela.plt' at offset 0x330 contains 10 entries:
Offset Info Type Sym. Value Sym. Name + Addend
0000000000001028 0001000000000007 R_X86_64_JUMP_SLOT 0000000000000000 __libc_start_main@GLIBC_2.34 + 0
0000000000001030 0002000000000007 R_X86_64_JUMP_SLOT 0000000000000000 __gmon_start__ + 0
... (más reubicaciones)
La tabla .rela.plt es particularmente interesante para el hacking ético, ya que las entradas R_X86_64_JUMP_SLOT son los puntos que serán parcheados con las direcciones reales de las funciones de la biblioteca. Esto es explotado en técnicas como GOT hijacking.
3. objdump - El Desensamblador 💻
objdump es otra herramienta vital que puede mostrar información similar a readelf, pero su característica más potente es la capacidad de desensamblar el código ejecutable de las secciones.
Desensamblando Secciones
Para ver el código máquina en formato de assembly, usamos la opción -d o -D (para desensamblar todas las secciones ejecutables).
objdump -d /bin/ls | less
Salida (extracto de .text):
/bin/ls: file format elf64-x86-64
Disassembly of section .init:
0000000000001000 <_init>:
1000: f3 0f 1e fa endbr64
1004: 48 83 ec 08 sub $0x8,%rsp
1008: 48 8b 05 01 2f 00 00 mov 0x2f01(%rip),%rax # 4110 <__gmon_start__>
100f: 48 85 c0 test %rax,%rax
1012: 74 02 je 1016 <_init+0x16>
1014: e8 e7 ff ff ff call 1000 <__gmon_start__>
1019: 48 83 c4 08 add $0x8,%rsp
101d: c3 ret
Disassembly of section .plt:
0000000000001020 <.plt>:
1020: ff 35 1a 2f 00 00 pushq 0x2f1a(%rip) # 4040 <_GLOBAL_OFFSET_TABLE_+0x8>
1026: 25 00 00 00 00 and $0x0,%eax
102b: f2 ff 25 21 2f 00 00 bnd jmpq *0x2f21(%rip) # 4050 <_GLOBAL_OFFSET_TABLE_+0x10>
1032: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1)
0000000000001038 <__libc_start_main@plt>:
1038: f3 0f 1e fa endbr64
103c: ff 25 e2 2e 00 00 jmpq *0x2ee2(%rip) # 3f24 <__libc_start_main@GLIBC_2.34>
1042: 0f 1f 40 00 nopl 0x0(%rax)
...
Esto nos muestra el código máquina (en hexadecimal) y su traducción a assembly. Esencial para ingeniería inversa, análisis de vulnerabilidades como buffer overflows o format string bugs, y para entender la lógica de un programa cuando el código fuente no está disponible.
4. strings - Extrayendo Cadenas Legibles 📜
strings busca secuencias de caracteres imprimibles dentro de un fichero binario. A menudo revela mensajes de error, nombres de archivos, URLs, claves de API o cualquier texto que el programa utiliza. Es una forma rápida de obtener una pista sobre la funcionalidad del programa o información sensible.
strings /bin/ls | grep "Usage"
Salida (ejemplo):
Usage: %s [OPTION]... [FILE]...
Try '%s --help' for more information.
Esto es muy útil para identificar funciones o características ocultas, o incluso credenciales codificadas en binarios maliciosos.
💡 Casos de Uso en Hacking Ético
El análisis de ficheros ELF no es solo una curiosidad académica; es una habilidad fundamental con aplicaciones directas en el hacking ético y la ciberseguridad.
1. Ingeniería Inversa de Malware 🦠
Cuando se encuentra un binario malicioso (un troyano, un rootkit, etc.) sin su código fuente, el análisis ELF es el primer paso. Se utiliza para:
- Identificar funciones clave (
main,init,evil_function). - Detectar llamadas a API sospechosas (red, archivos, procesos).
- Extraer cadenas de texto relevantes (direcciones IP de C2, nombres de archivos, claves).
- Comprender la lógica del malware para desarrollar herramientas de detección o desinfección.
2. Búsqueda de Vulnerabilidades en Binarios (Binary Exploitation) 💥
Los exploits binarios se basan en encontrar fallos en el código o la configuración de un programa compilado. El análisis ELF ayuda a:
- Identificar secciones con permisos de escritura y ejecución: Una sección
W E(writable and executable) es un sueño para un atacante, ya que puede escribir código en ella y luego ejecutarlo. - Localizar la tabla GOT (Global Offset Table) y la PLT (Procedure Linkage Table): Manipular estas tablas permite redirigir llamadas a funciones de biblioteca a código arbitrario (GOT/PLT Hijacking).
- Detectar mitigaciones de seguridad: Verificar si un binario ha sido compilado con protecciones como ASLR (Address Space Layout Randomization), DEP/NX (Data Execution Prevention/No-Execute), PIE (Position Independent Executable), o Canaries. Herramientas como
checksec(no parte de Binutils, pero esencial) se basan en la información ELF para esto.
3. Análisis Forense Digital 🕵️♂️
En un entorno forense, un analista podría necesitar:
- Examinar binarios sospechosos encontrados en un sistema comprometido.
- Identificar si un programa ha sido modificado o empaquetado (
packed). - Extraer información de ficheros
corepara entender la causa de un fallo o crash que fue explotado.
🛡️ Protecciones y Mitigaciones ELF
Los desarrolladores y los compiladores implementan diversas protecciones para dificultar la explotación de vulnerabilidades en binarios ELF. Conocer estas mitigaciones es tan importante como saber cómo analizarlas.
| Mitigación | Descripción | Impacto en Hacking Ético | Cómo verificar con readelf/objdump/checksec |
|---|---|---|---|
| --- | --- | --- | --- |
| NX/DEP | Marca las secciones de datos como no ejecutables, evitando la ejecución de código en la pila o el heap. | Dificulta la inyección y ejecución directa de shellcode. | readelf -l (segmentos LOAD sin E en Flags para secciones de datos). Herramienta checksec. |
| ASLR | Aleatoriza las direcciones de memoria de las librerías, pila, heap y el ejecutable en cada ejecución. | Dificulta la predicción de direcciones para saltar a código malicioso. Requiere fuga de información. | No directamente visible en ELF, pero checksec lo infiere si PIE está habilitado. |
| --- | --- | --- | --- |
| PIE | Compila el ejecutable para que sea independiente de la posición (como las bibliotecas compartidas). Complementa ASLR para el binario principal. | El binario principal también se carga en direcciones aleatorias. | readelf -h (Type: DYN). Herramienta checksec. |
| Canaries | Pequeños valores secretos insertados en la pila antes de la dirección de retorno de una función. Si se sobrescriben, el programa termina. | Previene buffer overflows simples que sobrescriben la dirección de retorno. | No directamente en ELF, pero objdump -d puede mostrar instrucciones xor y comparaciones de canary. Herramienta checksec. |
| --- | --- | --- | --- |
| RELRO | (Relocation Read-Only) Hace que las tablas de reubicación (.got, .plt) sean de solo lectura después de que el enlazador dinámico las ha resuelto. | Dificulta o impide el GOT/PLT Hijacking. | readelf -l (segmento GNU_RELRO). Herramienta checksec. |
¿Qué es `checksec`?
`checksec` es un script de shell (a menudo incluido en distribuciones como Kali Linux) que automatiza la inspección de binarios ELF para verificar qué mitigaciones de seguridad están habilitadas. Hace uso de `readelf` y `objdump` internamente para extraer la información relevante y presentarla de forma legible. Es una herramienta indispensable en el *binary exploitation*.Verificando Mitigaciones con checksec (ejemplo)
checksec --file=/bin/ls
Salida (ejemplo):
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
RPATH: No RPATH
RUNPATH: No RUNPATH
Symbols: No Symbols
Esta salida nos indica que /bin/ls tiene Full RELRO, Canary found, NX enabled y PIE enabled, lo que lo convierte en un binario relativamente seguro contra muchos ataques básicos. El No Symbols indica que está stripped.
🚀 Ejercicio Práctico: Analizando un Binario Simple
Vamos a poner en práctica lo aprendido con un pequeño programa en C. Crea un archivo llamado vuln.c con el siguiente contenido:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void greet(char *name) {
char buffer[64];
strcpy(buffer, name);
printf("Hello, %s!\n", buffer);
}
int main(int argc, char **argv) {
if (argc < 2) {
printf("Usage: %s <name>\n", argv[0]);
exit(1);
}
greet(argv[1]);
return 0;
}
Compílalo sin mitigaciones de seguridad (esto es crucial para el propósito de aprendizaje):
gcc -fno-stack-protector -z execstack -no-pie -o vuln vuln.c
Ahora, analicemos este binario vuln:
- Tipo de fichero:
file ./vuln
*Esperarás ver un `ELF 64-bit LSB executable, x86-64, ... not stripped`.* Nótese que es `executable`, no `shared object`, y `not stripped`.
2. Cabecera ELF:
readelf -h ./vuln
*El `Entry point address` será importante. `Type: EXEC`.* Fíjate que al no usar `-no-pie` el tipo de binario sería `DYN`.
3. Cabeceras de programa y permisos:
readelf -l ./vuln
*Busca un segmento `LOAD` con permisos `RWE` (Read, Write, Execute). Esto indica una sección ejecutable y escribible, un riesgo significativo.* Observa cómo la pila (stack) también podría ser `RWE` debido a `-z execstack`.
4. Secciones:
readelf -S ./vuln
*Localiza `.text`, `.data`, `.bss`. Identifica los tamaños y ubicaciones.*
5. Símbolos:
readelf -s ./vuln
*Deberías ver `greet`, `main`, `strcpy`, `printf`. La presencia de `greet` y `strcpy` es clave.* Si hubiéramos compilado con `-s` (`strip`), estos símbolos no estarían.
6. Desensamblado de la función greet:
objdump -d ./vuln | grep -A 20 "<greet>"
*Inspecciona el código assembly. Fíjate en la llamada a `strcpy` y cómo maneja `buffer`. Un `strcpy` a un buffer de tamaño fijo es una receta para un *buffer overflow* si la entrada es demasiado grande.* Identifica el tamaño del buffer y cómo se manipula el *stack pointer* (`rsp`).
7. Verificación de mitigaciones:
checksec --file=./vuln
*Aquí deberías ver `No RELRO`, `No Canary found`, `NX disabled`, `No PIE`. Esto confirma que hemos compilado un binario vulnerable para nuestro estudio.* La ausencia de estas mitigaciones hace que el binario sea mucho más fácil de explotar.
Este pequeño ejercicio muestra cómo, al combinar las herramientas, podemos obtener una imagen clara de la arquitectura de un binario, sus debilidades y las protecciones que se le han aplicado (o no).
✨ Conclusión y Próximos Pasos
El análisis de ficheros ELF es una piedra angular en el campo del hacking ético y la ciberseguridad. Al dominar herramientas como file, readelf, objdump y strings, obtienes la capacidad de mirar más allá de la superficie de un programa, desentrañar su funcionamiento interno y descubrir vulnerabilidades ocultas. Esta habilidad es indispensable para la ingeniería inversa, el desarrollo de exploits, el análisis de malware y la auditoría de seguridad de sistemas Linux.
Continúa practicando con diferentes binarios, experimenta con las opciones de las herramientas y profundiza en la arquitectura de los sistemas operativos y la compilación de programas. Cuanto mejor entiendas cómo se construye un programa, mejor podrás identificar sus puntos débiles y protegerlo.
¡Sigue explorando el fascinante mundo de la ciberseguridad!
Tutoriales relacionados
- Ataques de Fuerza Bruta y Diccionario: Defiéndete de la Fuerza Cruda 💥intermediate15 min
- Desmitificando el Phishing: Prevención, Detección y Contramedidas Avanzadasintermediate10 min
- Escaneo de Vulnerabilidades con Nessus: Una Guía Práctica para Hacking Éticointermediate25 min
- Anonimato y Privacidad en Hacking Ético: Navegación Segura y Ocultamiento de Huella Digital 👻intermediate18 min
- Configuración y Hardening de Servidores Web Apache/Nginx: Fortificando tu Fortaleza Digital 🏰intermediate20 min
Comentarios (0)
Aún no hay comentarios. ¡Sé el primero!