¡Acceso ilimitado 24/7 a todos nuestros libros y vídeos! Descubra la Biblioteca Online ENI. Pulse aquí
¡Acceso ilimitado 24/7 a todos nuestros libros y vídeos! Descubra la Biblioteca Online ENI. Pulse aquí

Las vulnerabilidades de las aplicaciones

Aspectos generales

Los ataques de buffer overflow se encuentran entre los más extendidos. Aproximadamente representan el 60 % de los ataques conocidos. Se hacen para explotar un error en la gestión de las zonas de memoria declaradas en el programa, para que ejecute una acción que no debería realizar.

En general, el pirata intentará obtener acceso remoto a la máquina de la víctima tomando el control del shell (línea de comando en Linux). Según el método que utilice, a esto se le denomina "bind de shell" o "reverse connection".

Se va a probar en un archivo binario que tenga el SUID root (es decir, un programa que se lanza al inicio con derechos de administrador en Linux), para elevar sus privilegios hasta el nivel de root.

Para conseguirlo hay que estar muy familiarizado con ensamblador, tener nociones de programación y un buen conocimiento de la estructura y funcionamiento de un ordenador, en especial del microprocesador.

Nociones de ensamblador

1. Introducción

Hay que saber cuándo escribir en ensamblador: en algunas ocasiones no queda otra posibilidad que escribir en ensamblador y otras no sirve de nada. El sistema operativo, así como una buena cantidad de productos estándares, está programado de tal manera que el ensamblador es simplemente inevitable. La reverse engineering y las vulnerabilidades de aplicación son ejemplos en los que el ensamblador es omnipresente.

2. Primeros pasos

a. Aprender a contar

En nuestras lecturas, todos nos hemos encontrado con números expresados en hexadecimal o binario. El sistema binario es el lenguaje de los ordenadores; tanto si escribimos programas en ensamblador, C o cualquier otro lenguaje, el PC entiende binario, habla en binario.

El sistema hexadecimal está hecho para permitir a los humanos una mejor comprensión del lenguaje del PC.

b. El sistema binario

El sistema binario se compone de dos elementos: 0 y 1.

Una secuencia de ocho bits se denomina byte, una secuencia de dieciséis bits se denomina palabra (2 bytes) y una secuencia de treinta y dos bits se denomina doble palabra (4 bytes).

Los microprocesadores Intel están compuestos por registros. Usaremos estos registros para comenzar a trabajar con ensamblador.

EAX, EBX, ECX y EDX son registros de 32 bits. Contienen respectivamente AX, BX, CX, DX (16 bits) en su parte inferior que, a su vez, se componen de AH, BH, CH, DH (8 bits) en su parte superior y de AL, BL, CL, DL (8 bits) en su parte inferior. A continuación, se muestra un pequeño diagrama que ilustra esto:

images/08ep01.png

Los registros EAX, EBX, ECX y EDX solo están disponibles en modo protegido (32 bits).

Consideremos el ejemplo del registro EAX de 32 bits (0 a 31). Para indicar los 16 bits menos significativos, usamos AX. Podemos dividir el registro AX en dos partes: AL los 8 bits menos significativos y AH los 8 bits más significativos. Hay otros registros que iremos viendo a medida que avancemos por estas páginas.

Para colocar un valor en el registro EAX, usaremos la instrucción mov. Esta instrucción se utiliza para copiar un byte o una palabra de un operando origen en un operando destino.

    mov  eax,10100101010010101001010101101101b 
    mov  ax,1011100101010101b 
    mov  al,10110101b 
    mov  bl,al...

Aspectos básicos de los shellcodes

Un shellcode es un trozo de código de lenguaje de máquina. No es ni más ni menos que un programa muy pequeño ejecutado por su procesador, por lo que es capaz de hacer todo lo que cualquier programa puede hacer.

1. Ejemplo 1: shellcode.py

xor eax,eax  
xor ebx,ebx  
xor ecx,ecx  
xor edx,edx  
jmp short string  
code:  
pop   ecx; colocamos la dirección de memoria de la frase en la pila  
mov bl,1  
mov dl,23  
mov al,4  
int 0x80; las cuatro líneas anteriores forman el write()  
dec bl  
mov al,1  int 0x80; Las tres líneas anteriores forman el exit()  
string:  
call code  db 'hola mundo' 

Para poder usar este código para un ataque de tipo buffer overflow, queremos transformarlo en código hexadecimal.

Para esto, utilizamos el programa en Python que se muestra a continuación (shellcode.py), que transforma directamente el código en hexadecimal. Para comprender este programa, consulte la traducción de Francisco Callejo Giménez del libro de Mark Pilgrim Dive Into Python (http://es.diveintopython.net/toc.html).

El programa en ensamblador debe estar en el mismo directorio que shellcode.py.

shellcode.py

#!/usr/bin/env python 
import os 
 
file=raw_input("introduzca...

Los buffers overflows

1. Algunas definiciones

  • Exploit: aprovechar una vulnerabilidad para hacer que el sistema de destino reaccione de una manera diferente a la prevista. Un exploit también es la herramienta, el conjunto de instrucciones o código que se utiliza para explotar una vulnerabilidad.

  • 0 day: este es un exploit para una vulnerabilidad que aún no se ha publicado; algunas veces se refiere a la vulnerabilidad en sí.

  • Fuzzer: es una herramienta o una aplicación que va a enviar a un programa la totalidad o una parte de valores inesperados para determinar si existe un error en este sistema.

Wikipedia da esta definición para los buffer overflows:

"En informática, un desbordamiento de memoria o buffer overflow (en inglés buffer overflow) es un error causado por un proceso que, mientras escribe en un búfer, escribe fuera del espacio asignado al búfer, sobrescribiendo la información necesaria para el proceso.

Cuando el error ocurre sin querer, el comportamiento del ordenador se vuelve impredecible. A menudo, esto provoca un bloqueo del programa o incluso de todo el sistema.

El error también puede estar provocado intencionalmente y se puede usar para violar la política de seguridad de un sistema. Los piratas informáticos utilizan comúnmente esta técnica. La estrategia del atacante es secuestrar el programa con errores haciendo que ejecute las instrucciones que introdujo en el proceso".

Para comprender a fondo estas definiciones, es fundamental tener buenas nociones de gestión de memoria y pila, así como de ejecución de programas.

2. Nociones esenciales

Cuando se ejecuta un programa, se almacenan diferentes elementos (por ejemplo, variables) en la memoria.

En primer lugar, el sistema operativo crea ubicaciones de memoria donde el programa puede "ejecutarse". Esta ubicación de memoria incluye las instrucciones del programa actual.

En segundo lugar, la información del programa se carga en el espacio de memoria creado.

Hay tres tipos de segmentos en el programa: .text, .bss y .data.

  • .text es de solo lectura, mientras que .bss y .data son de lectura/escritura.

  • .data y .bss están reservados para las variables globales.

  • .data contiene los datos inicializados.

  • .bss contiene los datos no inicializados.

  • .text contiene las instrucciones del programa.

Finalmente, se inicializan la pila...

Las vulnerabilidades de Windows

1. Introducción

Los conceptos que hemos visto en Linux en los capítulos anteriores siguen siendo válidos en Windows, pero transponiéndolos en función del sistema operativo.

Por supuesto, el funcionamiento de la pila y del heap es similar.

Como en Linux, necesitaremos un depurador. Aquí usaremos Immunity Debugger (https://www.immunityinc.com/products/debugger) para intentar explotar las vulnerabilidades. Este depurador es gratuito e incluye:

  • una interfaz sencilla y comprensible;

  • un lenguaje de script sólido y potente para automatizar la depuración;

  • la posibilidad de conexión con otras herramientas de desarrollo de exploits.

En la red encontrará muchos tutoriales para aprender a usarlo, así como varias explicaciones de Crack-me que usan Immunity Debugger (pequeño software creado para aprender Ingeniería Inversa).

Usaremos Dev-C++ para escribir y compilar nuestros programas: https://sourceforge.net/projects/orwelldevcpp/

2. Primeros pasos

Para empezar, vamos a utilizar el siguiente programa con el fin de comprender los conceptos básicos. Seguiremos con programas comerciales reales para implementarlos.

#include "string.h" 
void function(char* buf) 
       { 
        char ownz[10]; 
        strcpy(ownz, buf); 
       } 
int main(int argc,char* argv[ ]) 
      { 
       function(argv[1]); 
       return 0; 
      } 

a. En modo consola

Primero lanzaremos nuestro programa en línea de comandos (Start - Execute - cmd) añadiéndole argumentos, "A" por ejemplo.

hack1.exe AAAADCBA 
hack1.exe AAAAAAAADCBA 
etc. 

Aumentaremos el número de A hasta que obtengamos una reacción inusual de la máquina, como se muestra en la siguiente captura de pantalla:

images/09ept27.png

Como ha podido adivinar, el objetivo es sobrescribir EIP con el valor DCBA.

Esto también lo vemos en la ventana de error que aparece para el Offset (41424344 que se corresponde con ABCD en ASCII).

Por lo tanto, con un número preciso de bytes (aquí 24 bytes para llenar...

Caso concreto: Ability Server

Para trabajar en ataques de tipo buffer overflow, lo mejor es disponer de dos máquinas virtuales (por ejemplo, VirtualBox) que nos permitirán recrear la máquina víctima en local antes de realizar el ataque en la vida real.

La primera máquina funcionará con Debian y la segunda, la víctima, en Windows.

Por supuesto, la recogida de información sobre la víctima se realizará de antemano para recrear una máquina perfectamente idéntica. Es necesario conocer el sistema operativo utilizado, la versión exacta del software instalado que probablemente sea vulnerable y saber si se ha instalado un Service Pack.

Para el ejemplo, la víctima será una máquina con Windows XP Service Pack 2 con Ability Server 2.34. Puede encontrar este servidor FTP buscando en Google.

1. Fuzzing

Lo primero que debe hacer es probar el software para averiguar si es probable que sea vulnerable. Para esto, existe software casi listo para usar, como Fusil o Spike. Pero el objetivo aquí es entender cómo funciona un fuzzer, así que vamos a construirlo.

Podemos utilizar cualquier lenguaje, debemos saber programar sockets (conexiones remotas TCP/IP o UDP) y conocer el protocolo que vamos a atacar (FTP en nuestro ejemplo). La mejor manera de conocer un protocolo es obtener su RFC; una simple búsqueda en Google nos ayudará.

Según Wikipedia: "Fuzzing es una técnica para probar software. La idea es inyectar datos aleatorios en las entradas de un programa. Si el programa falla (por ejemplo, si se bloquea o genera un error), entonces hay vulnerabilidades que hay que corregir".

Conocemos la dirección IP de la víctima: 10.0.0.3. Nuestra dirección IP será 10.0.0.2.

Las máquinas virtuales están listas, podemos comenzar nuestro ataque.

Aquí está el programa en Python:

#!/usr/bin/python 
import socket 
buffer=["A"] 
counter=20 
while len(buffer) <= 100: 
      buffer.append("A"*counter) 
      counter=counter+20 
comandos=["MKD","CWD","STOR"] 
for comando in comandos: 
      for string in buffer: 
             print...

Caso concreto: MediaCoder-0.7.5.4796

El software utilizado aquí es MediaCoder-0.7.5.4796, que se puede obtener en la siguiente dirección: http://www.digital-digest.com/software/mediacoder_history.html

Por supuesto, podemos formarnos en este software porque la vulnerabilidad explotada se publicó en http://www.exploit-db.com/exploits/15663/

1. Crash del software

Intentaremos bloquear el software MediaCoder. Para hacer esto, vamos a crear un pequeño programa en Python que creará el archivo crash.m3u. Abriremos este último con MediaCoder, que a su vez se adjuntará a Immunity Debugger.

obArchivo = open('crash.m3u','w') 
obArchivo.write("A"*2000) 
obArchivo.close() 

Las 2000 "A" son aleatorias; lo que queremos es saber exactamente cuándo se sobrescriben SEH y next SEH.

images/09EP25N.png

Podemos ir a ver qué pasa en el lado del SEH: View - SEH chain.

images/09EP26N.png

Notamos que hemos sobrescrito el SEH con 41414141.

Continuemos nuestra exploración reemplazando las A con 2000 caracteres que, tomados de cuatro en cuatro, nunca son idénticos. Para esto usamos genbuf.py, cuyo código se da a continuación:

#!/usr/bin/env python 
import sys 
import string 
 
def usage(): 
   print "Uso: ", sys.argv[0], " <number> [string]" 
   print "   <number> es el tamaño del buffer a generar." 
   print "   [string] es la cadena de caracteres a encontrar 
(opcional)." 
   print "" 
   print "   si [string] se utiliza, el buffer no se  
mostrará, solo su localización" 
   print "     donde comienza la cadena de caracteres en el buffer. 
¡Esta búsqueda distingue entre mayúsculas y minúsculas!" 
   sys.exit() 
 
try: 
   dummy = int(sys.argv[1]) ...

Caso concreto: BlazeDVD 5.1 Professional

Aquí vamos a estudiar una vulnerabilidad descubierta en 2009 y disponible en: https://packetstormsecurity.org/files/82927/BlazeDVD-5.1-PLF-Buffer-Overflow.html

Este exploit puede ser efectivo usando un archivo PLF que abriremos con la aplicación.

Comencemos determinando cuántos argumentos necesitamos enviar (escribir en el archivo PLF) para sobrescribir la estructura SE.

Vamos a trabajar en un sistema Windows Vista.

obArchivo = open('blazesploit.plf','w') 
obArchivo.write("A"*608+"BBBBCCCC") 
obArchivo.close() 
images/10EP308.png

Vemos aquí que tenemos dos soluciones: o acceso directo a EIP porque encontramos 41414141 en EIP, o a través del SEH ya que tenemos 43434343 en el SE handler y 42424242 en el next SEH.

También tenemos ESP que apunta a nuestro búfer.

Ahora miremos en la tabla ASLR (iASLRdynamicbase que está en los plugins).

images/10EP309.png

Muchos módulos parecen no usar ASLR ("randomización" de direcciones), por ejemplo, podríamos usar skinscrollbar.dll.

Sobrescribimos EIP después de 260 argumentos. Entonces podríamos usar un jmp esp, un call esp o push esp/ret.

Buscando entre las DLL encontramos:

  • blazedvd.exe: 79 direcciones (pero bytes null);

  • skinscrollbar.dll: 0 dirección;

  • configuration.dll: 2 direcciones, sin bytes null;

  • epg.dll: 20 direcciones, sin bytes null;

  • mediaplayerctrl.dll: 15 direcciones...

Conclusión

Este capítulo le ha ofrecido una descripción general de las vulnerabilidades comunes de Linux y Windows, así como de las técnicas utilizadas.

En la parte legal veremos lo que podemos hacer legalmente y nuestras obligaciones cuando descubrimos vulnerabilidades.

La vigilancia tecnológica es muy importante y esencial, especialmente en términos de vulnerabilidades de aplicación. La suscripción a fuentes RSS, las visitas regulares a sitios web especializados, la recepción diaria de las vulnerabilidades descubiertas y su estudio permanente, y sobre todo la práctica intensiva son la clave del éxito para conseguir hacerse un nombre en este entorno tan cerrado.

La perseverancia será su mejor activo.

Referencias

Ensamblador: http://www.e-booksdirectory.com/details.php?ebook=1881

Python: http://www.inforef.be/swi/download/python_notes.pdf

Buffer overflow attacks: Detect, exploit, prevent de Jason Deckard - Edición syngress

David Litchfield’s guide to Buffer Overflow Attacks - Edición syngress

Debugging With GDB The Gnu Source-Level Debugger - Edición GNU Press

Hacking: The Art of Exploitation - Edición No Starch Press US

Sitio web de corelan: http://www.corelan.be