logo de clasesordenador

ATOMBOMBING TECNICA NUEVA DE INYECCIÓN DE CODIGO PARA WINDOWS

Nueva técnica de inyección de código, denominado AtomBombing, que manipula las tablas atom de Windows y las llamadas a procedimiento asincrónos (APC).

En la actualidad, esta técnica no es detectada por las soluciones de seguridad más comunes que se centran en la prevención de la infiltración. La inyección de código ha sido un arma poderosa en el arsenal de los hackers durante muchos años.

AtomBombing obras en tres etapas principales:

  • 1 Donde los escribe: La Escritura de datos arbitrarios en cualquier ubicación en el espacio de direcciones del proceso de destino.
  • 2 Ejecución: El secuestro de un hilo del proceso de destino para ejecutar el código escrito en la etapa 1.
  • 3 Restauración: La limpieza y restauración de la ejecución del hilo secuestrado en la etapa 2.

ATOMBOMBING ETAPA 1: DONDE LO ESCRIBE

Aqui teneis estas tres apis:

  • GlobalAddAtom: Agrega una cadena de caracteres de la tabla de átom global y devuelve un valor único (un átom) la identificación de la cadena.
  • GlobalGetAtomName: Recupera una copia de la cadena de caracteres asociado con el átomo global especificada.
  • GlobalAddAtom: Uno puede almacenar un valor nulo en el buffer y termina en la tabla de átom global. Esta tabla es accesible desde todos los demás procesos en el sistema. El buffer puede entonces ser recuperada llamando a la api GlobalGetAtomName. GlobalGetAtomName espera un puntero a un búfer de salida, por lo que la persona que llama elige dónde se almacenará el buffer de terminación nula.

En teoría, si se pudiera añadir un buffer que contiene código shell a la tabla de átomo global llamando GlobalAddAtom, y entonces de alguna manera el proceso de destino para llamar GlobalGetAtomName pude copiar código de ub proceso para el proceso de destino, sin llamar WriteProcessMemory.

¿cómo conseguir el proceso de destino para llamar GlobalGetAtomName? Mediante el uso de llamadas a procedimiento asincróno (APC):

QueueUserAPC: Agrega un objeto en modo de usuario llamando al procedimiento asincrónico (APC) a la cola APC del subproceso especificado.


DWORD WinAPI QueueUserAPC (
_IN_ PAPCFUNC pfnAPC,
_IN_ MANGO hThread,
_IN_ ULONG_PTR dwData
);

QueueUserAPC recibe un puntero a un APCProc que se define como sigue:


APCProc RETROLLAMADA VOID (
_IN_ ULONG_PTR dwParam
);

El prototipo de GlobalGetAtomName es:


UINT WINAPI GlobalGetAtomName (
_IN_ ATOM Natomas,
_Out_ LPTSTR lpBuffer,
_IN_ Int nTamaño );

Desde GlobalGetAtomName espera 3 parámetros (mientras APCProc se define a esperar sólo 1 parámetro) que no podemos utilizar QueueUserAPC para que el proceso de destino pueda llamar GlobalGetAtomName.

QueueUserAPC utiliza la llamada al sistema NtQueueApcThread indocumentado con el fin de añadir el APC a la cola APC del subproceso de destino.

Curiosamente NtQueueApcThread recibe un puntero a una función que se va a llamar de forma asincrónica en el subproceso de destino, pero la función que se pasa no es la función APCProc original de la persona que llama se pasa a QueueUserAPC. En su lugar la función de ser pasado es ntdll! RtlDispatchAPC, y la función original de APCProc pasado a QueueUserAPC se pasa como un parámetro para ntdll! RtlDispatchAPC.

Vamos a echar un vistazo a ntdll RtlDispatchAPC:

Se inicia verificando si el 3 rd parámetro es válido, lo que significa un ActivationContext necesita ser activada antes de enviar la APC.

Si un ActivationContext necesita ser activado. La función ntdll RtlDispatchAPC ejecuta lo siguiente:

Plantillas de wordpress

El ActivationContext pasado (actualmente en ESI) se activará llamando RtlActivateActivationContextUnsafeFast.

El parámetro a la función APCProc original (es decir el tercer parámetro pasado a QueueUserAPC) se inserta en la pila. Esto se debe a que estamos a punto de llamar a la función original de APCProc.

Justo antes de despachar la APC, una llamada a la CFG (__guard_check_icall_fptr) se hace para asegurarse de que el objetivo de APC es una función válida CFG.

Una llamada a la APCProc original se hace, y eso es todo - la APC ha sido enviado.

Una vez APCProc regresa, el contexto de activación se desactiva:

El código se salta todas las cosas relacionadas con la activación de contexto y simplemente distribuye el APC de inmediato después de llamar a CFG.

ATOMBOMBING ETAPA 2: EJECUCIÓN

Es necesario una manera de asignar la memoria constantemente RWX en el proceso de destino sin tener que llamar VirtualAllocEx dentro del contexto del proceso de inyección con lo que se asigna memoria ejecutable o cambiar los indicadores de protección de memoria ya asignada.

¿Qué es lo que tenemos hasta ahora? Escribir-lo-que sea un deseo ardiente + para conseguir un poco de memoria ejecutable. Cuando se inventó el DEP, pensaron sus creadores, "eso es todo, los datos ya no es ejecutable, por lo tanto, nadie va a ser capaz de aprovechar las vulnerabilidades de nuevo". Desafortunadamente, ese no era el caso; una nueva técnica de explotación fue inventado exclusivamente para eludir DEP

¿Cómo podemos utilizar a nuestro favor ROP con el fin de ejecutar nuestro código shell en el proceso de destino?

Podemos copiar nuestro código a un código cueva RW en el proceso de destino (utilizando el método descrito en la etapa 1). A continuación, utilice una cadena ROP meticulosamente diseñado para asignar memoria RWX, copiar el código de la cueva código RW a la memoria RWX recién asignada, y finalmente saltar a la memoria RWX y ejecutarlo.

Nuestra cadena de ROP tiene que hacer 3 cosas:

  • 2.1 Asignar memoria RWX
  • 2.2 Copiar el código shell de la cueva código RW a la memoria recién asignada RWX
  • 2.3 Ejecutar la memoria recién asignada RWX
Plantillas de wordpress

ROP CADENA PASO 2.1: ASIGNAR MEMORIA RWX

Destinar algo de memoria RWX. La primera función que viene a la mente es VirtualAlloc una función muy útil que se puede utilizar para asignar memoria RWX. El único problema es que la función devuelve la memoria RWX recién asignado en EAX lo que haría que nuestra cadena de ROP complicado por tener que encontrar una manera de pasar el VirtualAlloc valor almacenado en EAX a la siguiente función en la cadena.

Un truco muy ordenada puede ser empleado con el fin de simplificar nuestra cadena de ROP y hacerlo más sofisticado. En lugar de utilizar VirtualAlloc, podemos utilizar ZwAllocateVirtualMemory, que devuelve la memoria RWX recién asignado como parámetro de salida. De esta manera podemos realmente establecer nuestra pila de manera que ZwAllocateVirtualMemory almacena la memoria recién asignada aún más a lo largo de la pila, que pasa efectivamente la dirección a la siguiente función en la cadena.

ROP CADENA PASO 2.2: COPIAR EL CÓDIGO SHELL

La siguiente función que necesitamos es una función que va a copiar la memoria de un buffer a otro. Dos opciones vienen a la mente: establecimiento de memoria y RtlMoveMemory. Al crear este tipo de cadena de ROP uno puede ser inicialmente inclinado a ir con RtlMoveMemory, ya que utiliza la convención stdcall llamando, lo que significa que va a limpiar la pila después de sí mismo. Este es un caso especial, aunque. Tenemos que copiar a una dirección de memoria (colocado en la pila por ZwAllocateVirtualMemory) y entonces de alguna manera esta dirección tiene que ser llamado. Si utilizamos RtlMoveMemory, aparecerá la dirección del código shell RWX justo al lado de la pila a su regreso. Por otro lado, si usamos establecimiento de memoria, la primera entrada en la pila sería la dirección de retorno del establecimiento de memoria, seguido por el parámetro de destino del establecimiento de memoria (es decir, el código shell RWX).

ROP CADENA PASO 2.3: EJECUCIÓN DE LA MEMORIA RECIÉN ASIGNADA RWX

Hemos asignado la memoria RWX y copiado nuestro código shell a ella. Estamos a punto de volver del establecimiento de memoria, pero la dirección del código shell RWX en la pila es de 4 bytes lejos de la dirección de retorno. Por lo tanto, todo lo que tenemos que hacer es añadir un aparato extremadamente simple de nuestra cadena de ROP. Esta sencilla herramienta se ejecutará el código de operación "ret". memcpy volverá a este sencillo aparato que va a "ret" a la derecha en nuestro código shell RWX.

Establecer EIP para que apunte a ZwAllocateVirtualMemory, y ESP para que apunte a esta cadena ROP:


0x30000000 ntdll! memcpy // Devuelve la dirección de ZwAllocateVirtualMemory
0x30000004 0xffffffff // Manipulador Pseudo al proceso actual
0x30000008 0x30000020 // Dónde almacenar la memoria asignada
0x3000000C NULO // irrelevante
0x30000010 0x30000028 // Puntero al tamaño de la memoria necesaria
0x30000014 Mem_kmit // Confirmar y no de reserva
0x30000018 PAGE_EXECUTE_READWRITE // RWX
0x3000001C POINTER_TO_SOME_RET_INSTRUCTION // Devolver Dirección del establecimiento de memoria, nuestra herramienta extremadamente simple ret.
0x30000020 NULO // Donde se guarda la memoria asignada y el parámetro de destino del establecimiento de memoria. Esto almacenará la dirección del código shell RWX.
0x30000024 CODE_CAVE_ADDRESS // La cueva código RW que contiene el código shell que se desea copiar
0x30000028 SHELLCODE_SIZE // El tamaño del código shell que se asignará
Plantillas de wordpress

LA INVOCACIÓN DE LA CADENA ROP

APC permiten que mande a 3 parámetros. Obviamente necesito para almacenar 11 parámetros en la pila. Nuestra mejor apuesta es hacer pivotar la pila a una cierta memoria RW que contendrá nuestra cadena de ROP (por ejemplo, la cueva código RW en kernelbase).

¿Cómo podríamos hacer pivotar la pila?


NTSYSAPI NTSTATUS NTAPI NtSetContextThread (
_IN_ MANGO hThread,
_IN_ CONTEXTO const * lpContext
);

Esta llamada al sistema fijará el contexto (valores de registro) de hThread a los valores contenidos en lpContext. Si podemos conseguir que el proceso de destino para llamar a esta llamada al sistema con un lpContext que establecerá ESP para que apunte a nuestra cadena de ROP y establecer EIP para que apunte a ZwAllocateVirtualMemory, entonces nuestra cadena de ROP se ejecutará. La ejecución de la cadena de ROP, finalmente, dará lugar a la ejecución de nuestro código shell.

¿Cómo conseguimos el proceso de destino para realizar esta llamada? APC ha sido bueno con nosotros hasta el momento, pero esta llamada al sistema espera 2 parámetros y no 3, así que cuando se devuelve la pila será corrupto, y el comportamiento será indefinido. Dicho esto, si se pasa un identificador para el hilo actual como hThread, entonces la función nunca volverá. La razón es que una vez que la ejecución se pasa al núcleo, el contexto de la rosca se establecerá en el contexto especificado por lpContext, y no habrá ningún rastro de que alguna vez se llamó NtSetContextThread. Si todo sale como esperamos, vamos a tener éxito secuestrado un hilo y lo consiguió a ejecutar nuestro código shell maliciosos.

wordpress temas

ETAPA ATOMBOMBING 3: RESTAURACIÓN

El hilo que nos secuestran tenía un propósito antes de haber secuestrado él. Si no restauramos su ejecución, no se sabe qué tipo de efecto que podría tener sobre el proceso de destino.

¿Cómo restauramos ejecución? Me gustaría recordarles que estamos ahora en el contexto de un APC. Cuando la función APC completa, de alguna manera se restaura la ejecución segura. Veamos el envío de APC desde el punto de vista del proceso de destino.

Parece que la función de encargado de despachar las APC (WaitForSingleObjectEx en este ejemplo) es ntdll! KiUserApcDispatcher.

Podemos ver las llamadas "3" en este bloque de código. La primera llamada es CFG, la siguiente llamada es ECX (que es la dirección de la función APC), y, finalmente, una llamada a la ZwContinue indocumentado.

ZwContinue espera recibir un puntero a una estructura de contexto y se reanuda la ejecución. En realidad el núcleo comprobará si hay alguna más APC en cola APC del subproceso, y les envían antes de que finalmente reanudar la ejecución original del hilo, pero podemos ignorar eso.

La estructura CONTEXTO ser pasado a ZwContinue se almacena en EDI antes de llamar a la función APC (almacenado en ECX). Podemos guardar el valor de intercambio electrónico de datos al comienzo de nuestro código shell, y llamar a ZwContinue con el valor original de intercambio electrónico de datos al final del código shell, restaurando así la ejecución segura.

Hay que asegurarse de que el valor de intercambio electrónico de datos, no se anulará durante la llamada a NtSetContextThread, ya que modifica los valores de los registros. Esto se puede lograr fácilmente mediante el establecimiento de ContextFlags (miembro de la estructura CONTEXTO pasado a NtSetContextThread) para CONTEXT_CONTROL lo que significa que sólo EBP, EIP, SEGCS, EFLAGS, ESP, y SEGSS se verá afectada. Mientras (CONTEXT.ContextFlags | CONTEXT_INTEGER == 0) que deben estar bien.

Y ahí lo tienen, hemos inyectado código en chrome.exe. Nuestro código inyectado dio lugar a la calc.exe clásico que demuestra que funciona.

El núcleo comprueba cola APC del subproceso. Si la cola contiene punteros a funciones de devolución de llamada, el núcleo elimina el puntero de la cola y lo envía al hilo.

El hilo se ejecuta la función de devolución de llamada.

Pasos 1 y 2 se repiten para cada puntero permanece en la cola.

Cuando la cola está vacía, el hilo regresa de la función que la ha colocado en un estado alertable.

Para nuestra técnica sea eficaz el proceso de destino debe tener al menos un hilo que está en un estado alertable, o va a entrar en un estado alertable en algún momento, de lo contrario nuestros vehículos blindados en realidad nunca se ejecutará.

Plantillas de wordpress Fecha actualización el 2017-6-18. Fecha publicación el . Fuente: Breakingmalware. Categoría: Malware. Autor: Mapa del sitio
secuestrador de navegador