
Un aplanador de archivos ELF independiente de la arquitectura para shellcode
elfloader es un cargador extremadamente simple para archivos ELF que genera una representación plana en memoria del ELF.
Combínalo con Rust y ahora puedes escribir tu shellcode en un lenguaje adecuado, seguro y de alto nivel. Se puede usar cualquier objetivo que LLVM pueda apuntar, incluyendo especificaciones de objetivo personalizadas para plataformas y ABI realmente exóticas. Disfruta usando cosas como u64 en sistemas de 32 bits, arreglos con verificación de límites, manejo de drop de asignaciones, etc. :)
Simplemente concatena todas las secciones LOAD juntas, usando relleno de ceros si hay espacios, en un solo archivo plano grande.
Este archivo incluye la inicialización a cero de las secciones .bss, y por lo tanto se puede usar directamente como un payload de shellcode.
Si no quieres perder tiempo con scripts de enlace que fallan abiertamente, probablemente esta sea una excelente manera de hacerlo.
Esto no maneja reubicaciones, depende de ti asegurarte de que el ELF original esté basado en la dirección en la que deseas que esté.
Para usar esta herramienta, simplemente:
Usage: elfloader [--perms] [--binary] [--base=<addr>] <input ELF> <output>
--binary - Don't output a FELF, output the raw loaded image with no
metadata
--perms - Create a FELF0002 which includes permission data, overrides
--binary
--base=<addr> - Force the output to start at `<addr>`, zero padding from
the base to the start of the first LOAD segment if needed.
`<addr>` is default hex, can be overrided with `0d`, `0b`,
`0x`, or `0o` prefixes.
Warning: This does not _relocate_ to base, it simply starts
the output at `<addr>` (adding zero bytes such that the
output image can be loaded at `<addr>` instead of the
original ELF base)
<input ELF> - Path to input ELF
<output> - Path to output file
Para instalar esta herramienta ejecuta:
cargo install --path .
¡Ahora puedes usar elfloader desde cualquier lugar de tu terminal!
Este proyecto fue desarrollado en vivo aquí:
Hay un ejemplo en example_small_program, simplemente ejecuta make o nmake
y esto debería generar un example.bin de 8 bytes.
pleb@gamey ~/elfloader/example_small_program $ make
cargo build --release
Finished release [optimized] target(s) in 0.03s
elfloader --binary target/aarch64-unknown-none/release/example_small_program example.bin
pleb@gamey ~/elfloader/example_small_program $ ls -l ./example.bin
-rw-r--r-- 1 pleb pleb 8 Nov 8 12:27 ./example.bin
pleb@gamey ~/elfloader/example_small_program $ objdump -d target/aarch64-unknown-none/release/example_small_program
target/aarch64-unknown-none/release/example_small_program: file format elf64-littleaarch64
Disassembly of section .text:
00000000133700b0 <_start>:
133700b0: 8b000020 add x0, x1, x0
133700b4: d65f03c0 ret
Ahora puedes escribir tu shellcode en Rust, y no tienes que preocuparte por
si emites .data, .rodata, .bss, etc. ¡Esto lo manejará todo por ti!
También hay un ejemplo con .bss y .rodata
pleb@gamey ~/elfloader/example_program_with_data $ make
cargo build --release
Finished release [optimized] target(s) in 0.04s
elfloader --binary target/aarch64-unknown-none/release/example_program_with_data example.bin
pleb@gamey ~/elfloader/example_program_with_data $ ls -l ./example.bin
-rw-r--r-- 1 pleb pleb 29 Nov 8 12:39 ./example.bin
pleb@gamey ~/elfloader/example_program_with_data $ objdump -d target/aarch64-unknown-none/release/example_program_with_data
target/aarch64-unknown-none/release/example_program_with_data: file format elf64-littleaarch64
Disassembly of section .text:
0000000013370124 <_start>:
13370124: 90000000 adrp x0, 13370000 <_start-0x124>
13370128: 90000008 adrp x8, 13370000 <_start-0x124>
1337012c: 52800029 mov w9, #0x1 // #1
13370130: 91048000 add x0, x0, #0x120
13370134: 3904f109 strb w9, [x8, #316]
13370138: d65f03c0 ret
pleb@gamey ~/elfloader/example_program_with_data $ readelf -l target/aarch64-unknown-none/release/example_program_with_data
Elf file type is EXEC (Executable file)
Entry point 0x13370124
There are 4 program headers, starting at offset 64
Program Headers:
Type Offset VirtAddr PhysAddr
FileSiz MemSiz Flags Align
LOAD 0x0000000000000120 0x0000000013370120 0x0000000013370120
0x0000000000000004 0x0000000000000004 R 0x1
LOAD 0x0000000000000124 0x0000000013370124 0x0000000013370124
0x0000000000000018 0x0000000000000018 R E 0x4
LOAD 0x000000000000013c 0x000000001337013c 0x000000001337013c
0x0000000000000000 0x0000000000000001 RW 0x4
GNU_STACK 0x0000000000000000 0x0000000000000000 0x0000000000000000
0x0000000000000000 0x0000000000000000 RW 0x0
Section to Segment mapping:
Segment Sections...
00 .rodata
01 .text
02 .bss
03
Esta herramienta no se preocupa por nada excepto por las secciones LOAD. Determina
el endianness (little vs big) y el bitness (32 vs 64) desde el encabezado ELF,
y a partir de ahí crea una imagen plana basada en las direcciones virtuales del encabezado del programa (donde se carga), el tamaño del archivo (número de bytes inicializados) y el tamaño de la memoria (tamaño real de la región de memoria). Los bytes se inicializan desde el archivo según el desplazamiento y el tamaño del archivo, y luego se extienden con ceros hasta el tamaño de la memoria (o se truncan si el tamaño de la memoria es menor que el tamaño del archivo).
Estas secciones LOAD luego se concatenan junto con relleno de ceros para los espacios.
Está diseñado para ser increíblemente simple y agnóstico a la entrada ELF. Podría ser un ejecutable, un archivo objeto, un objeto compartido, un volcado de núcleo, etc., realmente no importa. Simplemente te dará la representación plana de la memoria, nada más.
Esto te permite convertir cualquier ELF en shellcode, o en un formato de archivo más simple que es más fácil de cargar en áreas de difícil acceso, como dispositivos embebidos. Personalmente, desarrollé esto para mi cargador MIPS NT 4.0 que me permite ejecutar código Rust.
Esta herramienta por defecto genera un formato de archivo FELF. Esto es un Falk ELF. Es un formato de archivo simple:
FELF0001 - Cabecera mágica
entry - Entero little-endian de 64 bits de la dirección del punto de entrada
base - Entero little-endian de 64 bits de la dirección base para cargar la imagen
<image> - El resto del archivo es la imagen sin procesar, para cargar en `base` y saltar
a `entry`
Esta herramienta por defecto genera un formato de archivo FELF. Esto es un Falk ELF. Es un formato de archivo simple con permisos:
FELF0002 - Cabecera mágica
entry - Entero little-endian de 64 bits de la dirección del punto de entrada
base - Entero little-endian de 64 bits de la dirección base para cargar la imagen
<image> - El resto del archivo es la imagen sin procesar, para cargar en `base` y saltar
a `entry`
<perms> - Permisos, que coinciden con los bytes de <image> donde el byte contiene
las siguientes banderas combinadas con OR bit a bit:
0x01 - Ejecutable, 0x02 - Escribible, 0x04 - Legible
Los bytes de relleno serán 0x00 y, por lo tanto, no tendrán permisos para ningún
acceso