
Ein architekturunabhängiger ELF-Datei-Flattener für Shellcode
elfloader ist ein extrem einfacher Lader für ELF-Dateien, der eine flache In-Memory-Repräsentation des ELF erzeugt.
Kombiniere dies mit Rust und du kannst deinen Shellcode in einer ordentlichen, sicheren, hochsprachlichen Sprache schreiben. Jedes Ziel, das LLVM ansprechen kann, kann verwendet werden, einschließlich benutzerdefinierter Zielspezifikationen für wirklich exotische Plattformen und ABIs. Genieße die Verwendung von Dingen wie u64s auf 32-Bit-Systemen, grenzgeprüften Arrays, Drop-Handling von Allokationen, usw :)
Es verkettet einfach alle LOAD-Segmente miteinander, unter Verwendung von Null-Padding, falls Lücken vorhanden sind, zu einer großen flachen Datei.
Diese Datei enthält die Null-Initialisierung von .bss-Segmenten und kann daher direkt als Shellcode-Payload verwendet werden.
Wenn du keine Zeit mit fehleranfälligen Linker-Skripten verschwenden möchtest, ist dies wahrscheinlich ein guter Weg.
Dies behandelt keine Relokationen; es liegt an dir, sicherzustellen, dass das ursprüngliche ELF an der gewünschten Adresse basiert.
Um dieses Tool zu verwenden, einfach:
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
Um dieses Tool zu installieren, führe aus:
cargo install --path .
Jetzt kannst du elfloader von überall in deiner Shell verwenden!
Dieses Projekt wurde live hier entwickelt:
https://www.youtube.com/watch?v=x0V-CEmXQCQ
Es gibt ein Beispiel in example_small_program, führe einfach make oder nmake aus und dies sollte eine example.bin erzeugen, die 8 Bytes groß ist.
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
Jetzt kannst du deinen Shellcode in Rust schreiben und musst dir keine Sorgen machen, ob du .data, .rodata, .bss usw. ausgibst. Dies erledigt alles für dich!
Es gibt auch ein Beispiel mit .bss und .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
Dieses Tool kümmert sich um nichts außer um LOAD-Segmente. Es ermittelt die Endianness (Little vs. Big) und Bitness (32 vs. 64) aus dem ELF-Header und erstellt daraus ein flaches Abbild basierend auf den virtuellen Adressen des Programm-Headers (wo es geladen wird), der Dateigröße (Anzahl initialisierter Bytes) und der Mem-Größe (Größe des tatsächlichen Speicherbereichs). Die Bytes werden aus der Datei basierend auf Offset und Dateigröße initialisiert und dann mit Nullen bis zur Mem-Größe erweitert (oder abgeschnitten, wenn die Mem-Größe kleiner als die Dateigröße ist).
Diese LOAD-Segmente werden dann mit Null-Byte-Padding für Lücken miteinander verkettet.
Dies ist so konzipiert, dass es extrem einfach und unabhängig von der ELF-Eingabe ist. Es könnte eine ausführbare Datei, eine Objektdatei, ein Shared Object, ein Core Dump usw. sein, es kümmert sich nicht wirklich. Es gibt dir einfach die flache Darstellung des Speichers, nichts weiter.
Dies erlaubt es dir, jedes ELF in Shellcode oder ein einfacheres Dateiformat zu verwandeln, das leichter in schwer zugänglichen Bereichen wie eingebetteten Geräten geladen werden kann. Persönlich habe ich dies für meinen MIPS NT 4.0 Loader entwickelt, der es mir erlaubt, Rust-Code auszuführen.
Dieses Tool erzeugt standardmäßig ein FELF-Dateiformat. Dies ist ein Falk-ELF. Dies ist ein einfaches Dateiformat:
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`
Dieses Tool erzeugt standardmäßig ein FELF-Dateiformat. Dies ist ein Falk-ELF. Dies ist ein einfaches Dateiformat mit Berechtigungen:
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