Tutoriales

Rust BOF: desbloqueando nuevo potencial en Cobalt Strike

Esto me tomó alrededor de 4 días (+2 días para actualizar), pero lo hice funcionar… núcleo de óxido + distribución para Cobalt Strike BOF.
Esto es en gran medida una prueba de concepto, pero me encantaría ver que otros lo usen y contribuyan.

Tabla de Contenidos

edificio

  1. Instalar mingw
  2. Instale Nightly Rust usando las cadenas de herramientas x86_64-pc-windows-gnu e i686-pc-windows-gnu
  3. correr cargo install cargo-make
  4. correr cargo make
  5. ????
  6. Beaufit

Hazlo tu mismo

Edite la función de entrada en rustbof/src/lib.rs. Puede utilizar el siguiente comando para agregar parámetros bof_pack Las funciones están en el script del atacante, simplemente no cambie las dos primeras ya que son reubicaciones.

¿Qué tal?

Creo que me gustaría escribir una entrada de blog sobre esto en algún momento, pero por ahora, este es el proceso:

  • ¿Cómo compilar un archivo objeto desde Rust?
    • Rustc tiene uno --emit=obj El indicador solo enviará el archivo de destino a la carpeta deps del destino.
  • BOF es un soltero Archivos objeto, no muchos. Rust compila cada componente en su propio archivo .o. ¿Cómo los combino?
    • ld tiene una característica llamada «reubicable» (-i) para enlaces incrementales
  • El cargador Cobalt Strike lanza NullPointerException beacon_inline_execute. ¿Por qué?
    • Un poco de descompilación y depuración de códigos de bytes me llevaron a descubrir que el analizador CS COFF estaba bloqueado por ciertos símbolos en el objeto. Resulta que estos símbolos sólo se incluyen como información de depuración (documentación).
    • En mi investigación encontré OBJExecutable y OBJParser ¡Las clases en cobaltstrike.jar tienen la función principal de obtener la ruta al archivo de destino e imprimir un montón de información útil!
  • ¿Cómo eliminar símbolos no deseados en el archivo de destino?
    • Excelentemente, strip ¡trabajar! strip en realidad hay uno --strip-uneeded Quite las banderas de todo lo que no sea necesario para la reubicación, como la información de depuración.
  • El cargador Cobalt Strike BOF ahora se queja de algunos símbolos indefinidos, p. rust_oom y __rust_alloc.
    • En este punto no estoy usando alloc en absoluto, pero todavía se está compilando y luego agregando al BOF a través de: ld -i. ¿Cómo puedo deshacerme de estos símbolos?
      • Al principio, simplemente eliminé el archivo de destino de asignación porque no lo estaba usando. Simple.
      • Creo que en algún momento descubrí --gc-sections Bandera para ld que le permite definir símbolos raíz mediante -u bandera, luego elimina cualquier símbolo al que nunca se haya hecho referencia. Esto también lo resolvió.
  • DE ACUERDO En este punto podemos cargar sin fallar. Intentemos importar una función de KERNEL32. Cómo utilizamos el óxido __imp_ ¿símbolo?
    • Podemos definir el nombre del enlace por #[link_name = "__imp_KERNEL32$OutputDebugStringA]
  • El problema con la pregunta anterior es que __imp_ El símbolo debe ser un puntero a la tabla de importación, no a la función en sí, por lo que Rust considera que el símbolo es un puntero único a una función, no un puntero doble a una función.
    • ¿Cómo diablos podemos convencer a Rust de una manera limpia de que las métricas de funciones son en realidad métricas de funciones?
      • Bueno, resulta que podemos usar óxido peligroso para convertir un puntero en un puntero doble, pero eso es sólo la mitad de la solución porque no quiero decir unsafe { make my function pointer a double pointer}(args) Cada vez que quiero llamar.
      • De hecho, puedes hacer que Rust importe símbolos de la siguiente manera __imp_ método, pero sólo puedo hacer que funcione en variables, no en funciones, por lo que es algo inútil.
  • ¿Cómo lanzar automáticamente el puntero durante una llamada?
    • Si no lo sabías, el óxido está llamando deref Al llamar al tipo. Entonces puedes envolver el indicador de función en un tipo y luego implementar core::ops::Deref Para este tipo, el indicador se puede convertir dinámicamente en un indicador doble.
      • El resultado es que la llamada a la función tiene éxito.
  • Ahora podemos importar la función, pero ¿qué pasa con el asignador? Quiero poder usar cadenas y vectores.
    • Esta es la ventaja de Rust Core: ¡alloc es fácil de implementar! El asignador solo crea y administra el montón a través de la función Rtl*Heap de NTDLL.
      • Definí un asignador global que debe inicializarse antes de poder usarse. Ahora obtengo esos símbolos indefinidos de alloc nuevamente, como __rust_alloc y rust_oom. ¿Por qué?
        • Resulta que cuando Rust vincula un archivo binario, crea estos símbolos y los señala al asignador del sistema o a un asignador personalizado (si hay uno definido). Necesito definirlos manualmente.
  • Ahora aparece un símbolo indefinido que se parece al nombre de mi asignador global. ¿Qué ocurre?
    • BOF no utiliza BSS, el asignador se coloca en BSS. Simplemente le dije explícitamente a Rust que pusiera ALLOCATOR en la sección .data #[link_section = ".data"]. Fijado.
  • Ahora que puedo asignar memoria, intentemos usar format! Hong Zai alloc asignar String. ¡Se estrelló! ¿Qué regalar?
    • Esto me llevó un tiempo encontrarlo. El cargador BOF no reubica el contenido en .data y .rdata, solo .text.
      • El analizador COFF tiene una función pe.OBJExecutable.getRelocations Crea estructuras de reubicación para símbolos en .text, pero nada más.
    • El bloqueo ocurrió en una tabla de funciones virtuales que no se actualizó en .rdata.
    • Sin embargo, el cargador BOF no admite el tipo 1 (IMAGE_REL_AMD64_ADDR64), que es el tipo que genera óxido para .rdata.
  • ¿Cómo puedo solicitar la reubicación?
    • Antes de hacer algo loco (como referencias de vtable), escriba mi propio programa de arranque para aplicar las reubicaciones manualmente
    • No es tan difícil, pero requiere cierta coordinación con el cliente Cobalt Strike.
    • Yo uso Cobalt Strike OBJExecutable categorías para analizar COFF y Parser Categoría para empaquetar movimientos adicionales.
      • Esto es más o menos lo que hace CS getRelocations. El lado de Rust obtiene la información a través del parámetro BOF y luego aplica la reubicación.
  • ¿Cómo sé dónde colocar las otras partes de la baliza?
    • El cargador BOF en la baliza elegirá un lugar para colocar .data y .rdata partes, pero no sabemos de dónde vienen esas partes en nuestro código.
    • Terminé agregando símbolos importables al comienzo de las secciones .text, .rdata y .data modificando el script del vinculador. Si alguien tiene una idea mejor, me encantaría escucharla.
  • Cuando estos símbolos se importan como estáticos externos usando variables, uno de ellos produce valores indefinidos. refptr símbolo. ¿Cómo puedo evitar que haga esto?
    • Terminé importándolos como funciones y luego convirtiéndolos para su uso. Un truco, pero hace el trabajo.
    • Una vez solucionadas, las llamadas a vtable funcionan.
  • a 32 bits. Cuando intentas cargarlo, un montón de símbolos no están definidos, incluido un montón que comienza con __. ¿Cómo podemos solucionar este problema?
    • 32 bits agrega un guión bajo a un montón de cosas que por alguna razón puedo buscar pero que no me importan.
    • Cambié el símbolo de importación a _imp_ en lugar de __imp_
      • Al hacer esto se rompieron los 64 bits, así que agregué un cfg_attr Hacer que los nombres de importación tengan la cantidad correcta de guiones bajos según el objetivo
    • Agregué un cfg_attr llegar __section_start__ La notación en el vinculador es solo para x86 y utiliza el nombre del vínculo menos un guión bajo.
    • El símbolo final es terrible. chkstk…He discutido esto en detalle
  • ¿Cómo podemos obtener chkstk?
    • Está definido en NTDLL por lo que podemos simplemente importarlo. NTDLL!_chkstk y definir un __chkstk lo llamamos nosotros mismos

Sí, ahora funcionan BOF de 32 y 64 bits.
Todavía no he probado nada demasiado sofisticado, pero avíseme si tiene alguna pregunta.

Para obtener más información, haga clic aquí.

LEER  BChecks: Liberando el poder de Burp Suite Professional: una guía completa

Publicaciones relacionadas

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Botón volver arriba