Mostrando entradas con la etiqueta C. Mostrar todas las entradas
Mostrando entradas con la etiqueta C. Mostrar todas las entradas

viernes, marzo 04, 2022

Cómo crear un bug de "Buffer Overflow" para crear su exploit y aprender

La ciberseguridad sigue su auge y la necesidad, y demanda, formativa es muy grande. La transversalidad de la ciberseguridad en cualquier paradigma tecnológico hace que su necesidad crezca y que la formación en perfiles de ciberseguridad sea muy demandada por las empresas. Esto daría para un artículo y hablar de esta necesidad y de las necesidades que tienen que cubrir las empresas, pero el artículo de hoy va más ligado a la parte técnica y al cómo crearse entornos para practicar lo estudiado, para practicar e interiorizar los conocimientos que uno va aprendiendo.

Figura 1: Cómo crear un bug de "Buffer Overflow" para crear su exploit y aprender

Una de las cosas más importantes es poder probar lo que uno estudia y para ello, el artículo de hoy, muestra cómo crearnos un pequeño escenario para estudiar y entender el Buffer Overflow. Hay muchos recursos en Internet que explican muy bien la teoría del desbordamiento de un buffer, es más, hay muchos que nos enseñan la teoría y nos dan ejemplos prácticos. Todo esto es fantástico y debemos valorarlo. No vale con ver cómo otros lo hacen en una clase. Debemos esforzarnos en entenderlo a bajo nivel, a pegarme con ello y una de esas posibilidades las obtengo cuando tengo que montarme el escenario.

Figura 2: Linux Exploiting. Un libro para aprender a explotar bugs
y hacer exploits funcionales .de 0xword.

Es como si quiero practicas técnicas de pivoting y me monto un laboratorio con máquinas virtuales, con diferentes redes y voy jugando con las diferentes formas de hacer pivoting. Esto me ayudará a profundizar mi aprendizaje, a encontrarme con problemas que el profesor no tuvo y a entender por qué suceden ciertas cosas y, sobre todo, interiorizar el conocimiento. Como digo esto nos daría para un artículo sobre la cultura del esfuerzo y la dedicación que es algo que en parte se está perdiendo y sustituyendo por la cultura de la inmediatez. 

Figura 3: RootedLab de Practical Pentesting

El entrenamiento es algo fundamental y eso es lo que se verá en el Rooted Lab sobre entrenamiento a través de la resolución de escenarios. El próximo 9 de marzo un día completo de pentesting práctico a través de la resolución de escenarios de todo tipo.

Creando un programa en C

En primer lugar, vamos a crear una aplicación vulnerable a Buffer Overflow. El objetivo no es aprender en este post a detectar un buffer overflow y explotarlo, el objetivo es mostrar cómo se crea un escenario donde uno puede profundizar e ir entrando en materia sobre la teoría que nos puedan impartir. Lo primero es mostrar el código del programa en pseudocódigo y ver la idea que se tiene:
  • El programa escrito en C recibirá un argumento (argv[1]) el cual será la ristra de bytes que queremos pasar a un buffer y provocar el buffer overflow.
  • Una vez recibimos la entrada, la cual es un string la vamos a convertir en hexadecimal (eso sí, siguen siendo bytes).
  • - Una vez que lo tenemos en hexadecimal aplicamos la función strcpy(b, buffer) dónde buffer almacena la ristra de bytes en hexadecimal de la entrada al programa y la variable b será el destino. La variable buffer será más grande que la variable b de modo que provocaremos el overflow al utilizar strcpy. La función strcpy es una función insegura, la cual no debe utilizarse en programación segura.
La idea es sencilla y un ejemplo rápido sería:

Figura 4: Un código en C con la función strcpy

Este es el esqueleto de la aplicación. Se puede ver cómo podríamos ir jugando a mano introduciendo en la variable chrs posibles valores para ir detectando el desbordamiento. Por ejemplo, si ponemos “a pelo” el valor chrs = “aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa” podríamos llegar a detectar el bug con el buffer overflow de forma sencilla. Eso sí, lo tendríamos "hardcodeado", luego necesitaríamos introducirlo como un argumento de la aplicación o, si montamos un ejemplo más avanzado, a través de un socket.

Figura 5: Ejemplo a través de un socket

El código para convertir el string a hexadecimal es sencillo, aunque en este caso lo que queremos es que la entrada a la aplicación ya venga en un string de hexadecimal, es decir, el string que recibirá la aplicación ya simula ser código hexadecimal, por lo que debe hacer nuestro código es convertirlo a hexadecimal que no dejará de ser un array de caracteres ‘unsigned’.

Probando y detectando overflow

Para este ejemplo sencillo y tras compilar el código, vamos a probar con un debbugger que el overflow está bien creado. Como debugger vamos a usar x64dbg, el cual es un debbugger Open Source. Para pasarle argumentos a nuestra aplicación vulnerable debemos hacerlo a través de la línea de comandos, como se puede ver en la siguiente imagen.

Figura 6: debugging overflow.exe con parámetro desbordante

Se puede ver un gran número de 41414141… para identificar el offset del overflow deberíamos usar un patrón, creado con mona o con pattern_create, pero no es el objetivo del artículo enseñar el buffer overflow, si no el de crear un entorno y ver qué funciona. Ahora, al arrancar el debbugger vemos que hay un desbordamiento y se ve reflejado en el registro EIP, tal y como se puede ver en la imagen.

Figura 7: Desbordamiento de Buffer en el debugger

Para ver cómo controlamos el EIP y estamos “machacando” el valor de retorno de la función strcpy del código en Lenguaje C, podemos modificar la entrada y meter las XA’ hasta llegar a la posición de la dirección de retorno de la función y le metemos 4 ‘B’. Se pueden ver reflejadas en la siguiente imagen, la aparición en el EIP de las 4 ‘B’ (\x42\x42\x42\x42).

Figura 8: Localización de las 'B' en el EIP

Revisamos sobre nuestro código en el debugger si podemos encontrar instrucciones del tipo JMP ESP o CALL ESP para poder redirigir el flujo de la ejecución hacia nuestro código. Después tendremos que meter el número de NOPs que creamos necesario, así como el código ejecutable o shellcode que queremos lanzar.

Figura 9:  Redirigir el retorno de strcpy a call ESP

Vamos ahora con la parte de la ejecución. En el buffer overflow básico el payload tendrá este aspecto:
  • bytes basura +
  • dirección RET modificada (apuntando a una instrucción de salto al ESP) +
  • NOPs
  • + Shellcode.
En la siguiente imagen, se puede ver toda la entrada en hexadecimal y cómo se pueden ver los N Bytes basura (\x41 = “A”) más la dirección utilizada para saltar de nuevo a la pila (76b19689) más 8 bytes con NOPs (\x90) y después el código de la shellcode. En este caso se ha creado un código que es una llamada a una calculadora.

Figura 10: Ejecución del payload

Si revisamos el escenario, este código funciona perfectamente en un entorno Windows 10. Esto es debido a que DEP no viene por defecto para binarios de 32 bits. Además, se ha compilado sin meter protecciones en la pila.

Figura 11: Ejecución de calculadora al abrir el debugger

Como podéis ver, es un ejercicio interesante para hacer y poder aprender más sobre el buffer overflow creando una pequeña aplicación en la que uno tiene vulnerabilidades para poder aprender con ellas. Un escenario didáctico creado para la formación, y si queréis aprender más, os recomiendo el libro de "Linux Exploiting" de 0xWord que trae muchos de estos ejemplos didácticos.

Saludos,

 Figura 12: Contactar con Pablo González

lunes, marzo 30, 2015

OSB-Rastreator: Cómo localizar automáticamente todos los bugs que tiene tu Ubuntu (o cualquier GNU/Linux)

Encontrar vulnerabilidades puede parecer más difícil hoy en día de lo que a priori puede llegar a ser. Este pensamiento es el que nos llevó a realizar una pequeña prueba de concepto de lo que podría hacer cualquiera de nosotros en su casa. Todo este mini proyecto nace de un tweet de mi compañero en Eleven Paths David Barroso en el que, tras los incidentes de seguridad que sufrió el pasado 2014 - y que parecen continuar en 2015 - el mundo del Open Source enunciaba:
“GNU has given a lot to the tech companies. Now it’s time to help GNU and audit many of its core components”. (@lostinsecurity).
Esto me hizo pensar en el gran número de aplicaciones Open Source que diariamente están expuestas en los repositorios. Una de las primeras preguntas que me hice fue, "¿Cuántos paquetes de software tenemos al alcance del comando apt-get install? ¿Este código será revisado en busca de vulnerabilidades?"

Figura 1: OSB-Rastreator: Cómo localizar automáticamente los bugs que tiene tu Ubuntu

Quise resolver la duda a mi primera pregunta realizando un simple ejercicio con apt-get. Para ello instale la última versión de Ubuntu, y ejecuté en una shell la instrucción: apt list | cut –d’/’ -f1 >> openSource.txt. Luego simplemente con un wc –l sobre el fichero podría ver el número de paquetes de software que, por defecto, tenemos accesibles desde Ubuntu. ¿Por qué Ubuntu? Está claro, es una de las distribuciones más utilizadas, si no la que más, en el mundo Linux. El resultado obtenido sobre el número de paquetes de software disponibles por defecto puede sorprender a más de uno. Cada usuario que instala Ubuntu tiene alrededor de 45.000 paquetes de software, en mi caso exactamente 45.494.

Figura 2: 45.494 paquetes disponibles en Ubuntu

A la pregunta de si el código es revisado puede haber varias opiniones, aunque por lo que parece tras un análisis muy básico probable la respuesta correcta es que "No lo suficiente". Ya en el pasado hemos visto estudios de hacking con buscadores sobre repositorios de código OpenSource utilizados para buscar vulnerabilidades de tipo SQL Injection, XSS o RFI, e incluso del tipo LDAP Injection. Solo con lanzar consultas adecuadas sobre el código fuente es posible sacar estos datos.

Creando OSB-Rastreator

Pensando sobre algunas vulnerabilidades clásicas, y que a día de hoy siguen apareciendo, llegué a la conclusión de que un buen punto de partida serían las funciones inseguras ampliamente conocidas. Aquí me surgió una nueva pregunta, ¿Seguirán utilizándose de manera masiva las funciones strcpy(), sprintf(), gets() o scanf(), entre otras? ¿Quién no vio su primer buffer overflow con un strcpy() o alguna de las mencionadas anteriormente? Esto fue el origen de la creación de OSB-Rastreator.

Pensando sobre esto quise buscar estas funciones, pensando que encontraría una cantidad ridícula de software con estas funciones en su código. Pensando un poco la mayoría del código que podemos encontrar en los repositorios de Linux será código en Lenguaje C, pero aun así seguía pensando que estas funciones potencialmente inseguras no tendrán mucho rastro por los repositorios, ¿o quizá sí? El siguiente paso era conseguir, de la manera más sencilla, y que cualquiera en su casa pudiera realizar, descargar todo el código fuente de los más de 45.000 paquetes de software. Para llevar a cabo la descarga de todos los paquetes de software se implementó un script en bash con el siguiente funcionamiento:
1. Leer paquete del fichero openSource.txt
2. Descargar paquete
3. Descomprimir tar.gz
4. Búsqueda de código C
5. Búsqueda de función insegura
6. Eliminar paquete (tar.gz y código descomprimido)
En la siguiente imagen se puede ver parte del código de descarga de los paquetes de software, algo muy sencillo y que automatizaba todo el proceso de obtención del código de los repositorios.

Figura 3: Descarga de paquetes y búsqueda de funciones inseguras en códigos C

Este código era la primera prueba de concepto para ver si el proceso era viable en un tiempo razonable. Decidí utilizar una máquina en la nube para llevar a cabo el proceso. Por cada paquete de software del que descargábamos el código fuente, hay que entender que podríamos obtener N ficheros de código en Lenguaje C, y cada fichero debía ser analizado buscando las funciones inseguras. En este punto aparte de pensar en las funciones inseguras recordé grandes “cagadas” de algunos developers a la hora de programar y comentar el código, por lo que pensé, "¿Qué puedo encontrar en los comentarios?" De esta forma decidí que también los comentarios debían ser buscados en esta primera pasada.

Figura 4: Resultados de búsqueda de funciones inseguras por el script

El fichero denominado down.sh tardó más de 8 días en realizar el proceso de descarga y búsqueda de información interesante en el código. ¿Qué ocurría cuando se encontraba una función insegura o un comentario? Pensando en que encontraría pocas cosas para su posterior análisis, decidí almacenar por carpetas las funciones inseguras. Es decir, el script encontraba un strcpy() y almacenaba dentro de la carpeta strcpy un fichero con el nombre de la aplicación y en el interior del fichero los ficheros de Lenguaje C pertenecientes al paquete de software, con la línea dónde se encontraba la función.

Figura 5: Un strcpy en Hydra 7.5

El script puede funcionar mucho más rápido si se distribuye, pero en este caso el tiempo no era importante para la prueba de concepto. En este momento y teniendo los resultados en la mano me quedé sorprendido. Estos son los resultados:
• La función strcpy() tenía 9567 ocurrencias, es decir, fue identificada en ese número de paquetes de software.
• La función gets() tenía 607 ocurrencias.
• La función scanf() tenía 1257 ocurrencias.
• La función sprintf() tenía 9426 ocurrencias.
• Por último, fueron identificados 15647 paquetes de software con comentarios multilínea. En este caso, solo multilínea.
Realmente mi gozo en un pozo. Por un lado me sorprendió ver tanto software que utilizase funciones inseguras, lo que fue algo positivo para esta prueba de concepto. Por otro lado analizar todo esto iba a ser algo inviable, por falta de recursos y tiempo. Para poder ver en qué partes encontraríamos bugs sin que fuera una pérdida el tiempo, decidí analizar a mano algunas aplicaciones.

Analizando los resultados en busca de bugs

Una de las primeras en las que me fijé fue dnsmasq, y analizado las llamadas strcpy() observé una por encima del resto strcpy(ifr.ifr_name, argv[1]) en un fichero llamado dhcp_release.c. Esto tiene que ser vulnerable sí o sí, no puede haber ejemplo más claro. Tras comprobarlo, efectivamente, no se comprueba el límite que se introduce en argv[1] por lo que tendremos un desbordamiento.

Esto me volvió a hacer pensar y preferí automatizar el proceso de búsqueda de patrones “curiosos” o “potencialmente vulnerables” como son los argv[x] que podemos encontrar, o por ejemplo las variables de entorno que son pasadas sin validación a través de la función getenv() a un strcpy(). En la imagen se puede ver como ejecutando el script search.sh se busca dentro de los resultados el patrón que nosotros queramos. En este caso se busca el texto getenv sobre resultados de funciones inseguras, por ejemplo buscando llamadas a getenv() que se pasan directamente a strcpy().

Figura 6: Búsqueda de patrones en los resultados de funciones inseguras

Otra prueba que quise automatizar viendo el gran número de argv[x] que se pasaban a funciones como strcpy() fue la de instalar automáticamente las aplicaciones que contengan esto, que automáticamente se intente desbordar el buffer y, a posteriori, se desinstalasen las aplicaciones. La imagen dónde se puede ver la ejecución de este script se encuentra unificada, para que se pueda ver rápidamente la instalación, la provocación del fallo y la desinstalación. Este script fue denominado buffer.sh y un ejemplo de su ejecución es la siguiente:

Figura 7: Instalación, prueba y explotación de buffer overflow automáticamente

Se encontraron fallos muy sencillos de detectar, pero sorprendió el número de errores, por lo que se puede decir que NO hay un análisis básico de seguridad o de validación de parámetros en la publicación o subida de aplicaciones a los repositorios de forma automatizada. En función de qué permisos utilice una aplicación o con qué usuario se ejecute estos fallos pueden ser más o menos graves.

Antes de instalar un paquete analiza los bugs

Llegados a este punto pensé en cómo se puede ayudar al usuario, o que al menos esté notificado de la calidad del código que está instalando, y quizá una solución viable es la modificación de apt-get para que cuando un usuario ejecute la instrucción apt-get install se realice un análisis de las funciones de la aplicación mediante expresiones regulares. Al final el trabajo fue presentado en Hackron 2015, (sí, sé que puedo dar envidia por el carnaval vivido allí y que no dejo indiferente a nadie…) Dicho esto, he de decir que por falta de tiempo no me pegue con apt-get y realicé un script que podría ser invocado como alias de apt-get para llevar a cabo las instalaciones de paquetes de software.

Figura 8: La instalación de un paquete antes verifica las funciones inseguras que tiene

El script denominado apt-get-install.sh antes de llevar a cabo la instalación del software realiza una serie de comprobaciones en el código, basadas en lo que se automatizó con down.sh. En primer lugar descarga el código fuente, aplica las expresiones regulares que podemos configurar sobre los ficheros en Lenguaje C y en el instante que detecta alguna función no segura notifica al usuario antes de seguir con la instalación.

Figura 9: Antes de instalar muestra las funciones inseguras

El script te permite ver las líneas dónde se encuentran las funciones no seguras y te permite acceder al código completo por si se quiere hacer alguna comprobación manual. Por último, se solicita al usuario la confirmación de instalación al usuario.

Figura 10: El usuario decide si instalar o no el paquete sabiendo ya la información de las funciones inseguras

Este sistema me parece un buen método de sentido común, sobre todo en casos en los que se quiere realizar un proceso de fortificar un servidor Linux. Con estos avisos se tiene mucha más información a la hora de extender la superficie de exposición, y del riesgo en que se está incurriendo en cada nuevo paquete que se instala.

Personalizando la automatización de búsqueda de bugs

Por último hay que decir que, tal y como se comentó anteriormente, la personalización a la hora de realizar las búsquedas en los paquetes de software es algo imprescindible. Cada uno puede tener distintas intenciones, o puede realizar distintas búsquedas a través de diferentes expresiones regulares basándose en esta arquitectura. Puede ser que en un momento dado salga cierta vulnerabilidad conocida implementada de alguna forma y que añadiendo la expresión regular al sistema podamos localizar más software vulnerable en los repositorios de Linux para explotar algunos ejemplos concretos.

Figura 11: Expresiones regulares para buscar distintos tipos de bugs

Para añadir las expresiones regulares al sistema disponemos de un fichero denominado regexp.txt, dónde por cada línea se indica la carpeta dónde se almacenará y la expresión regular. Este punto es importante porque da mucha flexibilidad al script y las posibilidades ya dependen de lo que cada uno quiera encontrar.

Figura 12: Búsqueda de bugs con ficheros de expresiones regulares

En la imagen anterior se puede ver como se lanzará el script denominado down_customize.sh, siendo el primer parámetro el fichero de texto que tiene el nombre de todos los paquetes de software y el segundo parámetro el fichero dónde se encuentran las expresiones regulares de lo que queremos encontrar en el código fuente.

Resultados y un Exploit de Memory Corruption en Chemtool

Los paquetes de software disponibles por defecto en una instalación de Ubuntu son 45.494. Tras lanzar alguna pasada más con el script personalizado se obtuvieron 13.670 paquetes de software distinto que contenía la función strcpy() y ni mucho menos esto significa que sean vulnerables, habría que llevar a cabo un análisis. Manualmente es una locura, pero basándose en pequeños tricks, intuiciones y herramientas se pueden detectar nuevos bugs.

En 13.430 paquetes de software se encontraron funciones sprintf() que también son potencialmente inseguras. En 1.789 paquetes de software se encontró la función scanf(), mientras que en 973 paquetes de software se encontró la función gets(). Respecto a los comentarios en el código, prácticamente la mitad del código analizado contenía comentarios, lo cual no es malo, pero se podría utilizar el script search.sh para buscar patrones o cadenas clave como password, user, connection, etcétera.

Para la charla de Hackron 2015 se quiso demostrar que cualquier podría sacar bugs de manera sencilla con este sistema o cualquiera similar que una persona se puede montar en casa. Por esta razón una de las aplicaciones encontradas que tenía un bug fue notificada al autor de la misma. Tras esto a través de Exploit-DB se lanzó la vulnerabilidad en la aplicación Chemtool, la cual contenía Memory Corruption.

Figura 13: ChemTool

El crasheo de la aplicación era llevado a cabo de dos maneras, la primera y más sencilla por no controlar el parámetro de entrada argv[1] cuando la aplicación era lanzada desde una terminal. Tras ver esto, quise analizar cómo se comportaba la aplicación al pasarle un fichero con contenido no esperado. Para crear el fichero malicioso se utilizó un pequeño script en Ruby:
#/usr/bin/ruby
buf = "a"*3000
filename = "crash.png"
file = open(filename,'w')
file.write(buf)
file.close
puts "file created!"
¿Se podría ejecutar código arbitrario? Es bastante probable, aunque no he comprobado esta parte. Con OSB-Rastreator me basé en encontrar puntos débiles y bugs de aplicaciones que se encuentran expuestas por defecto a millones de usuarios de Ubuntu en el mundo. Un par de días antes del evento de Hackron decidí enviar el bug para que OSVBD la diera de alta, si ellos lo consideraban. Al día siguiente tenía disponible el ID que identificaba el bug y podría mostrarlo en la charla.

Figura 14: El bug en OSVBD

A partir de aquí se puede ver por Internet como distintos sitios se hacen eco de la vulnerabilidad, por ejemplo en 1337day Inj3ct0r. Esto ejemplifica que cualquiera con ideas y conceptos básicos puede llevar a cabo un proyecto que permita de manera masiva hacer un análisis básico y obtener resultados sorprendentes.

Figura 15: Bug de ChemTool

En conclusión, por defecto existe gran cantidad de software en los repositorios que distribuciones como Ubuntu proporcionan a los usuarios que son vulnerables y fácilmente accesibles a los usuarios. Lógicamente, si añadimos repositorios a sources.list el número de paquetes de software a analizar serían mayores, por lo que se podrá detectar un mayor número de vulnerabilidades.

Hoy día técnicas como el Pentesting Persistente que encontramos en servicios como Faast, ayudan a mejorar y detectar vulnerabilidades de forma temprana. Aunque el sistema propuesto aquí está pensado para analizar repositorios muchas de las aplicaciones que podemos encontrar nuestros sistemas Linux de Ubuntu, en dichos repositorios podrían estar los paquetes de servidores Web, FTP, SSH o, directamente, tener interacción remota. Continuaremos evolucionando esta idea.

Autor: Pablo González Pérez (@pablogonzalezpe), Project Manager en Eleven Paths
Escritor de los libros "Metasploit para Pentesters" y "Ethical Hacking"

viernes, enero 09, 2015

Un hackaton de fin de semana por 10.000 dólares

Queda ya menos de una semana para que termine el Concurso de Plugins de Latch, que tiene como fecha límite para entregar los plugins el próximo día 15 de Enero a las 13:00 horas. Como sabéis, el ganador de ese concurso se llevará 10.000 USD, pero el segundo y el tercer clasificado también tienen premios de 5.000 USD y 1.000 USD respectivamente, además de una beca de trabajo para estudiantes que quieran venirse 6 meses a trabajar con nosotros.

Figura 1: Participa en el concurso y gana los 10.000 USD

Toda la información del concurso, así como las bases del mismo están disponibles en la web de Latch Plugin Contest. Si necesitas ayuda, para programar con Latch tienes disponibles varios seminarios que enseñan cómo manejar el SDK de Latch para .NET, para Java o para PHP, con los que podrás hacer cualquier integración que necesites en estas tecnologías, pero recuerda que tienes SDK para C, PowerShell, Ruby, Python y hasta NodeJS.
- Integrar aplicaciones .NET con Latch
- Integrar aplicaciones Java con Latch
- Integrar aplicaciones PHP con Latch
Quedan solo 6 días, pero por medio tienes un fin de semana que puedes utilizar para dedicarlo en modo Hackaton a llevarte la pasta. 

Figura 2: Herramientas, SDKs y plugins disponibles para que los uses

No merece la pena dedicarle este fin de semana a un proyecto chulo y ganar 10.000 USD? Monta un grupo con tus amigos, haz la integración más chula que puedas durante el fin de semana, e intenta llevarte el premio, que además de aprender y pasártelo bien tendrás la satisfacción de haber hecho algo distinto.

Saludos Malignos!

sábado, febrero 08, 2014

Explotar un 0day de Format Strings en mp3blaster para Linux. ¿Vulnerabilidades del pasado?

Hay días en que apetece leer código, mucho código, y dar rienda suelta a la imaginación entre los fuentes de alguna aplicación en busca de fallos de seguridad. Después de pasar 2 horas revisando aplicaciones aleatorias, me tropiezo con mp3blaster, un reproductor de mp3 basado en consola (ncurses). Ya que cualquier día parece bueno para encontrar un format string, instalo la última versión (la 3.2.5) en Ubuntu 13.10 (sudo apt­get install mp3blaster) y me encuentro con una evidente vulnerabilidad en la siguiente función del fichero main.cc.
void
warning(const char *txt, ... )
{

va_list ap;
char buf[1025];
mw_clear();
move(LINES­2,1);
attrset(COLOR_PAIR(CP_ERROR)|A_BOLD);
va_start(ap, txt);
vsnprintf(buf, 1024, txt, ap);
va_end(ap);
addnstr(buf, (COLS > 14 ? COLS ­ 14 : 1));
attrset(COLOR_PAIR(CP_DEFAULT)|A_NORMAL);
refresh();
}
La función vsnprintf() utiliza el parámetro txt como cadena de formato. warning() es llamada desde fw_convmp3(), una función que se invoca cuando el usuario quiere convertir un fichero MP3 a formato WAV:
void
fw_convmp3(const char *tmp, void *args)
{

char **selitems;
...
...
if (!(decoder = new Mpegfileplayer(Fileplayer::AUDIODRV_OSS)) ||
                       !decoder­>openfile(file,
                       file2write, WAV) || !decoder­>initialize(NULL))
     {

sprintf(bla, "Decoding of %s failed.", selitems[i]);
warning(bla);
selitems[i] contiene el nombre del fichero que el usuario desea convertir. El bug puede reproducirse de la siguiente manera: se crea un fichero con nombre “%n.mp3” en blanco, y se pulsa F1 dentro de mp3blaster para seleccionarlo, luego se pulsa F6 para invocar la función de conversión, el programa le solicitará un directorio para guardar el fichero WAV convertido, se pulsa ENTER et voilà!

Figura 1: Se seleccionar el fichero que explota el bug
Figura 2: Cuando se trata de guardar con F6 se produce el bug

Como se ha observado en el último fragmento de código, el fichero mp3 tiene que ser un archivo inválido, puesto que la llamada a warning() sólo se ejecuta cuando la conversión de MP3 a WAV falla. En caso contrario, es decir, con una canción mp3 normal pero con el nombre manipulado se ejecutarían las siguientes instrucciones:
sprintf(bla, "Converting '%s' to wavefile, please wait.", selitems[i]);
mw_settxt(bla);
...que no conducen al punto que nos interesa.

La vulnerabilidad puede ser explotada fácilmente (permitiendo una sobrescritura arbitraria de la memoria) en sistemas y versiones de GCC que no compilen el código con la directiva de seguridad FORTIFY_SOURCE aplicada. Observa en la siguiente ilustración cómo pueden volcarse valores de la memoria con un nombre particularmente diseñado:

Figura 3: Explotando el bug

Es más, para los incrédulos y aquellos que como yo pensaron al principio que el set de caracteres limitados para el nombre de un archivo no permitiría el control del flujo del programa, muestro aquí una prueba de concepto en la que genero un fichero mediante touch y perl en la línea de comandos, con una cadena de formato especialmente manipulada para redirigir el registro EIP de un sistema de 32 bits hacia la famosa dirección 0xdeadbeef.

El PoC escribe los valores adecuados en la dirección de la entrada free() presente en la GOT de la aplicación vulnerable. (Incluso puede verse el archivo generado en la carpeta, en la que se detecta una codificación errónea).

Figura 4: Obteniendo el control del programa

Con lo que obtenemos el control total del programa y por lo tanto la capacidad de ejecutar código arbitrario. El libro Linux Exploiting publicado por 0xWord contiene un capítulo completo dedicado a la explotación de vulnerabilidades de cadena de formato y otro apartado interesante que describe cómo otros exploiters en el pasado han utilizado un bypass para sortear la protección FORTIFY_SOURCE en programas como sudo.

Por lo tanto, ¿son las vulnerabilidades de cadena de formato cosa del pasado? De ninguna manera, y no hacen falta herramientas de análisis estático de código para encontrarlas, tan solo curiosidad y ganas de leer código. He aquí una prueba de que un sencillo fichero mp3 podría tomar control sobre tu sistema. Mi pequeño 0day.

Feliz Hacking!!!
by blackngel, autor del libro Linux Exploiting

miércoles, enero 01, 2014

El año que "latcheé" mi vida digital

Comienza el año 2014 y como todos vosotros he hecho mis propósitos a cumplir este año. La verdad es que salvo ver algunas series de TV, leer algunos libros, degustar comics, ir más al cine y disfrutar de un poco más de tiempo libre, el resto son retos auto-impuestos de emprendimiento personal. Ya veremos cómo va saliendo el año mes a mes y así os voy contando el devenir con cada cosa nueva.

Lo que sí que os puedo decir es que para mí este año es el año Latch. Forzamos mucho la máquina para poder presentar la tecnología antes de fin de año, y de ahora en adelante lo que haremos es centrarnos en "Latchear" el máximo de servicios disponibles, y de construir al rededor de esta tecnología el mayor número de apps y servicios para crear un ecosistema para los usuarios.

Creamos Latch como una forma en la que el usuario pudiera poner pestillos digitales en cualquier identidad o acción granular de un sistema, para que si alguien que hubiera robado la identidad, o la hubiera adivinado por un ataque de fuerza bruta, intentara usarla, no solo no pudiera, sino que además recibiéramos una alerta.

Figura 2: App de Latch para iPhone con identidades "latcheadas"

El servicio lo estamos implantando nosotros en un buen número de grandes servicios como el panel de control de Acens, o en tiendas online como Recover Messages, Nevele Bank y 0xWord para que lo pudierais probar como usuarios. Para que lo pudierais probar bien sacamos las apps para iPhone y Android, y aunque el proceso ha sido un poco más lento de lo esperado,  la app de Latch ya está aprobada en la Windows Phone Store para que la puedas descargar, instalar y utilizar en tus terminales con Windows Phone 8.

Figura 3: App de Latch para Windows Phone 8

Lo cierto es que además de que nosotros implantáramos Latch en grandes entornos, o que preparásemos sitos de prueba, queríamos que fueran los desarrolladores y administradores de sistemas los que decidieran dónde debería estar el servicio de Latch instalado. Por eso sacamos un set inicial de plugins para WordPress, PrestaShop, Joomla, Redmine, Drupal 6, DotNetNuke o el .NET Log in, que pueden ser instalados fácilmente, como se puede ver aquí en WordPress.

Figura 4: SKs y Plugins de Latch disponibles

Pero para dotar de más flexibilidad aún, quisimos publicar directamente los SDKs para todos los lenguajes, que pudieran ser usados en aplicaciones escritas en C, .NET, Java, Python, Ruby o PHP, que están disponibles todos dentro del area de developer de Latch

A día de hoy son casi 200 los entornos que lo han implantado, y algunos tan chulos como hizo Alejandro Ramos (@aramosf) configurando Latch en servidores FTP haciendo un port del SDK a Bash del que me quedo esta frase sobre Latch tras trabajar con él: "El sistema es brillantemente sencillo". Su trabajo luego ha sido utilizado para implantar Latch en el SSH de un VPS. En este último caso lo que hace el script es matar el demonio ssh si se detecta que el Latch configurado está bloqueado, lo que incapacita su utilización.

Figura 5: Script de configuración de Latch en un servidor SSH

Este último nos ha llamado especialmente la atención, ya que nosotros tenemos listo para salir un plugin escrito con el SDK de C para usar como módulo pam en los SSH, pero la flexibilidad que da la arquitectura de Latch permite este tipo de hacks.

Decían en HackPlayers que Latch es una aplicación para dominarlas a todas, y que les parecía la mejor idea de todas en 2013. No sé si tanto como eso, pero os garantizo que yo personalmente estoy muy contento de en lo que se ha convertido Latch, y espero que éste sea el año en que pueda poner un latch a la mayoría de mis identidades digitales... 

Saludos Malignos!

miércoles, diciembre 18, 2013

Nevele Bank: Probar Latch con operaciones granulares

Para que todo el mundo pudiera entender bien los conceptos detrás de Latch decidimos crear un sencillo banco al que llamamos Nevele (Eleven al revés) Bank. Este supuesto banco no es nada más que una aplicación web desarrollada en .NET en la que se permiten hacer falsas transferencias de capital a cuentas locales o a cuentas internacionales.

Esta webapp tiene instalado el SDK de .NET que puedes descargarte gratuitamente desde el área de Developer de Latch. En ella se ha configurado un sencillo árbol de operaciones que tiene que ver con las transferencias, y dentro de ellas, con las transferencias locales e internacionales para que puedas probar cómo funciona la granularidad de operaciones en Latch que se vio en el artículo de ayer.

Lo primero que debes hacer es irte a la web de Nevele Bank y abrirte una cuenta del sitio. Ten cuidado, es un banco fake, y algunos filtros antiphishing te pueden dar una alerta, pero no te preocupes por ello, no movemos dinero de verdad. También es necesario que tengas la app de Latch para Android o iPhone instalado en tu smartphone y te hayas sacado una cuenta gratuita del servicio Latch desde la propia app del terminal móvil.

Una vez que tengas tu cuenta de Latch y tu cuenta de Nevele Bank, el siguiente paso del proceso es establecer el Latch entre ambas, haciendo el mismo proceso de pareado que se describe en el ejemplo con 0xWord, hasta que la identidad de Nevele aparezca en la app de Latch en tu smartphone. Si entras en las opciones de la identidad de Nevele dentro de tu app de Latch en el smartphone, podrás vez cómo hay un nodo de Transfers dentro de las cosas que se pueden configurar para que estén ON u OFF.

Figura 1: Nevele Bank tiene un nodo Transfers para configurar

Y por supuesto, si entras dentro de Transfers hay dos tipos de transferencias que pueden ser controladas desde la app, como son Locales e Internacionales. Este sencillo árbol de operaciones es el que ha sido establecido por el dueño de la identidad - Nevele Bank - en el momento de darse de alta con Latch, pero podría cambiar y modificar estas operaciones a lo largo del tiempo para dotar de más flexibilidad o control de las operaciones del sistema al usuario.

Figura 2: Opciones de configuración de la identidad Nevele Bank en Latch

En este ejemplo se puede ver cómo las transferencias Internacionales están bloqueadas y las transferencias Locales están permitidas, por lo que el comportamiento que se espera en la web es más o menos el mismo. 

Si vamos al sitio web de Nevele Bank, podremos intentar hacer una transferencia local. Para ello seleccionamos transferencias locales y rellenamos los datos de la transferencia. No te preocupes demasiado por el formato, casi cualquier cosa es admitida. Casi cualquier.

Figura 3: Haciendo una transferencia Local en Nevele Bank

En la siguiente pantalla tendremos la opción de confirmar la transferencia, y si lo hacemos se verá que el resultado ha sido satisfactorio, ya que el estado de esa operación en Latch es desbloqueado, por lo tanto no debería negarse.

Figura 4: Transferencia Local realizada

Si probamos a hacer una trasferencia internacional con el mismo proceso, podremos ver que el resultado no es el mismo. En primer lugar vamos al menú de Operaciones en la web de Nevele Bank, seleccionamos una Transferencia Internacional y rellenamos los datos.

Figura 5: Haciendo una Transferencia Internacional en Nevele Bank

Cuando demos a confirmar la operación, el resultado que deberemos obtener es que esta ha sido bloqueada por la configuración de la operación en el Latch que ha manejado el usuario.

Figura 6: Operación bloqueada en Nevele Bank por la configuración de Latch

Al mismo tiempo, el dueño del Latch recibirá una alerta en su app del terminal móvil que le avisará de que una operación que tenía bloqueada se ha intentando, pero que el sistema debe haber bloqueado.

Figura 7: Mensajes de bloqueo en la app de Latch para iPhone

El comportamiento de la aplicación web depende en todo momento de lo que quiera hacer el desarrollador. En el area de developer de Lath hay SDKs para Python, PHP, Java, .NET, Lenguaje C y Ruby. Todos ellos son Open Source y tienes documentación que explica cómo debes integrar estos controles a nivel general de cuenta ON/OFF o a nivel granular con las distintas operaciones. Puedes probar todas las características de Latch en Nevele Bank, tal y como se ve en el siguiente vídeo.


Figura 8: Demostración de todas las funciones de Latch en Nevele Bank

Si quieres probar el uso de Latch a nivel de usuario, lo puedes hacer en 0xWord, en Nevele Bank como hemos visto aquí, y en 90 sitios que ya están integrados, pero que os iré contando poco a poco. Bájate la app y dale una prueba a ver qué te parece su uso. 

Saludos Malignos!

jueves, septiembre 26, 2013

Prescinde de tu Sistema Operativo, si puedes...

Imaginemos la siguiente situación: Usted tiene la intención de realizar un ataque de fuerza bruta sobre una contraseña aplicando un algoritmo dado. Entonces debe de tomar algunas decisiones importantes que influirán en el rendimiento y el tiempo de ejecución que su programa de crackeo tardará en completar el proceso. A pesar de que puede escribir su aplicación ayudado por las facilidades de algún lenguaje interpretado (como Perl o Python), se da cuenta de que escribir el código en C y obtener un binario compilado puede brindarle algunas mejoras significativas en lo que a performance se refiere.

Finalmente, y antes de correr el programa, cierra todas las aplicaciones y procesos que pudiesen estar consumiendo recursos del sistema: el navegador web, el procesador de textos y quizás hasta su preciado antivirus. Ahora ya puede dejar que su ordenador haga todo el trabajo y sentarse en el sofá a la espera de resultados, al fin y al cabo su procesador se encuentra ocupado únicamente con el algoritmo de bruteforce, ¿verdad?

La cruda realidad no muestra una cara tan bonita, por desgracia. Puede regresar a su equipo y abrir el monitor de procesos/administrador de tareas, tranquilamente otros 20 procesos podrían estar ejecutándose además del suyo, y algunos de ellos ni siquiera pueden “matarse” sin cargarse el sistema. Pero esto no es más que la punta del iceberg, su SO está gestionando por detrás múltiples hilos de kernel, interrupciones, mecanismos de entrada/salida (I/O), y si todavía no se ha desconectado de Internet, atendiendo a todos los paquetes que entran y salen por la interfaz de red.

Y hay más, muchísimo más, su querido sistema multitarea realiza controles de colas, intercambio de stacks entre procesos, IPC, y está haciendo verdaderas virguerías con la paginación y gestión de memoria virtual ayudado por el hardware subyacente. Digamos que cada 100ms una interrupción se genera en el procesador (ticks de reloj) y el núcleo del sistema retoma el control e invoca el scheduler para comprobar cuál es el próximo proceso que merece su tiempo (slice). Desde luego, esto es lo que hace un sistema operativo moderno, y otra infinidad de tareas que de forma intencionada nos dejamos en el tintero, pero usted solo quería crackear una contraseña, todo lo demás es “tiempo perdido”.

Bajo esta premisa decidí realizar la siguiente demostración. Cree un pequeño programa que aplicara la conjectura de Collatz para los primeros cincuenta millones de números. Al igual que un algoritmo de bruteforce, esto proporciona alimento suficiente para el procesador. El cálculo es sencillo, se selecciona un número, si es par se divide entre 2 y si es impar se multiplica por 3 y se suma 1, y se vuelve a aplicar el mismo proceso sobre el resultado. La conjetura dice que para todos los números naturales la secuencia siempre termina en 1. Observe el siguiente programa:
#include <stdio.h&gt
#include <stdlib.h>
#include <string.h&gt
#include <time.h>
void print_time(void)
{

char buff[32];
time_t now;
memset(buff, 0, 32);
now = time(0);
strftime (buff, 32, "%H:%M:%S", localtime(&now));
printf("%s\n", buff);
int main(int argc, char *argv[])
{

unsigned int i, n;
print_time();
for ( i = 1; i < 50000000; i++ ) {

n = i;
while ( n != 1 ) {
if ( n % 2 == 0 )

n = n / 2;
else
n = n * 3 + 1;
}
}
print_time();
return 0;
}

Luego lo ejecutamos en una distribución Ubuntu 12.04 (3.5.0-40) sobre un procesador Intel(R) Core(tm)2 Duo T8300 2.40 GHz con 3GB de memoria RAM. La imagen muestra el resultado.

Figura 1: Tiempo de ejecucion del programa de ejemplo

El cálculo se ha prolongado por 50 segundos del reloj. Y ahora viene la pregunta clave, ¿qué ocurriría si pudiésemos hace que el procesador dedicase todo su tiempo a nuestro algoritmo? La solución pasaba por crear un bootloader en el primer sector de un disquete o USB que simplemente crease una GDT básica para pasar del modo real al modo protegido (facilitando así la posibilidad de ejecutar código C) y luego aplicar el mismo algoritmo.

No tenemos la intención de mostrar aquí el código completo, solo lo suficiente para que comprenda la explicación. He aquí la primera parte del proceso de boot en ensamblador. En lo que nos concierne, no hace falta que lo comprenda, digamos que simplemente pasa al modo protegido y luego llama a una función bootmain().
.code16
.globl start
start:

cli
xorw %ax,%ax
movw %ax,%ds
movw %ax,%es
movw %ax,%ss
clear_scr:
movb $0x06,%ah
movb $0x07,%bh
xorw %cx,%cx
movb $24,%dh
movb $79,%dl
int $0x10
seta20.1:
inb $0x64,%al # Wait for not busy
testb $0x2,%al
jnz seta20.1
movb $0xd1,%al # 0xd1 -> port 0x64
outb %al,$0x64
seta20.2:
inb $0x64,%al # Wait for not busy
testb $0x2,%al
jnz seta20.2
movb $0xdf,%al # 0xdf -> port 0x60
outb %al,$0x60
lgdt gdtdesc
movl %cr0, %eax
orl $CR0_PE, %eax
movl %eax, %cr0
ljmp $(SEG_KCODE<<3), $start32
.code32
start32:

movw $(SEG_KDATA<&lt3), %ax # Our data segment selector
movw %ax, %ds # -> DS: Data Segment
movw %ax, %es # -> ES: Extra Segment
movw %ax, %ss # -> SS: Stack Segment
movw $0, %ax # Zero segments not ready for use
movw %ax, %fs # -> FS
movw %ax, %gs # -> GS
movl $start, %esp
call bootmain
spin:
jmp spin
.p2align 2
gdt:

SEG_NULLASM # null seg
SEG_ASM(STA_X|STA_R, 0x0, 0xffffffff) # code seg
SEG_ASM(STA_W, 0x0, 0xffffffff) # data seg
gdtdesc:
.word (gdtdesc - gdt - 1) # sizeof(gdt) - 1
.long gdt # address gdt
Y ahora la función bootmain() en C que finalmente ejecuta la Conjetura de Collatz e imprime el rango de tiempo:
#include "types.h"
static ushort *crt = (ushort*)0xb8000; // CGA memory
void
bootmain(void)
{

unsigned int n, i;
unsigned short segundos = 0x00, minutos = 0x00, horas = 0x00;
asm volatile ("xorb %%al,%%al;"
     "out %%al, $0x70;"
     "in $0x71, %%al": "=a"(segundos));
     asm volatile ("movb $0x02,%%al;"
     "out %%al, $0x70;"
     "in $0x71, %%al": "=a"(minutos));
asm volatile ("movb $0x04,%%al;"
     "out %%al, $0x70;"
     "in $0x71, %%al": "=a"(horas));
crt[160] = ((horas >> 4) + '0') | 0x0a00;
crt[161] = ((horas & 0x0f) + '0') | 0x0a00;
crt[162] = ':' | 0x0a00;
crt[163] = ((minutos >> 4) + '0') | 0x0a00;
crt[164] = ((minutos & 0x0f) + '0') | 0x0a00;
crt[165] = ':' | 0x0a00;
crt[166] = ((segundos >> 4) + '0') | 0x0a00;
crt[167] = ((segundos & 0x0f) + '0') | 0x0a00;
for ( i = 1; i < 50000000; i++ ) {

n = i;
while ( n != 1 ) {

if ( n % 2 == 0 )
     n = n / 2;
else
     n = n * 3 + 1;
}
}
asm volatile ("xorb %%al,%%al;"
     "out %%al, $0x70;"
     "in $0x71, %%al": "=a"(segundos));
     asm volatile ("movb $0x02,%%al;"
     "out %%al, $0x70;"
     "in $0x71, %%al": "=a"(minutos));
     asm volatile ("movb $0x04,%%al;"
     "out %%al, $0x70;"
     "in $0x71, %%al": "=a"(horas));
crt[240] = ((horas >> 4) + '0') | 0x0a00;
crt[241] = ((horas & 0x0f) + '0') | 0x0a00;
crt[242] = ':' | 0x0a00;
crt[243] = ((minutos >> 4) + '0') | 0x0a00;
crt[244] = ((minutos & 0x0f) + '0') | 0x0a00;
crt[245] = ':' | 0x0a00;
crt[246] = ((segundos >> 4) + '0') | 0x0a00;
crt[247] = ((segundos & 0x0f) + '0') | 0x0a00;
return;
}
En el centro de este código observamos el mismo bucle for() que en el programa original que ejecutamos en Linux, todo lo demás son las virguerías que hay que hacer para interactuar con los puertos y obtener la hora de la máquina. Recuerde que lo que estamos haciendo en realidad es programar un “mini sistema operativo” que únicamente ejecuta nuestro algoritmo y luego entra en un bucle infinito. Una vez que compilamos todo el tinglado y lo insertamos en un disquete (obviamos este proceso en el artículo), accedemos a la BIOS para indicarle que arranque desde el floppy. En la imágen el resultado:

Figura 2: Resultado obtenido prescindiendo del sistema operativo

El proceso ha durado tan solo 30 segundos frente a los 50 invertidos por el sistema operativo. Sorprendentemente hemos realizado la misma tarea en un 60% del tiempo inicial, lo cual quiere decir, de forma aproximada, que un ataque de bruteforce que se prolongase por 24 horas, podría realizarse en unas 14 horas “si prescindimos del sistema operativo”.

Otra prueba sobre Windows XP con un procesador AMD Athlon(tm) 64 3000+ 1.81GHz y 512 MB de RAM, proporcionó un resultado de 46 segundos frente a los 68 que tardaba la aplicación en la shell del sistema operativo.

¿Qué es un sistema operativo? Pues esos 22 segundos “fantasmas” de diferencia que usted no sabía que podía ahorrarse.

Obviamente, esta demostración y el artículo que está leyendo no son más que una demostración curiosa. Usted necesita un sistema operativo para trabajar y créame que hoy en día estos invierten ese tiempo fantasma de una forma más económica y elegante que hace algunos años.

Las pruebas se han realizado sobre sistemas operativos de 32 bits, con un procesador x86_64 y un Windows 7 de 64 bits (por poner un ejemplo), seguramente habría que trabajar sobre long mode para poder realizar comparativas fiables...

Entienda que estamos programando la máquina desde cero, y no disponemos de ninguna de las facilidades que un sistema operativo le ofrece al programador, no existen librerías del sistema y todo debe hacerse a bajo nivel, pero no sería descabellado crear un sencillo framework con un bootloader básico que cargase un kernel mínimo en el que usted pueda insertar su algoritmo. Funciones de manejo de cadenas y otras de salida por pantalla pueden ser creadas de antemano sin mucho esfuerzo y proporcionadas por anticipado. No es más que una idea... interactuar directamente con la GPU de la tarjeta gráfica siempre parece más atractivo.

Toda esta teoría también podría aplicarse a un dispositivo Raspberry Pi si usted es capaz de crear un bootloader para ARM, prescindiendo así de la distribución Raspbian de Linux. Estos aparatos pueden realizar algunas tareas costosas si se combinan en un potente cluster, pero si usted no es un gurú o un ninja de la programación, será realmente complicado comunicar entre sí todos los dispositivos y realizar cualquier tipo de procesamiento paralelo.

Por lo demás… Happy Hacking!

by blackngel (blackngel@blackbycode.com)

Entrada destacada

+300 referencias a papers, posts y talks de Hacking & Security con Inteligencia Artificial

Hace un mes comencé a recuperar en un post mi interés en los últimos años, donde he publicado muchos artículos en este blog , y he dejado mu...

Entradas populares