[Link]
com/clases/indice/id/315/classId/1180
Vulnerabilidades
Las vulnerabilidades son la piedra angular de la seguridad, puesto que suponen
el origen del que derivan numerosos fallos de seguridad. Una vulnerabilidad en
un programa informático o software es simplemente un error, un problema en su
código o en su configuración.
Es muy probable –por no decir que se produce siempre– que los programas
contengan errores, puesto que han sido creados por seres humanos. Esto es
especialmente frecuente en el caso de las aplicaciones muy complejas (como por
ejemplo, un sistema operativo), que tienden a contener errores de manera
exponencial.
La peculiaridad que convierte un simple fallo en una vulnerabilidad es la
posibilidad de que el "abuso inteligente" de este defecto pudiera llevar a un
riesgo de seguridad que compromete todo el sistema sobre el que se ejecuta esa
aplicación.
En las siguientes páginas analizaremos los aspectos básicos de las
vulnerabilidades:
Por qué ocurren y Cómo gestionarlas.
Qué es una vulnerabilidad
Una vulnerabilidad es un fallo en un programa o sistema informático. Pero no
cualquiera, sino un fallo de seguridad. Es necesaria esta distinción puesto que no
todos los errores de programación derivan en fallos de seguridad. Un error en un
programa puede llevar a que no funcione correctamente o que su
comportamiento no sea el esperado, pero no todos estos tipos de problemas
pueden considerarse fallos de seguridad.
Según la capacidad de aprovecharse de este defecto, la vulnerabilidad será más
o menos grave.
Una vulnerabilidad se define, básicamente, por cinco factores o parámetros que
deben identificarla.
Producto
Para definir una vulnerabilidad, lo primero que es necesario conocer es a qué
productos afecta. Dentro de un mismo programa, incluso, puede afectar a una
sola versión, a toda una rama o incluso a programas totalmente diferentes que
compartan un mismo fallo.
Este último supuesto ocurre cuando la aplicación afectada por la vulnerabilidad
reside en sistemas operativos diferentes. Por ejemplo, todas las distribuciones
de Linux comparten una buena cantidad de software, entre ellos, el propio
núcleo del sistema. Una sola vulnerabilidad en el kernel puede afectar a todas
las distribuciones que lo utilicen, desde Debian a Mandriva pasando por
OpenSuSE.
Para identificar correctamente una vulnerabilidad debe especificarse a qué
productos o versiones de productos afecta concretamente.
Dónde
Dentro de un mismo programa, una vulnerabilidad se localiza habitualmente en
1 de 7 11/11/2013 12:38 p.m.
[Link]
un componente o módulo. Los programas suelen componerse de varios módulos
que interactúan entre sí. Una vulnerabilidad puede encontrarse en un módulo
concreto del programa o bien por utilizar una configuración concreta. Por
ejemplo, puede existir una vulnerabilidad en el módulo de interpretación de
ficheros en formato RTF en Microsoft Word sin afectar al módulo que procesa
otro tipo de ficheros. O en el módulo de procesado de ficheros MP3 en el
programa de reproducción Winamp. Es posible que la vulnerabilidad no pueda
ser aprovechada si este módulo no se encuentra activo. Por ejemplo, el módulo
de procesamiento de JavaScript en documentos PDF no se encuentra activo por
defecto en Adobe Reader.
Si, por el contrario, el fallo se encuentra en un componente intrínseco al
programa, no existe posibilidad de deshabilitar componentes. Esto puede ocurrir
por ejemplo si se encuentra un fallo en el explorador de Windows o en su propio
núcleo.
Causa y consecuencia
¿Cuál es el origen del problema? ¿En qué falló su programador? Esto se refiere al
fallo técnico concreto que cometió el programador a la hora de crear la
aplicación que es el origen de la vulnerabilidad. Por ejemplo, puede que no
comprobarse bien qué valores alojaba una variable, los límites de la memoria, o
que olvidara establecer unos permisos adecuados a unos ficheros.
Las consecuencias técnicas de estos fallos suelen ser diferentes. Desde el
desbordamiento de memoria hasta el consumo excesivo de memoria. Estas
consecuencias suelen ser siempre las mismas derivadas de los mismos
descuidos, y es lo que buscarán los cazadores de vulnerabilidades.
Un ejemplo claro es, que una falta de comprobación de caracteres en una
aplicación web (causa), lleve a una posible inyección SQL (consecuencia). Otro
ejemplo: Una falta de comprobación de límites en una variable (causa), puede
llevar a un desbordamiento de memoria intermedia (consecuencia). Un último
ejemplo: Un descuido a la hora de establecer los permisos de un servicio en
Windows (causa) puede llevar a un salto de restricciones (consecuencia).
Normalmente la causa de una vulnerabilidad es un fallo técnico de
programación, una falta de comprobación que permite que se den circunstancias
indeseadas en el código durante su ejecución.
Impacto
El impacto es lo que puede conseguir un atacante que aprovechase la
vulnerabilidad. Por ejemplo, si existe un desbordamiento de memoria
intermedia, es posible que el atacante pueda conseguir ejecutar código. Si la
consecuencia de la vulnerabilidad es que el programa comienza a consumir
recursos, es posible que el atacante pueda llegar a conseguir una denegación de
servicio (hacer que el programa deje de responder). Si no se han comprobado
bien los permisos del programa (causa) puede que se produzca un salto de
restricciones (consecuencia) y que el atacante consiga elevar privilegios en el
sistema (impacto).
El impacto define en gran medida la gravedad de la vulnerabilidad. La ejecución
de código arbitrario supone la mayor gravedad puesto que significa que el
atacante podrá ejecutar cualquier programa en el sistema de su víctima. Por
tanto, podría realizar cualquier acción. En estos casos, se dice que el sistema
queda "comprometido" porque ha quedado en manos de la voluntad de un
tercero.
Vector
2 de 7 11/11/2013 12:38 p.m.
[Link]
A la forma que tiene el atacante de aprovechar la vulnerabilidad se le conoce
como “vector de ataque”. Un vector de ataque común es el envío de información
especialmente manipulada a un puerto concreto del sistema. Otra forma de
conseguir aprovechar una vulnerabilidad es creando un fichero manipulado que
será procesado por ese programa.
Por ejemplo, si se encuentra una vulnerabilidad en Word, es muy probable que
el vector de ataque sea un archivo en formato .doc que aproveche la
vulnerabilidad. Si la víctima lo procesa con un Word vulnerable, el atacante
conseguirá el impacto deseado.
Otros vectores de ataque pueden ser muy sencillos de llevar a cabo: como hacer
que la víctima visite un enlace. Por ejemplo, muchas de las vulnerabilidades
encontradas en los navegadores son aprovechadas por atacante creando una
página web adulterada que, al ser visitada con el navegador vulnerable,
aprovecha la vulnerabilidad. Por tanto, enviar un enlace a la potencial víctima,
sería el vector de ataque en este caso, y el impacto, podría ser la ejecución de
código.
Llegados este punto, pongamos un ejemplo concreto para unir todos estos
parámetros que definen una vulnerabilidad.
La aplicación de diseño gráfico MSPaint necesita conocer el tamaño de la imagen
que va a abrir antes de procesarla. Para conseguir esto, [Link] se ayuda
de fichero llamado image_size.dll que está especializado en esta función.
El programador de la aplicación MSPaint 7 omitió la comprobación correcta de un
parámetro que define la longitud que debe tener una imagen, en la función
SetImageSize del fichero image_size.dll. Así pues, como no se comprueba que el
valor del parámetro coincida con el tamaño real de la imagen, se produce un
desbordamiento de memoria.
Explicado de forma sencilla: la memoria del ordenador llega a sitios donde no
debería.
Por tanto, un atacante puede retocar la cabecera de una imagen para modificar
su atributo de tamaño, añadir a esa imagen qué código quiere ejecutar y
aprovecharse de la vulnerabilidad.
Vamos a extraer la información necesaria que define la vulnerabilidad:
Producto y versión: MSPaint 7 es la versión afectada.
Causa / Consecuencia: Se omite la comprobación del parámetro tamaño
(causa) y se produce un desbordamiento de memoria intermedia
(consecuencia)
Dónde / Módulo: En la función SetImageSize del componente
image_size.dll. Es una librería en la que se apoya [Link].
Impacto: Lo que podría conseguir un atacante es ejecutar código
arbitrario. O sea, ejecutar cualquier programa que desee: malware, virus,
etc.
Vector: ¿Cómo puede un atacante explotarla? Un atacante debería enviar
un archivo de imagen manipulado (con el código que quiere ejecutar
incrustado en su interior, y el parámetro incorrecto retocado) a la víctima,
y ésta abrirla con la versión de MSPaint vulnerable. En ese momento se
ejecutaría el código arbitrario con el que se ha manipulado la imagen y la
víctima quedaría comprometida.
Gestión de Vulnerabilidades
Las vulnerabilidades son difíciles de gestionar. Se descubren decenas día a día, y
clasificarlas es una tarea compleja. Para ello, la organización MITRE
([Link]) – organismo con más de 40 años de experiencia en el mundo de la
investigación y las tecnologías que está especializado en la prestación de
3 de 7 11/11/2013 12:38 p.m.
[Link]
servicios al Gobierno de EEUU – creó un sistema, imprescindible hoy en día para
estandarizar las vulnerabilidades, para poder hacer referenciarlas y conocer su
gravedad de forma objetiva. Además, este permite responder, siempre que sea
posible, a todas los parámetros que definen la vulnerabilidad.
CVE (Common Vulnerabilities and Exposures)
El CVE es un estándar (administrado por MITRE que se encarga de identificar
unívocamente a las vulnerabilidades. Se puede decir que es DNI de una
vulnerabilidad.
Su formato es el siguiente:
CVE-2011-1234
CVE, seguido del año en el que se asignó el código a la vulnerabilidad, seguido
de un número de cuatro cifras. Los grandes fabricantes normalmente toman
lotes de CVE válidos pero no usados, que MITRE les adjudica. A medida que van
encontrado vulnerabilidades, se los van asignando. Con los creadores de
software más pequeños, el propio MITRE se encarga de dicha asignación a
medida que se descubren vulnerabilidades.
El CVE ha tenido gran aceptación entre todos los fabricantes porque la mayor
parte de las veces es muy complejo saber a qué vulnerabilidad nos estamos
refiriendo solo por ciertas características. Es necesario disponer de una especie
de número de identidad único para cada fallo, puesto que en ocasiones las
vulnerabilidades son tan parecidas entre sí, tan complejas o se ha ofrecido tan
poca información sobre ellas que la única forma de diferenciar las
vulnerabilidades es por su CVE.
CVSS (Common Vulnerability Scoring System)
El CVSS es también un estándar que gradúa la severidad de manera estricta a
través de fórmulas establecidas. De esta forma los administradores o usuarios
pueden conocer de manera objetiva (a través de un número) la gravedad de los
fallos que afectan a sus sistemas. Esto permite dar prioridad a la hora de
parchear.
CVSS clasifica la facilidad de aprovechar el fallo y el impacto del problema
teniendo en cuenta los tres pilares de la seguridad de la información: qué nivel
de compromiso de la confidencialidad, integridad y disponibilidad de los datos se
podrían obtener aprovechando la vulnerabilidad.
Para calcular la puntuación base de una vulnerabilidad se toman nueve
parámetros, divididos en tres grupos de tres parámetros cada uno. Los tres
grupos principales son:
Explotabilidad, Impacto y Temporal.
Explotabilidad define cuán complicado puede llegar a ser para un atacante
aprovechar el fallo. Cuenta a su vez con tres parámetros, Vector,
Complejidad y nivel de Autenticación. Cada uno a su vez con varios
valores posibles.
El grupo Impacto define qué grado de acceso a los datos podría obtener el
atacante. Se define a través de tres valores a su vez, confidencialidad: (si
el atacante puede leer aquello que no debería). Integridad (si el atacante
puede escribir o modificar aquello que no debería) y Disponibilidad (si el
servicio o máquina puede seguir funcionando después de ser atacado).
El grupo Temporal cuenta también con tres parámetros. Mide ciertas
propiedades que influyen en cómo se percibe la vulnerabilidad a lo largo
4 de 7 11/11/2013 12:38 p.m.
[Link]
del tiempo y por tanto, son susceptibles de cambios. Explotabilidad: Indica
si es sencillo aprovechar la vulnerabilidad. Nivel de Remedio: Si existe o no
existe una solución y Confianza de la información: O sea, si la información
sobre la vulnerabilidad es oficial o no.
CVRF (Common Vulnerability Reporting Framework)
Finalmente, el CVRF se trata de un estándar reciente, que pretende dar
uniformidad a la forma en la que se avisa de vulnerabilidades de software a un
programador o compañía que crea un programa. Con este método se persigue
que, cuando un investigador o empresa cree haber encontrado un fallo de
seguridad en un programa, se le proporcione al fabricante la información precisa,
rigurosa y adecuada para que pueda confirmarlo, entenderlo y sobre todo,
parchearlo de forma eficaz.
Este estándar es muy reciente y su adopción es todavía limitada. Hoy en día,
cada investigador tiene su propio criterio a la hora de reportar una
vulnerabilidad, pero los datos que se proporcionan suelen ser los descritos más
arriba: causa, consecuencia, impacto, módulo, etc.
Fuente: INTECO
Desbordamiento de búfer
Un desbordamiento de buffer (del inglés buffer overflow o buffer overrun) es un
error de software que se produce cuando un programa no controla
adecuadamente la cantidad de datos que se copian sobre un área de memoria
reservada a tal efecto (buffer), de forma que si dicha cantidad es superior a la
capacidad preasignada los bytes sobrantes se almacenan en zonas de memoria
adyacentes, sobrescribiendo su contenido original. Esto constituye un fallo de
programación.
En las arquitecturas comunes de computadoras no existe separación entre las
zonas de memoria dedicadas a datos y las dedicadas a programa, por lo que los
bytes que desbordan el buffer podrían grabarse donde antes había instrucciones,
lo que implicaría la posibilidad de alterar el flujo del programa, llevándole a
realizar operaciones imprevistas por el programador original. Esto es lo que se
conoce como una vulnerabilidad.
Una vulnerabilidad puede ser aprovechada por un usuario malintencionado para
influir en el funcionamiento del sistema. En algunos casos el resultado es la
capacidad de conseguir cierto nivel de control saltándose las limitaciones de
seguridad habituales. Si el programa con el error en cuestión tiene privilegios
especiales constituye en un fallo grave de seguridad.
Se denomina shellcode al código ejecutable especialmente preparado que se
copia al host objeto del ataque para obtener los privilegios del programa
vulnerable.
La capacidad de los procesadores modernos para marcar zonas de memoria
como protegidas puede usarse para aminorar el problema. Si se produce un
intento de escritura en una zona de memoria protegida se genera una excepción
del sistema de acceso a memoria, seguido de la terminación del programa. Por
desgracia para que esta técnica sea efectiva los programadores han de indicar al
sistema operativo las zonas que se necesita proteger, programa a programa y
rutina a rutina, lo que supone un problema para todo el código heredado.
Descripción técnica
Un desbordamiento de búffer ocurre cuando los datos que se escriben en un
5 de 7 11/11/2013 12:38 p.m.
[Link]
búffer corrompen aquellos datos en direcciones de memoria adyacentes a los
destinados para el búffer, debido a una falta de validación de los datos de
entrada. Esto se da comúnmente al copiar cadenas de caracteres de un búffer a
otro.
Ejemplo básico
En este ejemplo, un programa tiene definidos dos elementos de datos continuos
en memoria: un buffer de 8 bytes tipo string, A, y otro de dos bytes tipo entero,
B. Al comienzo, A contiene bytes nulos y B contiene el número 3 (cada carácter
se representa mediante un byte).
0 0 0 0 0 0 0 0 0 3
Buffer Buffer
A B
A continuación, el programa intenta almacenar la cadena de caracteres
"demasiado" en el buffer A, seguido de bytes nulos para marcar el fin de string.
Al no validarse la longitud de la cadena, se sobrescribe el valor de B:
'd' 'e' 'm' 'a' 's' 'i' 'a' 'd' 'o' 0
Buffer Buffer
A B
A pesar de que el programador no quería cambiar el contenido del búffer B, el
valor de éste ha sido reemplazado por un número equivalente a parte de la
cadena de caracteres. Para este ejemplo, en un sistema big-endian que use
ASCII, el carácter 'o' seguido del byte nulo equivale al número 28416.
Si B fuese la única variable aparte de A definida en el programa, la escritura de
datos que sobrepasen los límites de B generarían un error como segmentation
fault, concluyendo así el programa.
Código fuente de ejemplo
En el siguiente ejemplo se presenta un código fuente en C con un error de
programación. Una vez compilado, el programa generará un desbordamiento de
buffer si se lo invoca desde la línea de comandos con un argumento lo
suficientemente grande, pues este argumento se usa para llenar un buffer, sin
validar previamente su longitud.
/* overflow.c - demuestra un desbordamiento de buffer */
#include <stdio.h>
#include <string.h>
int main(int argc, char *argv[])
{
char buffer[10];
if (argc < 2)
{
fprintf(stderr, "MODO DE USO: %s stringn", argv[0]);
6 de 7 11/11/2013 12:38 p.m.
[Link]
return 1;
}
strcpy(buffer, argv[1]);
return 0;
}
Strings de 9 caracteres o menos no provocarán desbordamiento de buffer. Por el
contrario, strings de 10 caracteres o más, sí: Esto siempre es incorrecto, aunque
no siempre resultará en un error del programa o segmentation fault.
Este programa puede reescribirse en forma más segura usando la función
strncpy de la siguiente manera:
/* mejor.c - demuestra un método de resolver el problema */
#include <stdio.h>
#include <string.h>
int main(int argc, char *argv[])
{
char buffer[10];
if (argc < 2)
{
fprintf(stderr, "MODO DE USO: %s stringn", argv[0]);
return 1;
}
strncpy(buffer, argv[1], sizeof(buffer));
buffer[sizeof(buffer) - 1] = '';
return 0;
}
Stack smashing
El pisado de pila o stack smashing es un tipo de desbordamiento de buffer que
es aprovechado por algunos virus y otros programas maliciosos para tomar
control sobre una aplicación, o provocar su terminación.
Esto sucede cuando, por algún error imprevisto, se ingresa a la pila de la
aplicación más datos que los que ésta puede contener, lo que provoca que esta
se "desborde" y algunos datos se sobreescriban. Para evitar que suceda esto, los
compiladores se mejoran cada día dejándole tiempo al programador para pensar
en lo que realmente importa.
Fuente: Wikipedia
7 de 7 11/11/2013 12:38 p.m.