lunes, junio 09, 2014

Explotar SQL Injection en Access para atacar Windows

Cuando en una auditoría de seguridad te toca lidiar con una vulnerabilidad SQL Injection en la que la base de datos está almacenada en un motor JET de Microsoft Access las herramientas con las que hay que trabajar son muy distintas a las del resto de los motores de bases de datos. No es que cuando te enfrentes a un Microsoft SQL Server, un Oracle Database o un MySQL sean las cosas exactamente iguales en ellos, pero la ausencia de un diccionario de datos que consultar como los esquemas de Oracle o la no existencia de procedimientos almacenados como los que hay en Transact-SQL hacen las cosas siempre un poco más difíciles.


Figura 1: Codemotion 2013 - Feliz 15 aniversario SQL Injection

En la charla que di en el pasado Codemotion que os dejo por aquí hago un repaso a las principales técnicas de SQL Injection durante los últimos 15 años, y si quieres aprender bien las técnicas de SQL Injection puedes leer el libro de Hacking de Aplicaciones Web: SQL Injection.

SQL Injection en Microsoft Access

En uno de esos sitios me topé con SQL Injection in-band en el que era posible construir una consulta de inyección con un UNION. Hacer esto había sido fácil porque el truco para hacerlo es sencillo y lo había utilizado ya para hacer las consultas pesadas en el trabajo de Time-Based Blind SQL Injection using Heavy Queries sobre Access. Solo debía tirar sobre una de las tablas que existen en las bases de datos y que pueden utilizarse en una consulta SQL

En este caso la tabla que utilicé fue MSysAccessObjects que está disponible en las versiones de Microsoft Access 97 o Microsoft Access 2000, ya que en 2003 y 2007 esa tabla fue cambiada por otras. La lista de tablas que hay en cada versión de MS Access 97 a MS Acces 2007 - a partir de MS Access 2010 el formato dejó de ser mdb - está disponible en muchos sitios. Yo tiré de este tutorial de Net_Spy. Como se puede ver, aunque cada versión tiene una lista de tablas comunes, solo son utilizables en una consulta SQL aquellas que tienen el carácter asterisco a su lado.

Figura 2: Tablas disponibles en las versiones de Microsoft Access 97 a 2007

Una vez que está construida la consulta de inyección SQLi con el UNION usando constantes y apoyándonos en una tabla conocida como MSysAccessObjects, el siguiente paso es descubrir las tablas que forman parte de esa base de datos, para lo que se debe tirar a los sospechosos habituales - como users, users, customers, clients, usuarios, etcétera -.

En mi caso, no hubo suerte con los sospechosos habituales, así que había que pasar a tirar un diccionario con una buena cantidad de palabras porque no había encontrado ninguna. Esto puede pasar muchas veces. Si el dueño de la base de datos ha decidido utilizar una nomenclatura del tipo tablaAppCompras_usuarios, te puedes hacer anciano esperando a descubrir una de esas tablas. Ese es el problema de no tener un diccionario de datos que se pueda consultar en el motor. 

Si se hubiera descubierto el nombre de las tablas, luego se puede abusar de los mensajes de error que se obtienen al usar la claúsula Group BY, o bien volver hacer un ataque de diccionario otra vez contra los campos de las tablas. En este caso en concreto solo fui capaz de sacar el nombre de unos campos de la tabla con ataques out-band generando mensajes de error del motor JET.

No obstante, en Microsoft Access se pueden hacer cosas muy chulas fuera del motor de la base de datos, que paso a detallaros, ya que os pueden venir bien en una auditoría en el futuro. Todos ellos son ataques out-band que se apoyan en el mensaje de error del motor JET de Microsoft Access.

Descubrir si un fichero existe y/o si está en uso

En el lenguaje SQL de Microsoft Access se puede hacer uso del operador IN para especificar que una tabla se encuentra dentro de otro fichero mdb de Microsoft Access. Es decir, es posible hacer una consulta que haga JOIN entre dos tablas y que cada una de ellas se encuentre en un fichero .mdb distinto. La consulta es tan sencilla como:
and exists (Select 1,2,3 from MSysAccessObjects IN 'autoexec.bat')
Poniendo el nombre de cualquier fichero - no importa si el fichero no es de bases de datos Microsoft Access - recibiremos un error distinto en tres situaciones posibles:

a) El fichero no existe

Figura 3: error al usar "and exists (Select 1 from MSysAccessObjects in 'foca.foca')

b) El fichero existe pero está en uso y no se puede abrir

Figura 4: El fichero boot.ini existe pero no se puede abrir

c) El fichero existe, se puede abrir, pero no es una base de datos Access

Figura 5: error al usar "and exists (Select 1 from MSysAccessObjects in 'autoexec.bat')

Por desgracia, yo me he encontrado con algunas limitaciones con estas inyecciones, que paso a detallaros:
1.- No es posible cambiar de unidad, así que si la base de datos del motor JET está en la unidad C:, este truco te permitirá listar solo ficheros de esa unidad. 
2.- No se pueden listar ficheros sin extensión. El error que he obtenido ha sido siempre el mismo con ficheros que sé que existen y con ficheros que se que no existen. 
3.- No funciona para listar directorios. Si se intenta usar para saber si un directorio existe o no, solo se puede hacer con algún fichero que se conozca que está dentro. Si no, el mensaje de error que se obtiene es siempre el mismo. 
4.- No se pueden utilizar comodines * o ? para escribir el nombre de los ficheros. 
5.- No se pueden utilizar las variables de entorno tipo %USERDIR%, %WINDIR%, etcétera.
Sacar los datos de un fichero mdb en el mismo servidor

En los ejemplos anteriores se ha supuesto que el objetivo era conocer la existencia o no de un fichero, pero si se descubriera otro fichero mdb de otra aplicación en el sistema. Se podría extraer toda la información de la misma usando como base la conectada a la aplicación con SQL Injection que se está utilizando para lanzar el ataque.

Figura 6: Extraer datos de otra base de datos mdb no expuesta a Internet

Sacar la lista de usuarios de un sistema Windows

Aprovechándome de esta función de listar ficheros, decidí que un buen truco sería listar ficheros de los perfiles de los usuarios que existieran. En este caso, se puede ver que tirando del directorio de Default User es cómodo construir alguna consulta con algún fichero de los perfiles de usuario.

Figura 7: Ruta al perfil de Default User

En este ejemplo usé NTUSer.dat para tener un fichero sobre el que apoyarme y luego ir cambiando el nombre de los usuarios. Como puede verse, el usuario Administrator existe.

Figura 8: Ruta al perfil del Administrador. Existe y está en uso, lo que significa que la sesión está abierta.

El resto es lanzar un diccionario con los nombres de usuario para tener la lista completa y, por supuesto, aprovecharse de los nombres de usuarios que FOCA hubiera descubierto de los metadatos de los documentos del sitio web.

Descubrir si un fichero existe y/o si está en uso usando formato de nombres 8:3

Acordándome del bug de IIS Shortname se me ocurrió que podría ser buena idea intentar localizar nombres de ficheros de más de 8 caracteres usando la codificación 8:3. Esto haría que fuera bastante más reducido el tiempo con un intento de descubrir ficheros de una carpeta, sobre todo si se va a hacer un ataque de fuerza bruta.

Figura 9: Ruta al perfil de Default User con nombres 8:3

Para comprobarlo, utilicé de nuevo el fichero de perfil de Default User y como se puede ver es posible, por lo que para hacer un ataque de fuerza bruta a la lista de usuarios se simplificaría un poco. Se podría haber hecho de igual forma con el usuario Admini~1, y volveríamos a ver que el fichero está en uso.

Figura 10: Ruta al perfil del Administrador en nombres 8:3

No solo ayuda a reducir el número de ataques, sino a reducir el tamaño de la URL inyectada, lo que siempre viene bien para evitar cualquier límite de tamaño que pudiera existir.

Listar el software instalado en el servidor

Con la misma idea de reconocer usuarios del sistema por los ficheros de su perfil, se podría hacer un listado del software instalado en el equipo - y el que está en arrancado por los errores de acceso en uso - buscando los ficheros de los programas. Para ello habría que tener una lista de rutas de instalación por defecto de los programas más comunes y buscarlos.

Figura 11: Ruta de instalación por defecto de WinZip. No está en el sistema

Si los programas están instalados en otra unidad o están en rutas distintas esto no valdría, pero si se conoce el software se podría intentar preparar, por ejemplo un ataque de evil grade o un exploiting concreto conociendo la existencia de un software vulnerable sin actualizar.

Descubrir la ruta de instalación del servidor web

Otro de las fugas de información que se pueden aprovechar con los errores JET es el del path de instalación del motor en el servicio web. No es la ruta en la que se encuentra el fichero mdb de la base de datos Microsoft Access, pero sí que da información más que suficiente para conocer con qué servidor Windows se está lidiando.

Figura 12: Local path disclosure al buscar en foca.foca

Para conseguirlo solo hay que hacer una consulta SQL inyectada similar a esta:
and exists (Select 1,2,3 from fake_database.fake_table) 
Al no existir el fichero fake_database con una base de datos Microsoft Access en la carpeta donde se encuentra la base de datos actual, se produce un error que muestra la ruta local al servicio IIS.

Listar los roles del servidor. ¿Es un Controlador de Dominio?

Los archivos que tiene un servidor Windows sirven también para identificar los roles que tiene un servidor Windows asignado. Si tiene un servicio DHCP, un servicio DNS o si es un controlador de domino.

Figura 13: Petición de la base de datos de Active Directory. No está disponible.

Por ejemplo, para saber si es un Active Directory se podría buscar la base de datos NTDIS.Dat en la ruta en la que se configura. En este caso no tiene asignado este rol.

Descubrir el nombre del fichero de la base de datos

Uno de los ataques más fáciles de realizar a sistemas que usan como repositorio de datos un fichero mdb de Microsoft Access, es descargar el fichero completo. Para ello hay que averiguar dos cosas que son, el directorio donde se almacena - que deberá ser público para poder descargarlo- y el nombre del fichero.

Si esto se hace en dos fases en lugar de en una, el proceso es un poco más sencillo. Averiguar el nombre del fichero, sin importar el directorio en el que esté, se puede hacer haciendo uso de una consulta inyectada similar a la anterior, pero en lugar de usar una fake_database, usar uno de los nombres posibles:
and exists (Select 1,2,3 from fichero.tabla_existente)
esto permite que se obtenga un error cuando el fichero no exista se obtenga un error que indica que ese fichero mdb no está allí.  Si existe, se conseguirá otra respuesta de la base de datos, en este caso un mensaje de syntax error porque la inyección se ha hecho para forzar el error out-band.

Figura 14: El fichero se llama database.mdb (nada imaginativo)

Una vez conocido el nombre de la base de datos, conocer su ubicación puede realizarse probando en todos los directorios de la web descubiertos con todas las técnicas que ya os dejé en el artículo: Técnicas para descubrir los ficheros de un servidor web.

Una frase para terminar

Un bug de SQL Injection es un fallo muy serio, y si tenemos una base de datos MS Access por medio se pueden hacer muchas cosas en el sistema operativo aunque no seamos capaces de descubrir las tablas de la base de datos. En este caso se ha hecho aprovechando un ataque out-band para ver los mensajes de error de que el fichero existe, la tabla existe, etcétera y errores de sintaxis, pero también se podría haber usado la técnica de Blind SQL Injection o Time-Based Blind SQL Injection using Heavy Queries para generar retardos de tiempo cuando la respuesta fuera correcta.

Saludos Malignos!

3 comentarios:

  1. Muy útil para auditar páginas de grandes empresas, access es muy utilizado. Chema tienes que quejarte ya... no te han puesto en esta lista ..raro, raro: http://www.esquire.es/actualizacion/1926/los-hackers-mas-famosos-del-mundo

    ResponderEliminar
  2. @Anónimo, es que esos son algunos de los más famosos de la historia }:P. Reducir la lista a 10 hoy en día sería muy complicado. }:)

    Saludos!

    ResponderEliminar
  3. Que paso Chema se acaba elladodelmal? recientemente vi una poncia tuya y en mundo hacker ahora usas Mac -_-

    ResponderEliminar