
Uno strumento di appiattimento di file ELF indipendente dall'architettura per shellcode
elfloader è un caricatore estremamente semplice per file ELF che genera una rappresentazione piatta in memoria dell'ELF.
Abbinalo a Rust e ora puoi scrivere il tuo shellcode in un linguaggio di alto livello, sicuro e appropriato. Qualsiasi destinazione supportata da LLVM può essere utilizzata, incluse specifiche di destinazione personalizzate per piattaforme e ABI davvero esotiche. Goditi l'uso di cose come u64 su sistemi a 32 bit, array con controllo dei limiti, gestione della drop per le allocazioni, ecc. :)
Concatena semplicemente tutte le sezioni LOAD insieme, usando zero-padding se ci sono spazi vuoti, in un unico grande file piatto.
Questo file include l'inizializzazione a zero delle sezioni .bss, e quindi può essere utilizzato direttamente come payload shellcode.
Se non vuoi perdere tempo con linker script aperti al fallimento, probabilmente questa è un'ottima strada da seguire.
Questo non gestisce alcuna rilocazione; sta a te assicurarti che l'ELF originale sia basato all'indirizzo desiderato.
Per usare questo strumento, basta:
Usage: elfloader [--perms] [--binary] [--base=<addr>] <input ELF> <output>
--binary - Non produrre un FELF, ma l'immagine caricata grezza senza
metadati
--perms - Crea un FELF0002 che include i dati dei permessi, sovrascrive
--binary
--base=<addr> - Forza l'output ad iniziare a `<addr>`, con zero padding dalla
base all'inizio del primo segmento LOAD se necessario.
`<addr>` è esadecimale di default, può essere sovrascritto con
prefissi `0d`, `0b`, `0x` o `0o`.
Attenzione: Questo non _riloca_ alla base, semplicemente inizia
l'output a `<addr>` (aggiungendo byte zero in modo che
l'immagine di output possa essere caricata a `<addr>` invece
che alla base ELF originale)
<input ELF> - Percorso all'ELF di input
<output> - Percorso al file di output
Per installare questo strumento esegui:
cargo install --path .
Ora puoi usare elfloader da qualsiasi punto della tua shell!
Questo progetto è stato sviluppato dal vivo qui:
https://www.youtube.com/watch?v=x0V-CEmXQCQ
C'è un esempio in example_small_program, basta eseguire make o nmake
e questo dovrebbe generare un example.bin di 8 byte.
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
Ora puoi scrivere il tuo shellcode in Rust, e non devi preoccuparti se emetti
.data, .rodata, .bss, ecc. Questo gestirà tutto per te!
C'è anche un esempio con .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
Questo strumento non si preoccupa di nulla tranne che delle sezioni LOAD. Determina
l'endianness (little vs big) e il bitness (32 vs 64) dall'intestazione ELF,
e da lì crea un'immagine piatta basata sugli indirizzi virtuali dei program header
(dove viene caricato), la dimensione del file (numero di byte inizializzati) e la
dimensione della memoria (dimensione effettiva della regione di memoria). I byte
sono inizializzati dal file in base all'offset e alla dimensione del file, e poi
estesi con zeri fino alla dimensione della memoria (o troncati se la dimensione
della memoria è minore della dimensione del file).
Queste sezioni LOAD vengono quindi concatenate insieme con riempimento di byte
zero per gli spazi vuoti.
È progettato per essere incredibilmente semplice e agnostico rispetto all'ELF di input. Potrebbe essere un eseguibile, un file oggetto, un oggetto condiviso, un core dump, ecc., non importa. Ti darà semplicemente la rappresentazione piatta della memoria, niente di più.
Questo ti permette di trasformare qualsiasi ELF in shellcode, o in un formato di file più semplice da caricare in aree difficili da raggiungere, come dispositivi embedded. Personalmente, l'ho sviluppato per il mio loader MIPS NT 4.0 che mi permette di eseguire codice Rust.
Questo strumento di default genera un formato di file FELF. Questo è un Falk ELF. È un formato di file semplice:
FELF0001 - Intestazione magica
entry - Intero a 64 bit little endian dell'indirizzo del punto di ingresso
base - Intero a 64 bit little endian dell'indirizzo base per caricare l'immagine
<image> - Il resto del file è l'immagine grezza, da caricare a `base` e saltare
dentro a `entry`
Questo strumento di default genera un formato di file FELF. Questo è un Falk ELF. È un formato di file semplice con permessi:
FELF0002 - Intestazione magica
entry - Intero a 64 bit little endian dell'indirizzo del punto di ingresso
base - Intero a 64 bit little endian dell'indirizzo base per caricare l'immagine
<image> - Il resto del file è l'immagine grezza, da caricare a `base` e saltare
dentro a `entry`
<perms> - Permessi, che corrispondono ai byte di <image> dove il byte contiene
i seguenti flag combinati con OR bit a bit:
0x01 - Eseguibile, 0x02 - Scrivibile, 0x04 - Leggibile
I byte di padding saranno 0x00, e quindi non avranno permessi per
alcun accesso