26 de abril de 2011

90-60-90, Desarrollos a medida...

Muchas aplicaciones Web utilizan partes de las URLs como parámetros de entrada de la propia aplicación. Un ejemplo de ello son los Sistemas de Gestión de Contenidos o CMS.

En Drupal, por ejemplo, todos los contenidos son denominados nodos y pueden ser accedidos a través de URLs como la siguiente:

http://www.example.com/node/21947

De este modo, 21947 es un parámetro de entrada que le dice al CMS qué contenido tiene que recuperar de la Base de Datos.

Al ser un parámetro de entrada que además se utiliza para consultar una Base de Datos... en principio podría ser susceptible a inyecciones de código. Por "desgracia", la mayor parte de los CMS ya contemplan esta posibilidad y están protegidos contra este vector de ataque.

¿Pero qué pasa con los desarrollos a medida que imitan este funcionamiento? Hace poco me he encontrado con una aplicación Web que utilizaba URLs como la anterior en la que se puede ver que una parte de la misma es un parámetro de entrada.

Así, se me ocurrió probar a acceder a recursos como:

http://www.example.com/node/21947'%20and%20'a'='a
http://www.example.com/node/21947'%20and%20'a'='b

y... ¡¡bingo!! La aplicación tenía una inyección de código SQL en una de las partes que formaba la propia URL.

De hecho, la URL era más compleja que la anterior. La inyección estaba más o menos por la mitad y tenía parámetros por GET, pero todos estaban cabreantemente limpios:

http://www.example.com/user/21947'%20and%20database()%20like%20'mydb'%20and%20'a'='a/getProfile?a=b&c=d

¿Quién les iba a decir a los desarrolladores de la aplicación que se habían dejado un punto de entrada sin validar? Y no sólo eso, sino ¿a qué herramienta de análisis automático se le ocurriría realizar este tipo de pruebas? Las pruebas que tendría que realizar por cada URL se multiplicarían y el tiempo de análisis sería mucho mayor.

En resumen, desde el punto de vista del auditor, las herramientas automáticas sólo sirven para dar una primera impresión del estado de seguridad de un sistema pero siempre hay que escavar mucho más profundo para hacer un análisis exhaustivo.

Desde el punto de vista de los desarrolladores, siempre hay que validar absolutamente todos los parámetros de entrada de la aplicación, por muy recónditos que puedan parecer. Hay que pensar que es imposible prever todos los usos que hará un usuario (legítimo o no) de la aplicación, por lo que hay que intentar no dejar nada al azar.

24 de abril de 2011

Firesheep bajo Linux

Mucho se ha hablado de Firesheep, esa pequeña extension de Firefox que nos permite capturar las sesiones HTTP que estén presentes en nuestro mismo dominio de colisión.
Cierto es que este tipo de ataques se conocen desde hace bastante tiempo, como se puede ver en http://www.defcon.org/images/defcon-16/dc16-presentations/defcon-16-perry.pdf

En este artículo no se va a tratar de aspectos técnicos de la implementación de Firesheep, ni de session sidejacking, eso lo haremos en artículos posteriores.
Por ahora, nos vamos a concentrar en la compilación y ejecución de Firesheep sobre Linux.

El código de Firesheep está disponible en https://github.com/codebutler/firesheep
La extensión está programada en C++ y JavaScript usando como backend de captura las libpcap (winpcap en el caso de Windows).

En principio, esta extensión estaba disponible sólo para Windows y MAC, pero el 5 de Noviembre de 2010 se realizó el commit con el código de Linux como se puede ver aquí https://github.com/codebutler/firesheep/commit/f6ced0af100df60d472f760bc9b47fa05f4ebfb1

Si nos fijamos en los comentarios de los commits, podemos leer:

* Added linux support using Michajlo Matijkiw's work with various fixes
* Needs policykit installed (no need to manualy set permissions on backend)

Para bajar y compilar Firesheep, usamos git & autotools:


git clone git://github.com/mickflemm/firesheep.git
cd firesheep
git submodule update --init
./autogen.sh --with-xulrunner-sdk=/usr/lib/xulrunner-devel-1.9.2.13
make
firefox build/firesheep.xpi
cd ~/.mozilla/firefox/iquftp6a.default/extensions/firesheep@codebutler.com/platform/Linux_x86_64-gcc3sudo
./firesheep-backend --fix-permissions
airmon-ng wlan0 start


Como se puede intuir, Firesheep usa un binario de backend, el cual le hace de interfaz con las libpcap y los dispositivos de captura de la máquina. Este binario se suele encontrar en ~/.mozilla/firefox/xxxXXXxXX.default/extensions/firesheep@codebutler.com/platform/Linux_x86_64-gcc3/firesheep-backend en las máquinas de 64bits una vez instalada la extensión en el navegador Firefox.


usuario@studio:~/.mozilla/firefox/asddas.default/extensions/firesheep@codebutler.com/platform/Linux_x86_64-gcc3$ ./firesheep-backend --help
Syntax: ./firesheep-backend <iface> <capture filter>


Si miramos el código que procesa los *argv[] del backend en backend/src/main.cpp, allí podemos ver los parámetros que le podemos pasar al binario, justo a partir de la línea 52:


53 if (argc > 1) {
54 if (argv[1] == string("--fix-permissions")) {
55 if (platform.is_root()) {
56 if (platform.check_permissions()) {
57 /* Nothing to do */
58 return EXIT_SUCCESS;
59 } else {
60 platform.fix_permissions();
61 return EXIT_SUCCESS;
62 }
63 } else {
64 bool success = platform.run_privileged();
65 return (success) ? EXIT_SUCCESS : EXIT_FAILURE;
66 }
67 } else if (argv[1] == string("--list-interfaces")) {
68 list_interfaces(&platform);
69 return EXIT_SUCCESS;
70 }
71 }
72
73 if (!platform.is_root()) {
74 cerr << "Run --fix-permissions first." << endl;
75 return EXIT_FAILURE;
76 }
77
78 if (argc < 3) {
79 cerr << "Syntax: " << argv[0] << " <iface> <capture filter>" << endl;
80 return EXIT_FAILURE;
81 }


Tras ver el código, comprobamos que las opciones que podemos usar son: --fix-permissions --list-interfacesy los parámetros son los necesarios para que el backend haga su tarea.Si observamos las líneas 73 a 76 del archivo main.cpp, podemos ver cómo es necesario ejecutar --fix-permissions:


73 if (!platform.is_root()) {
74 cerr << "Run --fix-permissions first." << endl;
75 return EXIT_FAILURE;
76 }


Vamos a probar si Firesheep detecta bien las interfaces de nuestra máquina. Para esto, usamos la opción --list-interfaces del backend:


firesheep-backend --list-interfacesterminate called after throwing an instance of 'std::runtime_error'
what(): libhal_device_get_property_string failed: org.freedesktop.Hal.NoSuchProperty
No property info.vendor on device with id /org/freedesktop/Hal/devices/net_0a_00_27_00_00_00
Abortado


Uf, parece que ha petado bien! Si nos fijamos en el error que nos da, dice que lo que ha fallado ha sido justamente libhal_device_get_property_string. Vamos a buscar ese método get en el código del backend del Firesheep:


usuario@studio:~/firesheep/backend/src$ grep -n libhal_device_get_property_string *
linux_platform.cpp:44: char *buf = libhal_device_get_property_string(context, device.c_str(), key.c_str(), error);
linux_platform.cpp:46: runtime_error ex(str(format("libhal_device_get_property_string failed: %s %s") % error->name % error->message));
usuario@studio:~/firesheep/backend/src$


Vemos que en el archivo linux_platform.cpp hay coincidencias, así que le echamos un vistazo:


41
42 string device_get_property_string(LibHalContext *context, string device, string key, DBusError *error)
43 {
44 char *buf = libhal_device_get_property_string(context, device.c_str(), key.c_str(), error);
45 if (dbus_error_is_set(error)) {
46 runtime_error ex(str(format("libhal_device_get_property_string failed: %s %s") % error->name % error->message));
47 dbus_error_free(error);
48 throw ex;
49 }
50 return string(buf);
51 }


Éste es el código que lanza el error, justo en la línea 46.Como esto no nos aclara mucho, vamos a buscar el string device_get_property_string en el código del backend, a ver si encontramos algo más interesante:


usuario@studio:~/firesheep/backend/src$ grep device_get_property_string *
linux_platform.cpp:string device_get_property_string(LibHalContext *context, string device, string key, DBusError *error)
linux_platform.cpp: char *buf = libhal_device_get_property_string(context, device.c_str(), key.c_str(), error);
linux_platform.cpp: runtime_error ex(str(format("libhal_device_get_property_string failed: %s %s") % error->name % error->message));
linux_platform.cpp: string iface = device_get_property_string(context, devices[i], "net.interface", &error);
linux_platform.cpp: string category = device_get_property_string(context, devices[i], "info.category", &error);
linux_platform.cpp: string parent = device_get_property_string(context, device, "net.originating_device", &error);
linux_platform.cpp: string parent_subsystem = device_get_property_string(context, parent, "info.subsystem", &error);
linux_platform.cpp: parent = device_get_property_string(context, parent, "info.parent", &error);
linux_platform.cpp: string vendor = device_get_property_string(context, parent, "info.vendor", &error);
linux_platform.cpp: string product = device_get_property_string(context, parent, "info.product", &error);
usuario@studio:~/firesheep/backend/src$


Bingo! Editamos el archivo linux_platform.cpp a partir de la línea 123. Ahí comentamos las dos llamadas a device_get_property_string que son las que fallan, y ponemos las string vendor y product que nosotros queramos:


121 /* Get device properties */
122
123 //string vendor = device_get_property_string(context, parent, "info.vendor", &error);
124 //string product = device_get_property_string(context, parent, "info.product", &error);
125 string vendor = "Surmano Fumano";
126 string product = "FireDev";
127 string description(str(format("%s %s") % vendor % product));


Por último, recompilamos e instalamos de nuevo la extensión.Perfecto! Podemos ver en las opciones de interface de Firesheep, cómo las interfaces aparecen con el vendor y product que hemos harcodeado en el archivo linux_platform.cppYa no falla Firesheep al ejecutarse y podemos usarlo justo para lo que fue creado: hacer sidejacking