Набор инструментов для стыковки секций ELF-бинарных файлов для доставки бесстадийных полезных нагрузок, обеспечивающий присоединение полезной нагрузки в полевых условиях, обход сигнатур и устойчивость к статической/динамической загрузке с помощью манипуляции пользовательскими секциями ELF.
.data.payload.h:
const data[3432] = {
0x43, 0x28, 0x41, 0x11, 0xa3, 0xff,
...
0x00, 0xff, 0x23
};
Достигается вручную или с помощью инструментов, таких как bin2c или xxd -i payload.bin > payload.h, с дальнейшим подключением заголовочного файла.
Хранение полезных нагрузок в .text и .data в общем виде также является плохой идеей из-за простоты отслеживания загрузки и анализа семантики поведения загружаемых данных для выполнения.
__attribute__. Это немного лучше, но всё ещё хорошо отслеживается из-за того, как ELF создаётся и загружается.char stack[10000] __attribute__ ((section ("binstack"))) = {
0x43, 0x28, 0x41, 0x11, 0xa3, 0xff,
...
0x00, 0xff, 0x23 };
int init_data __attribute__ ((section ("bindata"))) = 0;
main()
{
/* Initialize stack pointer */
init_sp (stack + sizeof (stack));
/* Initialize initialized data */
memcpy (&init_data, &data, &edata - &data);
}
Директива, аналогичная .incbin, зависящая от ассемблера, может создать секцию и встроить полезную нагрузку. Пример: gcc -c payload.s или ld -r -b payload.bin -o payload.o
.section .bindata
.global payload_start
.type payload_start, @object
.section .binddata
.balign 64
payload_start:
.incbin "payload.bin"
.balign 1
payload_end:
.byte 0
С последующим извлечением в загрузчике как:
int main(void) {
extern uint8_t payload_start;
uint8_t *ptrPayload = &payload_start;
...
}
Примечание: Мы можем включить полнофункциональный ELF в payload.bin, что важно при создании «жирных» бинарников, содержащих элементы нескольких тулкитов.
Примечание: Существуют более эргономичные инструменты для выполнения этой задачи, например INCBIN от @graphitemaster [link]
Вариация на тему — встроенный ассемблер, например так:
/* Raw image data for all embedded images */
#undef EMBED
#define EMBED( _index, _path, _name ) \
extern char embedded_image_ ## _index ## _data[]; \
extern char embedded_image_ ## _index ## _len[]; \
__asm__ ( ".section \".rodata\", \"a\", " PROGBITS "\n\t" \
"\nembedded_image_" #_index "_data:\n\t" \
".incbin \"" _path "\"\n\t" \
"\nembedded_image_" #_index "_end:\n\t" \
".equ embedded_image_" #_index "_len, " \
"( embedded_image_" #_index "_end - " \
" embedded_image_" #_index "_data )\n\t" \
".previous\n\t" );
EMBED_ALL
/* Image structures for all embedded images */
#undef EMBED
#define EMBED( _index, _path, _name ) { \
.refcnt = REF_INIT ( ref_no_free ), \
.name = _name, \
.data = ( userptr_t ) ( embedded_image_ ## _index ## _data ), \
.len = ( size_t ) embedded_image_ ## _index ## _len, \
},
static struct image embedded_images[] = {
EMBED_ALL
};
Примечание: Обратите внимание на PROGBITS в определении секции, это будет важно.
Включение бинарника полезной нагрузки через компилятор/компоновщик не идеально
Есть компромиссы:

Тип секции и флаги, установленные на новой секции, определяют, будет ли загрузчик ОС загружать её в память при запуске исполняемого файла. Некоторые секции загружаются автоматически по умолчанию, другие — нет (например, .symtab, .strtab)
С точки зрения атаки, какую эффективность мы можем из этого извлечь?
Мы можем избегать установки флагов на секции, которые предполагают загрузку в память по умолчанию.
Мы можем использовать другой тип секции, который не загружается в память.
Поставщику или системному инженеру может потребоваться пометить объектный файл специальной информацией, которую другие программы могут проверить на соответствие или совместимость. Секции типа
SHT_NOTEи элементы программного заголовка типаPT_NOTEмогут использоваться для этой цели.
В последнем случае мы можем увидеть использование этого типа секции в системных бинарниках:
$ readelf --sections /bin/tar | grep NOTE
[ 2] .note.gnu.bu[...] NOTE 00000000000002c4 000002c4
[ 3] .note.ABI-tag NOTE 00000000000002e8 000002e8
И можем проверить их содержимое:
$ readelf -p .note.ABI-tag /bin/tar
String dump of section '.note.ABI-tag':
[ c] GNU
Конечный результат создания секции SHT_NOTE в ELF будет выглядеть так:

Бонус: SHT_NOTE предоставляет нам структуру, если мы захотим её использовать (а мы будем использовать её далее):

До сих пор мы могли создать секцию и избежать её загрузки в память загрузчиком ОС. Секция фактически находится в спящем состоянии в образе ELF. Мы обсудим, как её загружать, немного позже. Однако более насущный вопрос заключается в том, что мы всё ещё работаем на уровне компилятора и компоновщика, и секция — это объект, который вплетается в структуру конечного ELF, создавая связи и адреса памяти из кода загрузчика, который ссылается на её содержимое.

Что, если бы мы могли создать секцию ELF со встроенной полезной нагрузкой вне процесса компиляции загрузчика и присоединить эту секцию позже к бинарнику загрузчика?