lunes, febrero 09, 2009

RFD [Remote File Downloading] en aplicaciones web con Blind SQL Injection (III de VI)

*************************************************************************************************
RFD [Remote File Downloading] en aplicaciones web con Blind SQL Injection (I/VI)
RFD [Remote File Downloading] en aplicaciones web con Blind SQL Injection (II/VI)
RFD [Remote File Downloading] en aplicaciones web con Blind SQL Injection (III/VI)
RFD [Remote File Downloading] en aplicaciones web con Blind SQL Injection (IV/VI)
RFD [Remote File Downloading] en aplicaciones web con Blind SQL Injection (V/VI)
RFD [Remote File Downloading] en aplicaciones web con Blind SQL Injection (VI/VI)
*************************************************************************************************

RFD en Microsoft SQL Server 2000 mediante opciones de carga masiva

En Microsoft SQL Server 2000 se permite la carga de ficheros registros de una o varias tablas mediante las operaciones de carga masiva. Existen tres formas distintas para realizar la carga masiva de datos desde ficheros. La primera opción es utilizar el comando bcp a nivel del sistema operativo, la segunda opción consiste en realizar un Insert desde una fuente de datos cargada con OpenRowSet como se ha visto en el apartado anterior y la tercera mediante el uso del comando Bulk Insert. Esta última opción es la que un atacante puede utilizar para acceder a ficheros a los que no puede acceder mediante el uso de fuentes de datos externas infrecuentes por no existir un OLE DB Data Provider para ellos.

Restricciones y permisos

Para la creación de tablas es necesario tener como rol en la base de datos db_owner o db_ddladmin, es decir ser el propietario de la misma o tener permisos de administración.

Es cierto que es posible crear tablas temporales, visibles solo durante la sesión en curso (#TablaTemporal) o tablas temporales globales, visibles desde todas las sesiones (##TablaTemporal), y que para la creación de estas tablas no es necesario poseer los roles de bases de datos db_owner y db_ddladmin.

Sin embargo, a la hora de realizar “bulk insert” es necesario que el usuario que ejecuta dicha sentencia, además de poseer el rol de servidor bulkadmin, debe poseer el rol a nivel de base de datos db_owner o db_ddladmin para poder insertar sobre la tabla, y por lo tanto el uso de tablas temporales no tiene sentido

En cuanto a la consideración de a que archivos se tiene acceso y a cuales no, esto dependen del perfil de seguridad del proceso SQL Server. Dado que el usuario es miembro de la función fija de Servidor bulkadmin, el usuario tiene acceso de lectura a todos aquellos archivos a los que el SQL Server puede acceder aunque él no tuviese concedidos esos permisos.

Proceso de la extracción del fichero

En este caso son necesarias cinco fases para la extracción del fichero del sistema operativo. La primera fase consistirá en la creación de una tabla temporal para almacenar los datos del fichero. Una segunda fase en la que el fichero se carga dentro de la tabla temporal. La tercera implica la creación de una columna IDENTITY.

Una cuarta fase de extracción mediante la booleanización de los datos almacenados en esa tabla. Una última fase para eliminar la tabla temporal creada dentro de la base de datos.

- Fase 1: Creación de una tabla temporal: En esta fase, se utiliza el parámetro inyectable para lanzar una consulta de creación de la tabla temporal como sigue:

http://server/app.cod?param=1; Create Table TablaTemporal as (fila varchar(8000))--

Fase 2 - Carga del fichero dentro de una tabla temporal: Se debe poder ejecutar dentro del servidor un comando Bulk Insert. La inyección en un servidor vulnerable sería como sigue:

http://server/app.cod?param=1; Bulk Insert TablaTemporal From 'c:\fichero.ext' With (FIELDTERMINATOR = '\n', ROWTERMINATOR = '\n')--

Como se puede apreciar en el ejemplo anterior el comando Bulk insert tiene una serie de parámetros para indicar cuáles son los caracteres separadores de campos y de fila. En este caso se ha utilizado el retorno de carro como carácter delimitador de campo y de fila.

Fase 3 - Creación de una columna IDENTITY: Una vez cargado el contenido del fichero sobre la tabla, se tiene una tabla con una sola columna y tantas filas como líneas tuviese el fichero, para poder leer correctamente esta tabla en un proceso de booleanización sería necesario contar con un identificador único para cada registro de la tabla.

Para crear los identificadores únicos lo que se hace es añadir una columna de tipo IDENTITY, configurada con valor de inicio 1 e incrementos unitarios, de modo que SQL Server 2000 se encarga de asignarle el valor numérico correspondiente a cada registro. La inyección en un servidor vulnerable sería:

http://server/app.cod?param=1; alter table TablaTemporal add num int IDENTITY(1,1) NOT NULL--

Es muy importante destacar que este paso es necesario hacerlo aquí, si se especifica esta columna durante la creación de la tabla en el paso 1, la carga del fichero sobre la misma no se realiza de forma correcta, por eso es necesario añadir la columna una vez cargada la tabla con la información del fichero

Fase 4 - Proceso de Booleanización: Como resultado de los pasos anteriores se tiene una tabla con dos columnas, cuyos registros se corresponden con las líneas del fichero que se ha leído, para poder descargar el fichero es necesario determinar cuántas filas tiene la tabla. Para determinar el número de registros se utiliza Blind SQL Injection con un proceso de búsqueda binaria, la sentencia tipo a inyectar en un servidor vulnerable sería:

http://server/app.cod?param =1 and (select COUNT(fila) from TablaTemporal) > 255 --

Una vez fijado el número de registros que tiene la tabla, por cada uno de estos se calcularía el número de caracteres de la columna fila, que serían los que habría que leer mediante un proceso de booleanización. Siguiendo el ejemplo visto, la sentencia a inyectar en un servidor vulnerable para determinar el número de caracteres de la primera línea del fichero sería:

http://server/app.cod?param=1 and (select top 1 len(fila) from TablaTemporal where num = 1) > 255 --

De nuevo es necesario establecer un sistema de búsqueda binaria, para inferir la longitud del campo fila del registro, obsérvese que en esta sentencia se hace uso de la columna IDENTITY creada anteriormente, y que sirve para identificar cada uno de los registros de la tabla y por tanto cada una de las filas del fichero.
Una vez determinados el número de filas y el número de caracteres, el siguiente paso es inferir los caracteres de la fila. Siguiendo los ejemplos anteriores la cadena a inyectar sería:

http://server/app.cod?param=1 and (select top 1 ASCII(SUBSTRING(fila,1,1)) from TablaTemporal where num = 1) > 255 --

En esta cadena se está infiriendo el primer carácter de la primera fila de la tabla, como se ha averiguado el número de filas y para cada fila se averigua el número de caracteres es posible fijar los valores a utilizar para descargar el contenido completo del fichero

Fase 5 - Eliminación de la tabla Temporal: Por último, se procederá a eliminar la tabla temporal creada en la base de datos como sigue:

http://server/app.cod?param=1; Drop Table TablaTemporal--

*************************************************************************************************
RFD [Remote File Downloading] en aplicaciones web con Blind SQL Injection (I/VI)
RFD [Remote File Downloading] en aplicaciones web con Blind SQL Injection (II/VI)
RFD [Remote File Downloading] en aplicaciones web con Blind SQL Injection (III/VI)
RFD [Remote File Downloading] en aplicaciones web con Blind SQL Injection (IV/VI)
RFD [Remote File Downloading] en aplicaciones web con Blind SQL Injection (V/VI)
RFD [Remote File Downloading] en aplicaciones web con Blind SQL Injection (VI/VI)
*************************************************************************************************

1 comentario: