
Kit de encaixe de seção binária ELF para entrega de payload sem estágio, permitindo anexo de payload em campo, evasão de assinatura e resistência a carregamento estático/dinâmico por meio de manipulação personalizada de seção ELF.
.data.payload.h:
const data[3432] = {
0x43, 0x28, 0x41, 0x11, 0xa3, 0xff,
...
0x00, 0xff, 0x23
};
Feito manualmente ou com ferramentas como bin2c ou xxd -i payload.bin > payload.h com inclusão de cabeçalho adicional.
Armazenar payloads em .text e .data de forma genérica também é uma má ideia devido à facilidade de rastreamento de carregamento e introspecção de semântica comportamental do carregamento de dados para execução.
__attribute__. Isso é um pouco melhor, mas ainda bem rastreável devido à forma como o ELF é criado e carregado.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);
}
Uma diretiva do tipo .incbin dependente do montador pode criar uma seção e incorporar um payload.
Ex: gcc -c payload.s ou 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
Com recuperação posterior no carregador como:
int main(void) {
extern uint8_t payload_start;
uint8_t *ptrPayload = &payload_start;
...
}
Nota: Podemos incluir um ELF totalmente funcional no payload.bin, o que é importante quando se trata de criar binários "gordurosos", contendo elementos de vários kits de ferramentas.
Nota: Existem ferramentas mais ergonômicas para realizar a tarefa, como INCBIN de @graphitemaster [link]
Uma variação do tema é o ASM inline como:
/* 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
};
Nota: Observe PROGBITS na definição de uma seção, isso será importante.
A inclusão binária de payload baseada em compilador/link ed não é ideal
Há concessões:

O tipo de seção e os sinalizadores definidos na nova seção que a contém determinam se o carregador do SO a carrega na memória ao iniciar o executável. Algumas seções são carregadas automaticamente por padrão, outras não (por exemplo, .symtab, .strtab)
Do ponto de vista ofensivo, quais eficiências podemos obter disso?
Podemos evitar definir sinalizadores em seções que assumem carregamento padrão na memória.
Podemos usar um tipo diferente de seção que não carrega na memória.
Um fornecedor ou engenheiro de sistema pode precisar marcar um arquivo objeto com informações especiais que outros programas possam verificar para conformidade ou compatibilidade. Seções do tipo
SHT_NOTEe elementos de cabeçalho de programa do tipoPT_NOTEpodem ser usados para esse fim.
No último caso, podemos ver o uso deste tipo de seção em binários do sistema:
$ readelf --sections /bin/tar | grep NOTE
[ 2] .note.gnu.bu[...] NOTE 00000000000002c4 000002c4
[ 3] .note.ABI-tag NOTE 00000000000002e8 000002e8
E podemos inspecionar seu conteúdo:
$ readelf -p .note.ABI-tag /bin/tar
String dump of section '.note.ABI-tag':
[ c] GNU
O resultado final da criação de uma seção SHT_NOTE terá esta aparência no ELF:

Bônus: SHT_NOTE nos dá estrutura se precisarmos usá-la (e usaremos mais adiante):

Até agora fomos capazes de criar uma seção, evitando que seja carregada na memória pelo carregador do SO. A seção está efetivamente dormente na imagem ELF no momento. Discutiremos como carregá-la um pouco mais tarde. No entanto, uma questão mais urgente é o fato de que ainda estamos operando no nível do compilador e do vinculador, e a seção é um objeto que é entrelaçado na estrutura do ELF final, criando relacionamentos e endereços de memória a partir do código do carregador que referencia seu conteúdo.

E se fôssemos capazes de criar uma seção ELF com payload incorporado fora do fluxo de trabalho de compilação do carregador, e anexar essa seção posteriormente ao binário do carregador?