
Um achatador de arquivos ELF agnóstico de arquitetura para shellcode
elfloader é um carregador super simples para arquivos ELF que gera uma representação plana na memória do ELF.
Combine isso com Rust e agora você pode escrever seu shellcode em uma linguagem adequada, segura e de alto nível. Qualquer alvo que o LLVM possa atingir pode ser usado, incluindo especificações de alvo personalizadas para plataformas e ABIs realmente exóticas. Aproveite o uso de coisas como u64s em sistemas de 32 bits, arrays com verificação de limites, tratamento de drop de alocações, etc :)
Ele simplesmente concatena todas as seções LOAD juntas, usando preenchimento com zeros se houver lacunas, em um único arquivo plano grande.
Este arquivo inclui inicialização zero das seções .bss e, portanto, pode ser usado diretamente como um payload de shellcode.
Se você não quer perder tempo com scripts de vinculação que falham abertamente, esta é provavelmente uma ótima maneira.
Isso não lida com nenhuma realocação, é sua responsabilidade garantir que o ELF original esteja baseado no endereço que você deseja.
Para usar esta ferramenta, simplesmente:
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 ferramenta execute:
cargo install --path .
Agora você pode usar elfloader de qualquer lugar no seu shell!
Este projeto foi desenvolvido ao vivo aqui:
https://www.youtube.com/watch?v=x0V-CEmXQCQ
Há um exemplo em example_small_program, simplesmente execute make ou nmake e isso deve gerar um 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
Agora você pode escrever seu shellcode em Rust, e não precisa se preocupar se você emite .data, .rodata, .bss, etc. Isso vai lidar com tudo para você!
Há também um exemplo com .bss e .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 ferramenta não se importa com nada além das seções LOAD. Ela determina a endianness (little vs big) e a largura de bits (32 vs 64) a partir do cabeçalho ELF, e a partir daí cria uma imagem plana baseada nos endereços virtuais do cabeçalho do programa (onde é carregado), tamanho do arquivo (número de bytes inicializados) e tamanho da memória (tamanho da região real de memória). Os bytes são inicializados a partir do arquivo com base no offset e tamanho do arquivo, e então isso é estendido com zeros até o tamanho da memória (ou truncado se o tamanho da memória for menor que o tamanho do arquivo).
Essas seções LOAD são então concatenadas com preenchimento de bytes zero para lacunas.
Isso é projetado para ser incrivelmente simples e agnóstico em relação à entrada ELF. Pode ser um executável, arquivo objeto, objeto compartilhado, core dump, etc, não importa. Ele simplesmente fornecerá a representação plana da memória, nada mais.
Isso permite que você transforme qualquer ELF em shellcode, ou em um formato de arquivo mais simples que é mais fácil de carregar em áreas de difícil acesso, como dispositivos embarcados. Pessoalmente, desenvolvi isso para meu carregador MIPS NT 4.0, que me permite executar código Rust.
Esta ferramenta por padrão gera um formato de arquivo FELF. Este é um Falk ELF. Este é um formato de arquivo simples:
FELF0001 - Magic header
entry - 64-bit little endian integer of the entry point address
base - 64-bit little endian integer of the base address to load the image
<image> - Rest of the file is the raw image, to be loaded at `base` and jumped
into at `entry`
Esta ferramenta por padrão gera um formato de arquivo FELF. Este é um Falk ELF. Este é um formato de arquivo simples com permissões:
FELF0002 - Magic header
entry - 64-bit little endian integer of the entry point address
base - 64-bit little endian integer of the base address to load the image
<image> - Rest of the file is the raw image, to be loaded at `base` and jumped
into at `entry`
<perms> - Permissions, matching the bytes of <image> where the byte contains
the following flags bitwise or-ed together:
0x01 - Executable, 0x02 - Writable, 0x04 - Readable
Padding bytes will be 0x00, and thus have no permissions for any
access