Exploits y Stack Overflows en Windows.txt

Embed Size (px)

Citation preview

***************************************** * EXPLOITS Y STACK OVERFLOWS EN WINDOWS * * Rojodos rojodos2[at]yahoo[dot]es * ***************************************** Naaas :) Visto el "miedo" que se tiene al tema de los exploits y los buffers overflows, que parece algo mstico, y que cada dos por tres un script kiddie pregunta en el foro como compilar un exploit, como funciona un exploit o donde encuentro un exploit, me he decidido a hacer un taller de buffers overflows y exploits, en Windows :) Tambin veremos como crear una shellcode muy muy bsica, muy explicadita, para que se entienda perfectamente. Quizs, si el tema tiene xito, hagamos otro sobre Linux, aunque son bastante parecidos, en Linux la cosa cambia en muchos aspectos. Este documento esta basado en muchos que hay por la red, los cuales estn al final del mismo, y de la aportacin de muchos usuarios en diversos foros, listas de correo y como no, de exploits. Es mi primer texto "en serio", adems es bastante largo, y aunque creo que no contiene errores muy graves, puede tenerlos :P cualquier fallo del texto, comentario, opinin, amenaza, donacin, oferta de trabajo, etc... al correo :) (abstenerse gilipolleces) Vamos al tema :) -== INTRODUCCION ==La teora sobre el tema la iremos viendo segn avance el documento, aunque antes de nada, haremos unas definiciones muy simples. La idea de dichas definiciones es saber "lo bsico", ya que este texto esta dirigido a iniciados, no a gente que ya domina el tema, aqu no vera nada nuevo, pero es imprescindible buscar por Internet mucha mas informacin (sobre todo lo referente a programar en C/C++ y ASM) - C/C++ Es un lenguaje de programacin muy extendido, multiplataforma, y fcil. Es la base de nuestros sistemas operativos(salvo cosas en ensamblador como rutinas de boot) y es tremendamente potente y optimizado. Sus archivos bsicos son *.c y *.cpp (para los C++). Es el lenguaje ms recomendable para aprender, el ms til. - Ensamblador (ASM) Es el lenguaje ms "bsico" que permite al programador interactuar con el CPU. Las instrucciones en ASM se pasan a binario, que es lo que "entiende" la CPU, es decir, 1s y 0s (aunque se agrupan en cadenas hexadecimales para mayor claridad). Realmente, un compilador ASM lo nico que hace es calcularte las etiquetas, los saltos y los calls, y "encapsular" el ejecutable. Todos los lenguajes de programacin, a la hora de compilar (obviamente, los lenguajes de script no), convierten su cdigo en instrucciones ASM. Instrucciones en ASM (Intel) son por ejemplo mov, push, pop, etc....(En AT&T,

seria popl, movl, pushl, etc..) Es un lenguaje de programacin difcil de aprender, solo para cosas puntuales o que requieran una gran optimizacin, pero saberlo te dar muchas alegras :) Cualquier informtico debera poder entender y dominar las instrucciones bsicas. - Debugger (Depurador) Un debugger es un programa que permite ir "paso a paso", instruccin a instruccin a otro programa. Al ir instruccin a instruccin, podemos ver completamente que esta pasando, los registros, la memoria, etc, as como muchas mas funciones muy interesantes. Su funcin principal es la de auditar cdigo, y ver el porque falla (o simplemente porque no realiza lo que queremos que haga), es una herramienta imprescindible para cualquier programador. Lo que pasa que tambin puede servir para otras cosas :) - Dissasembler (Desamblador) Un desamblador es un programa que te muestra el cdigo de un programa, una dll, lo que sea que este hecho de cdigo que el desamblador entienda. Normalmente, te muestra su cdigo en ASM (por ejemplo, un programa codeado en C, te muestra la conversin de dichas instrucciones C en ASM), aunque hay desambladores que permiten ver su cdigo (o parte de el) de programas hechos en JAVA o VBasic, por ejemplo. Normalmente, debugger y dissasembler van en el mismo programa, los mas usados son el Ollydbg (el que usare aqu), Softice, IDA, Win32dasm... - Hex Editor (Editor Hexadecimal) No hay que confundir un dissasembler con un hex editor. El primero te muestra el cdigo de un programa, el hex editor simplemente te muestra el contenido de un archivo, del tipo que sea, como un dumpeo hexadecimal y/o binario, as como la posibilidad de modificar y guardar dicho archivo. Se usa para rastrear y modificar archivos que usan programas, tanto para fines "de programacin" (el porque al cargar el archivo falla, el porque no se escribe bien, etc...) como de "hacking" o "cracking". A mi, personalmente, me gusta mucho el Hackman, pero se que hay mucho mejores :P Cuestin de buscar. - La CPU (microprocesador) La CPU es el "corazn" de un ordenador. Es la unidad de hardware encargada de ejecutar las instrucciones de un programa o sistema operativo, instruccin a instruccin, que estn en una determinada rea de memoria. Se ayuda de registros donde almacena variables, datos o direcciones. Una explicacin completa sobre el tema, requerira uno o varios libros, aunque googleando se encuentra muchsima informacin. - Registros de la CPU. La cpu (microprocesador) contiene una serie de registros, donde almacena variables, datos o direcciones de las operaciones que esta realizando en este momento. El lenguaje ASM se sirve de dichos registros como variables de los programas y rutinas, haciendo posible cualquier programa (de longitudes considerables, claro). Los ms interesantes son:

EIP Extended Instruction Pointer. El registro EIP siempre apunta a la siguiente direccin de memoria que el procesador debe ejecutar. La CPU se basa en secuencias de instrucciones, una detrs de la otra, salvo que dicha instruccin requiera un salto, una llamada...al producirse por ejemplo un "salto", EIP apuntara al valor del salto, ejecutando las instrucciones en la direccin que especificaba el salto. Si logramos que EIP contenga la direccin de memoria que queramos, podremos controlar la ejecucin del programa, si tambin controlamos lo que haya en esa direccin. EAX, EBX... ESI, EDI... Son registros multipropsito para usarlo segn el programa, se pueden usar de cualquier forma y para alojar cualquier direccin, variable o valor, aunque cada uno tiene funciones "especificas" segn las instrucciones ASM del programa: EAX: Registro acumulador. Cualquier instruccin de retorno, almacenara dicho valor en EAX. Tambin se usa para sumar valores a otros registros en funciones de suma, etc.... EBX Registro base. Se usa como "manejador" o "handler" de ficheros, de direcciones de memoria (para luego sumarles un offset) etc... ECX Registro contador. Se usa, por ejemplo, en instrucciones ASM loop como contador, cuando ECX llega a cero, el loop se acaba. EDX Registro direccin o puntero. Se usa para referenciar a direcciones de memoria mas el offset, combinado con registros de segmento (CS, SS, etc..) ESI y EDI Son registros anlogos a EDX, se pueden usar para guardar direcciones de memoria, offsets, etc.. CS, SS, ES y DS Son registros de segmento, suelen apuntar a una cierta seccin de la memoria. Se suelen usar Registro+Offset para direccionar a una direccin concreta de memoria. Los mas usados son CS, que apunta al segmento actual de direcciones que esta ejecutando EIP, SS, que apunta a la pila y DS, que apunta al segmento de datos actual. ES es "multipropsito", para lo mismo, referenciar direcciones de memoria, y un largo etc... ESP EBP Extended Stack Pointer y Extender Base Pointer. Ambos los veremos ms en profundidad cuando explique la pila. Sirven para manejar la pila, referenciando la "cima" (ESP) y la "base" (EBP). ESP siempre contiene la direccin del inicio de la pila (la cima) que esta

usando el programa o hilo (thread) en ese momento. Cada programa usara un espacio de la pila distinto, y cada hilo del programa tambin. EBP seala la direccin del final de la pila de ese programa o hilo. - Que es una vulnerabilidad? Una vulnerabilidad es un fallo que compromete la seguridad del programa o sistema. Aunque se le asocia tambin a "bug" (fallo), pero no es lo mismo. Un bug es un fallo de cualquier tipo, desde que un juego no funcione bien porque vaya lento, a un programa que funciona mal al intentar hacer una divisin por 0. Las vulnerabilidades son bugs de seguridad, que pueden comprometer el sistema o el programa, permitiendo al "hacker" ejecutar cdigo arbitrario, detener el sistema o aprovecharse del mismo para sacar cualquier tipo de beneficio. - Que es un exploit? Un exploit es un cdigo, un "mtodo", un programa, que realiza una accin contra un sistema o programa que tiene una vulnerabilidad, "explotndola", y sacando un beneficio de la misma. Dicho beneficio normalmente es la ejecucin de cdigo (dentro de ese programa, con los privilegios del mismo) que nos beneficia, dndonos por ejemplo una contrasea, o dndonos una shell de comandos, aadir un usuario administrador al sistema, o incluso lo nico que hacen es detener el servicio o el sistema, segn nuestros propsitos. Habra que distinguir entre exploits "completos" (los que estn completamente funcionales) y los POCs (proof of concept) que son exploits que demuestran que dicha vulnerabilidad existe y que es explotable, pero que no dan ningn beneficio o el beneficio es mnimo. Normalmente se usan estos ltimos para evitar el uso de los mismos por niatos (script kiddies) o para evitar gusanos (supongo que se acuerdan del blaster o del sasser, se liberaron los exploits completamente funcionales) - Que es una shellcode? Una shellcode es un cdigo bsico en ASM, muy corto generalmente, que ejecuta los comandos que queremos, como system("cmd.exe") (ejecuta una shell msdos en windows); o execv("/bin/sh") (ejecuta una shell sh en Linux/Unix), o sirve para aadir un usuario a la cuenta del sistema, para descargar un troyano y ejecutarlo, para dejar abierto un puerto conectado a una shell, etc.... Es el cdigo que ejecutara el programa vulnerable una vez tengamos su control. No es nada difcil de programar sabiendo ASM bsico y como funciona tu SO. Una vez programada en ASM (para testearla, por ejemplo, adems de que es mas fcil programarla en ASM que directamente con opcodes :P), se pasa a un string, compuesto por los opcodes (codigos de operacion, en hexadecimal) de dichas instrucciones ASM. Lo veremos mas adelante :) - Que es un overflow? Un overflow es, bsicamente, cuando resguardamos espacio de memoria insuficiente para una variable (allocate), y le introducimos ms datos a dicha variable de los que puede soportar. La variable "desborda", y los datos que no caben sobrescriben memoria continua a dicha variable. Si declaramos una variable que solo debe soportar 8bytes, si le movemos 10bytes, los 2bytes restantes no se pierden, sino que sobrescriben la memoria contigua a dicha variable. Hay distintos tipos de overflow, stack overflow (el que veremos aqu, tambin llamado buffer overflow, o desbordamiento de buffer, etc...), heap overflow (ya lo veremos en algn otro texto, se refiere a desbordar una variable declarada en el heap en vez de en la pila...), format string overflow (bugs de formato de las

cadenas de texto), integer overflow (debidos a declaraciones de variables con un espacio mnimo o negativo que proveemos nosotros...), etc... - Porque se le llama Stack Overflow? La pila (stack) es una estructura tipo LIFO, Last In, First Out, ultimo en entrar, primero en salir. Pensad en una pila de libros, solo puedes aadir y quitar libros por la "cima" de la pila, por donde los aades. El libro de mas "abajo", ser el ultimo en salir, cuando se vace la pila. Si tratas de quitar uno del medio, se puede desmoronar. Bien, pues el SO (tanto Windows como Linux, como los Unix o los Macs) se basa en una pila para manejar las variables locales de un programa, los retornos (rets) de las llamadas a una funcin (calls), las estructuras de excepciones (SEH, en Windows), argumentos, variables de entorno, etc... Por ejemplo, para llamar a una funcin cualquiera, que necesite dos argumentos, se mete primero el argumento 2 en la pila del sistema, luego el argumento 1, y luego se llama a la funcin. Si el sistema quiere hacer una suma (5+2), primero introduce el 2 argumento en la pila (el 2), luego el 1 argumento (el 5) y luego llama a la funcin suma. Bien, una "llamada" a una funcin o direccin de memoria, se hace con la instruccin ASM Call. Call direccin (llamar a la direccin) call registro (llama a lo que contenga ese registro). El registro EIP recoge dicha direccin, y la siguiente instruccin a ejecutar esta en dicha direccin, hemos "saltado" a esa direccin. Pero antes, el sistema debe saber que hacer debe seguir ejecutando cdigo. El programa puede llamara la funcin suma, multiplicacin, o simplemente mostrarlo por saber por donde seguir la ejecucin una vez cuando termine la funcin, por donde pero con el resultado, hacer una pantalla. Es decir, la CPU debe terminada la funcin suma.

Para eso sirve la pila :) Justo al ejecutar el call, se GUARDA la direccin de la siguiente instruccin en la pila. Esa instruccin se denomina normalmente RET o RET ADDRESS, direccin de "retorno" al programa principal (o a lo que sea). Entonces, el call se ejecuta, se guarda la direccin, coge los argumentos de la suma, se produce la suma y, como esta guardada la direccin por donde iba el programa, VUELVE (RETORNA) a la direccin de memoria que haba guardada en la pila (el ret), es decir, a la direccin siguiente del call. Vamos a verlo por pasos :) 1 Llegamos al call (EIP apunta a la instruccin call) 2 Se ejecuta el call. EIP apunta a la instruccin del call, es decir, donde debemos ir) 3 Se guarda la siguiente instruccin despus del call en la pila (el ret) En ese momento, ESP | ESP +4bytes | ESP +8bytes | la pila esta as: RET ADDRESS | EBP -8bytes argumento 1 | EBP -4bytes argumento 2 | 41414141 :) Esto lo podemos ver mucho mejor en un debugger, como el Ollydbg (en www.elhacker.net lo encontraras fcilmente, o en su pagina principal, googlead un poco) Usar un debugger, y mas el olly, es realmente fcil, no tiene ningn misterio. Si alguien se cree que es una herramienta para "elites" y sper difcil de usar, esta completamente equivocado. Bien, con el olly, cargamos el programa (File -> Open -> vuln1.exe). Veris que salen un montn de instrucciones en la ventana principal, con la direccin relativa de cdigo inicial de 00401000. Esta direccin es la direccin base del ejecutable en memoria (00400000, el 99% de los ejecutables se carga en esa direccin) mas el offset sealado en el PE header, que indica donde empieza el cdigo (entry point, en este caso el offset es +1000h). Tambin deberais ver a vuestra derecha, el estado de los registros de la CPU, EAX, EBX...ESI, EDI, EBP, ESP y EIP, y los valores que contienen. Abajo a la izquierda, deberais ver el dumpeo en hexadecimal, cosa que no usaremos, y abajo a la derecha, la pila (stack). Ah tenis que tener la vista casi fija :) Una vez cargado el ejecutable (se os abrir una ventanita de MS-DOS, pero que no sale nada, no os preocupis, el programa esta cargado en memoria, pero no se esta ejecutando aun),le metemos los argumentos (copiamos todas las AAAs que hay mas arriba en el texto, y nos vamos a Debug -> Arguments, y las copiamos ah). Os dir que tenemos que resetear el programa para que los argumentos tengan efecto (nos vamos a Debug-> Restart). Y listo :) Le damos a RUN (Debug -> Run F9) y.... Access violation when executing [41414141] Fijaros en el valor de EIP (ventana de los registros del CPU). EIP = 41414141 Ha tratado de ejecutar lo que hay en la direccin "AAAA" :) Vamos a ver esto un poco mas "pausado", para ver como funciona realmente. Hacemos un Restart (Debug->Restart) y vuelve el programa a su estado inicial (los argumentos siguen siendo las AAAs que metimos, no hay que cambiarlo). Esta vez vamos a poner un breakpoint en la funcin de strcpy, para ver en directo que esta pasando. Un breakpoint es un "punto de ruptura", que indica al debugger que cuando la ejecucin llegue ah (cuando el registro EIP seale la direccin de memoria donde hemos puesto el BP), se pare la ejecucin (NO SE EJECUTA LA INSTRUCCION SEALADA CON EL BP), para echar un vistazo, a ver que esta pasando :) Bajamos un poco por el cdigo, hasta que encontramos algo as:

004012CD |. 68 80124000

PUSH vuln1.00401280

; /format = ; \printf

"Introduzca un argumento al programa" 004012D2 |. E8 79170000 CALL 004012D7 |. 83C4 10 ADD ESP,10 004012DA |. EB 17 JMP SHORT vuln1.004012F3 004012DC |> 83EC 08 SUB ESP,8 004012DF |. 8B45 0C MOV EAX,DWORD PTR SS:[EBP+C] 004012E2 |. 83C0 04 ADD EAX,4 004012E5 |. FF30 PUSH DWORD PTR DS:[EAX] 004012E7 |. 8D45 B8 LEA EAX,DWORD PTR SS:[EBP-48] 004012EA |. 50 PUSH EAX 004012EB |. E8 50170000 CALL 004012F0 |. 83C4 10 ADD ESP,10

; ; ; ;

/src | |dest \strcpy

Os explico antes de nada, que es cada "cosa". 004012XX es la direccin relativa de memoria donde esta el ejecutable. Es decir, su direccin relativa en la RAM. Como ya he dicho, todos los ejecutables cargados en memoria, empiezan en 00400000+offset del entry point (que normalmente es 1000h, osea, el punto inicial en la memoria de inicio del cdigo del programa es 00401000h). EIP va cogiendo cada direccin, una detrs de otra, y la CPU ejecuta la instruccin contenida en esa direccin. 68 80124000 --> Son los "opcodes" de la instruccin ASM, mas o menos como decir que es la instruccin ASM convertida en hexadecimal (mas bien de binario 01010.. a hexadecimal, para que lo podamos comprender mucho mejor). Esto nos vendr bien para que hagamos nuestra shellcode :) PUSH vuln1.00401280 --> instrucciones en ASM, en este caso esta introduciendo en la pila la direccin del ejecutable (seccin .data) donde esta el string "Introduzca un..." Lo dems, es una "ayuda" del ollydbg, que te puede decir por ejemplo que estas introduciendo en la pila (format="Introduzca..."), o a que estas llamando (CALL ; \printf), etc.... Bien, el printf ese, es el cdigo que se ejecuta si no le metemos argumentos al programa, no nos tiene porque interesar (es el cdigo que se ejecuta cuando no metemos argumentos al programa) Pero si esto: 004012EB |. E8 50170000 004012F0 |. 83C4 10 CALL ADD ESP,10 ; \strcpy

Aqu se produce la llamada a la funcin vulnerable (llama a la DLL msvcrt.dll, donde esta la funcin C strcpy) y si os fijis, la siguiente direccin a ejecutar es 004012F0 ADD ESP,10. Cuando se produzca el Call strcpy, se pusheara en la pila 004012F0, que es la direccin de retorno (ret address). Para verlo, pondremos un breakpoint en la llamada a strcpy. Pulsis con el ratn en esa direccin, y pulsis F2. Se tendra que iluminar de rojo esa direccin. Pues tras ponerle el BP, le damos a RUN (F9) El programa se detiene antes de ejecutar esa instruccin (fijaos que ahora, aparte de rojo, aparece con un cuadro negro la direccin de memoria, significa

que esa es la siguiente direccin a ejecutar). Por si no nos ha quedado claro, EIP marca precisamente esa direccin, 004012EB Que hay en la pila? 0022FF00 0022FF28 |dest = 0022FF28 0022FF04 003D24A3 \src = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA (muchas AAAAAAAAs) AAAAAAAAAAAAAAAA" ESP apunta a 0022FF00, donde vemos el destino (0022FF28, que es la direccin de la variable buffer en la pila, mas "abajo", osea en direcciones mas altas). Y "src" (source -> fuente) es lo que vamos a copiar en el destino, 0022FF28. Esta referenciado por 003D24A3, que precisamente es la direccin de argv[1], donde comienza la cadena "AAAAA....". Sigamos. Que hay en 0022FF28? Pues espacio "reservado" para la variable buffer. Sin embargo, vemos algo as: 0022FF28 |FFFFFFFF