domingo, 4 de diciembre de 2016

Se cumplen 20 años del artículo "Smashing the Stack For Fun & Profit"

Smashing the Stack For Fun & Profit por Aleph One, publicado en la edición # 49, en 1996, es un paper clásico sobre los desbordamientos de búfer de la pila, en parte responsable de popularizar la vulnerabilidad. Elias Levy  es el autor, el webmaster de underground.org y de la lista de correo Bugtraq.



Elias Levy (también conocido como Aleph One) fue el moderador de "Bugtraq", una lista de correo de vulnerabilidad de divulgación completa, desde el 14 de mayo de 1996 hasta el 15 de octubre de 2001. Fue el CTO y cofundador de la empresa de seguridad informática SecurityFocus, que fue adquirida por Symantec el 6 de agosto de 2002. Es conocido como el autor del artículo "Smashing The Stack para la diversión y  beneficio", publicado en 1996 Phrack Revista número 49, que fue la primera de alta calidad, que publicó, paso a paso introducción a la pila de las vulnerabilidades de desbordamiento de búfer y su explotación.

Después de la venta de SecurityFocus a Symantec en agosto de 2002, Levy fue acusado por muchos de "vender" y poner en peligro los altos principios de la lista de Bugtraq. La lista de distribución de Full-Disclosure fue fundada en parte como una protesta contra la venta.


Net Cafe, en el año 1996 entrevistó a Elias Levy (Aleph One), autor del famoso txt “Smashing the Stack for Fun and Profit”. 

Este es el primer episodio de la serie Net Cafe. Fue filmado en un cibercafé en San Francisco llamado CoffeeNet. Se ve en la cultura hacker y su influencia en el crecimiento temprano de Internet. Los invitados incluyen Dan Farmer, autor de SATAN y COPS; Elias Levi (alias Aleph 1), webmaster de underground.org y Bugtraq; También "Reid Fleming" y "Caballero Blanco" de Cult of the Dead Cow. 



Reedición del artículo original escrito por Aleph1 en 1996 para la revista phrack, en su edición 49. Artículo original: phrack.org/issues/49/14.html#article. Uno de los mejores artículos de la historia de la seguridad informática.

Re-edición y formateado:



Del Código Máquina al Lenguaje Ensamblador


Cuando nos encontramos frente a una variante de algún código malicioso en un sistema y nos disponemos a analizarlo tenemos el archivo binario y debemos utilizar un desensamblador para generar el código en assembler con el objetivo de analizarlo. Ensamblador (assembler) es en realidad una clase de lenguaje de programación. Cada variante de ensamblador corresponde a una familia particular de microprocesadores tales como x86, x64, SPARC, PowerPC, MIPS o ARM. Dentro de todas estas familias la más habitual dentro las arquitecturas de procesadores es la x86, aunque con el pasar de los años vemos más y más procesadores x64.




  • Data: La sección de datos de un programa hace referencia a una región específica de memoria. Contiene lo que se conoce como las variables estáticas que no cambian con la ejecución del programa. También en esta sección se encuentran las variables globales, que están disponibles desde cualquier parte del programa.
  • Code: En esta región de memoria se almacena el código que se ejecuta del programa donde se alojan todas las instrucciones que se van a ejecutar.
  • Heap: El heap es una región de memoria que se utiliza para alocar nuevos valores durante la ejecución del programa como así también para eliminarlos una vez que se dejaron de utilizar. El heap es una memoría dinámica y su contenido varía a medida que se ejecuta el programa
  • Stack (Pila): La pila se utiliza para alojar las variables locales, parámetros y valores de retorno de una función como así también contiene las direcciones de retorno entre una llamada a una función y otra, siendo muy útil para controlar el flujo de ejecución del programa.


Cualquier software se ejecuta de forma secuencial, es decir una instrucción detrás de otra, la siguiente instrucción a ejecutar se almacena en un registro llamado IP, mediante programación se puede desviar esa ejecución hacia funciones que realicen tareas concretas, el programa en un principio ejecutará el flujo normal hasta que llega a una llamada a una función, en ese momento guarda en RAM el registro IP, ejecuta la función y retorna al flujo principal del programa porque fué capaz de leer el valor almacenado en RAM del registro IP.

Registros


Un registro es, de forma simplificada, un pequeño almacén en la CPU y es, evidentemente, la forma más rápida que tiene la CPU de acceder a datos. En la arquitectura x86 de Intel existen ocho registros de propósito generalEAXEDXECXESIEDIEBPESP y EBX (cambiar E por R en arquitecturas de 64 bits).

Son utilizados para facilitar la tarea en el procesado de las instrucciones, cómo para almacenar datos que se utilizaran posteriormente por las mismas.
  • El registro EAX (RAX)
  • El registro EDX (RDX)
  • El registro ECX (RCX)
  • Los registros ESI (XSI) y EDI (RDI)
  • Los registros ESP (RSP) y EBP (RBP)
  • El registro EBX (RBX)
  • Registro especial EIP (RIP)

 Estos son alguno de los registros básicos que existen:

  • EAX (Accumulator register): Utilizado tanto para realizar cálculos, cómo para el almacenamiento de valores de retorno en "calls".
  • EDX (Data register): Extensión de EAX, utilizada para el almacenamiento de datos en cálculos más complejos.
  • ECX (Count register): Utilizado en funciones que necesiten de contadores, como por ejemplo bucles.
  • - EBX (Base register): Se suele utilizar para apuntar a datos situados en la memoria.
  • ESI (Source index): Utilizado para la lectura de datos.
  • EDI (Destination index): Utilizado para la escritura de datos.
  • ESP (Stack pointer): Apunta a la cima de la pila “stack”.
  • EBP (Base pointer: Apunta a la base de la pila “stack”.

Instrucciones

Son acciones predefinidas en el lenguaje ensamblador. Algunas de las más habituales de ver son:

  • PUSH: Guarda el valor en la pila.
  • POP: Recupera valor de la pila.
  • MOV (dst, src): Copia el operador “src” en el operador “dst”.
  • LEA (reg, src): Copia una dirección de memoria en el registro destino (ej: EAX).
  • ADD (o1, o2): Suma los dos operadores y los almacena en el operador uno.
  • SUB (o1, o2): Resta el valor del segundo operador sobre el primero y lo almacena en el primer operador.
  • INC: Incrementa en 1 el valor del operador indicado.
  • DEC: Decrementa en 1 el valor del operador indicado.
  • AND: El resultado es 1 si los dos operadores son iguales, y 0 en cualquier otro caso.
  • OR: El resultado es 1 si uno o los dos operadores es 1, y 0 en cualquier otro caso.
  • CMP: Compara dos operadores.
  • JMP Salta a la dirección indicada.
  • CALL: Llama/Salta a la dirección/función indicada.
  • - NOP: Not Operation.

Estructura de la pila (Stack)


La pila es una estructura FILO (First In, Last Out) donde los argumentos son apilados en la cima de la misma cuando se invoca una función y retirados cuando la función finaliza. El registro ESP se usa para seguir la pista a la parte más alta del marco de la pila y el registro EBP para seguirle la pista a la parte baja (aunque ya vimos que ciertos compiladores pueden decidir no usar ebp para eso).

La PILA o Stack es un conjunto de direcciones de memoria encargadas de almacena información de llamadas a funciones, variables locales, direcciones de retorno a funciones anteriores, entre otras tareas. La PILA es dinámica, por lo que va cambiando su tamaño dependiendo de la función a la cual se encuentre asociada, dispone de una estructura “First In, Last Out”, por lo que lo último que entra será lo primero en salir, delimitada siempre por su cima (ESP) y por su base (EBP).

Puntos de Interrupción (Breakpoints)


La habilidad de parar un proceso que está siendo depurado se consigue mediante el fijado de puntos de interrupción o breakpoints. Al parar el proceso podemos inspeccionar el valor de las variables, los argumentos de la pila, y las direcciones de memoria sin que el proceso modifique estos valores hasta que no se lo indicas de esa forma al depurador.


Los puntos de interrupción son la herramienta principal proporcionada por los depuradores. Los puntos de interrupción le permiten interrumpir la ejecución del programa en un lugar específico. Hay dos tipos de puntos de interrupción:

  • Puntos de interrupción de software
  • Puntos de interrupción de hardware


Es muy difícil realizar ingeniería inversa de software sin puntos de interrupción. Las tácticas populares de ingeniería anti-reversa se basan en la detección de puntos de interrupción, proporcionando una serie de métodos anti-depuración correspondientes.

Cómo omitir una verificación de punto de interrupción de software

No existe un enfoque universal para evitar una verificación de punto de interrupción de software. Para evitar esta protección, debes encontrar el código que calcula la suma de verificación y sustituir el valor devuelto con una constante, así como los valores de todas las variables que almacenan sumas de verificación de funciones.

Puntos de interrupción de hardware

  • DR0-DR3 – breakpoint registers -“Debug Address Registers” or “Address-Breakpoint Registers”
  • DR4 & DR5 – reserved - “Reserved Debug Registers”
  • DR6 – debug status - "Debug Status Register”
  • DR7 – debug control -“Debug Control Register”


SEH (Structured Exception Handling)



El manejo estructurado de excepciones es un mecanismo proporcionado por el sistema operativo a una aplicación que le permite recibir notificaciones sobre situaciones excepcionales como la división por cero, la referencia a un puntero inexistente o la ejecución de una instrucción restringida. Este mecanismo le permite manejar excepciones dentro de una aplicación, sin la participación del sistema operativo. Si no se maneja una excepción, dará como resultado la finalización anormal del programa. Los desarrolladores suelen ubicar punteros a SEH en la pila, que se denominan marcos SEH. La dirección de trama SEH actual se encuentra en el desplazamiento 0 en relación con el selector FS (o el selector GS para los sistemas x64).

VEH (Vectored Exception Handler)


VEH se introdujo en Windows XP y es una variación de SEH. VEH y SEH no dependen el uno del otro y funcionan simultáneamente. Cuando se agrega un nuevo "manejador" VEH, la cadena SEH no se ve afectada ya que la lista de manejadores VEH se almacena en la variable no exportada ntdll! LdrpVectorHandlerList. Los mecanismos VEH y SEH son bastante similares, la única diferencia es que las funciones documentadas se utilizan para configurar y eliminar un controlador VEH.

Exploiting sobre Linux-x86

La porción de RAM utilizada por un programa cuando se llama a una función es comunmente llamada pila o stack, debemos tener en cuenta que la pila crece de arriba a abajo, como podemos ver hay una parte de la ram que almacena la copia del registro IP(EIP) en el momento de realizar la llamada a la función, en el programa no se tiene en cuenta que el dato introducido sea de una longitud concreta, se realiza la copia a ciegas a la variable nombre, aprovechando este descuido podemos hacer crecer la variable nombre hasta llegar a ocupar la dirección de retorno EIP haciendo así que el software termine retornando a otra posición de memoria y no la que se guardó al realizar la llamada a la función func.

¿Qué es una Shellcode?


Una shellcode no es mas que el conjunto de opcodes (instrucciones en hexadecimal) que ejecutará el procesador para realizar un acción en concreto, las shellcodes suelen estar escritas en ensamblador ya que nos permite un control total sobre el proceso de ejecución además de un tamaño inferior de la shellcode.

Stack Buffer Overflow

In software, a stack buffer overflow occurs when a program writes to a memory address on the program's call stack outside of the intended data structure; usually a fixed length buffer.

Es decir, la vulnerabilidad Stack Buffer Overflow ocurre cuando una aplicación no controla correctamente el número de bytes que son almacenados en una dirección de memoria previamente reservada, de forma que la cantidad de bytes que se van a almacenar son superiores a los reservados.




Nuestro principal objetivo es llegar a sobrescribir la dirección de retorno (almacenada en la PILA) con un valor que apunte a nuestra shellcode.

  • Registro EIP (Extended Instruction Pointer): Este registro apunta a la siguiente dirección de memoria que el procesador va a ejecutar.
  • Instrucción RETN: Es la instrucción encargada de recoger el valor de ESP y almacenarlo en el registro EIP, de este modo el valor de ESP será la próxima dirección de memoria que el
    procesador va a ejecutar.
  • Dirección de retorno: Es el valor exacto que nos indica la dirección de memoria donde habíamos dejado la aplicación ante de entrar en una subfunción, para así cuando esta termine volver a la posición exacta donde nos quedamos. Este valor se encontrará almacenado en la siguiente dirección de memoria del EBP de la subfunción

#include
void main() {
char *name[2]; name[0] = "/bin/sh";
name[1] = NULL;
execve(name[0], name, NULL);
}


Anti-sandboxing y Anti-debugging

Debuggers como OllyDbg, WindDbg, Radare o IDA ya sabréis que se requieren conocimientos sobre lenguaje ensamblador y estructuras internas de ficheros.

Una técnica anti-debugging muy fácil de implementar, pero también muy fácil de evadir, es la función “IsDebuggerPresent”. PEB (Process Environment Block)

  • Los códigos de espagueti y basura hacen que las herramientas de análisis comunes sean ineficaces

El primer problema de ofuscación que requiere una solución es la eliminación de las instrucciones basura y el "código de espagueti", que es una técnica que pretende confundir los programas de desamblado El código de espagueti hace que el programa fluya de forma difícil de leer al agregar saltos continuos de código, de ahí el nombre.

Este problema no es nuevo, y en situaciones comunes se conocen complementos de reversing que pueden ayudar en esta tarea. Sin embargo, a veces no podemos encontrar un buen complemento de desensamblador interactivo (IDA) que pueda normalizar el flujo de código.

VM detection

  • hardware IDs are VmBus in case of HyperV
  • VEN_15AD in case of VMware


Debugger detection 

  • IsDebuggerPresent
  • ThreadHideFromDebugger
  • ProcessDebugPort
  • ProcessDebugObjectHandle
  • ProcessDebugFlags
  • CheckRemoteDebuggerPresent
  • NtQueryInformationProcess
  • ProcessBasicInformation

No hay comentarios:

Publicar un comentario