domingo, agosto 02, 2009

SQL Injection básico I/II

¿Qué es?
SQL Injection es una vulnerabilidad bastante común que consiste en hacer que una aplicación que ejecuta una sentencia SQL, ejecute otra sentencia SQL que nosotros queramos, esto se logra “inyectando” un código para así modificar la sentencia SQL original.

¿Cual es el alcance/riesgo de este tipo de vulnerabilidad?
Todo dependerá del escenario (motor de base de datos, versión del motor de la base de datos, sistema operativo, permisos, lenguaje de programación, etc), pero entre las cosas que se pueden lograr están:

  • Saltarse sistemas de autenticación (login bypass)
  • Averiguar contraseñas u otra información, ya sea almacenada en la base de datos o en algún archivo.
  • Agregar, eliminar, editar registros en una base de datos.
  • Agregar, eliminar, editar tablas.
  • Leer archivos (por ejemplo /etc/passwd), lo cual comprometería por completo un sistema...
  • etc.

¿Por qué se da esta vulnerabilidad?
Debido a un mal trato de la aplicación a las variables que recibe, es decir, no se verifica el tipo de datos que esta recibiendo antes de utilizarlo, un error humano bastante común en muchos programadores que no tienen en cuenta la seguridad, en los ejemplos veremos mejor a qué nos referimos.


¿A quienes afecta?
Este tipo de vulnerabilidad se da en cualquier motor de base de datos que ejecute sentencias SQL (MySQL, MS SQL, Oracle, PostgreSQLetc), lo más común es encontrarla en aplicaciones web, aunque también existen otros tipos de inyecciones como: LDAP, XML, XPATH, etc.

El tema SQL Injection es un tema muy extenso e interesante, en este manual únicamente haremos una breve demostración de 2 cosas que se pueden hacer en aplicaciones web: En la primera parte utilizaremos SQL injection para saltarnos un sistema de registro (login bypass) y en la segunda parte veremos un caso real en el cual, haciendo uso de esta vulnerabilidad logramos acceso al panel de administración de un sitio.


Que conste que no soy experto en el tema, tan solo un aficionado que quiere compartir un poco sobre esto...

Introducción
¿Cómo podemos verificar si una aplicación web es vulnerable a SQL Injection? Lo primero que podemos probar es hacer que la base de datos nos genere algún tipo de error, el cual podríamos verificar para obtener mas información, para esto (y para otras cosas que veremos mas adelante) hacemos uso de la comilla simple (') o las comillas dobles (“), por ejemplo, es muy común encontrar sitios webs que en la url muestran algo asi:

http://www.sitiovulnerable.com.sv/faq_details.php?id=1

entonces, simplemente ponemos una comilla simple al final y nos queda:

http://www.sitiovulnerable.com.sv/faq_details.php?id=1'


Lo que nos podría generar el siguiente error:

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 '\'' at line 1


Este error nos dice que la comilla simple esta siendo “escapada” (se le está colocando un backslash antes de la comilla, osea.. la esta “escapando”), al ver que el lenguaje es PHP sabemos que esto se puede lograr, entre otras formas, usando la función addslashes() o teniendo magic_quotes_gpc() activadas en php.ini y además que se trata de un servidor MySQL.

En aplicaciones web las variables se pueden pasar haciendo uso del método GET (pasando variables por la URL como en el ejemplo anterior) o el método POST (haciendo uso de formularios), en ambos casos podemos encontrar este tipo de vulnerabilidad.


Primera parte: Login Bypass

*Nota: para que el siguiente ejemplo funcione, la función magic_quotes_gpc debe estar off en el php.ini, esta opción en las ultimas versiones de php se encuentra ON por default, pero aún podemos encontrar algunos servidores en OFF.


Analicemos la siguiente pagina en PHP /MySQL


+++++++++++++++++++++++
// mysqlinjection_login.php
// realiza la conexión con MySQL siendo
// localhost el servidor de BD, root el usuario y
// password la contraseña.
$conexion=mysql_connect("localhost","root","password");

// Utiliza la conexión anterior para leer la base
// de datos llamada mysqlinjection
mysql_select_db("mysqlinjection",$conexion);

// define la variable user y pass las cuales toma por medio del metodo POST
// para leerlas del formulario, verificar que no se están verificando.
$user=$_POST['usuario'];
$pass=$_POST['password'];

// Esas variables se usan sin “sanitizarse” en una consulta SQL
$consulta = "SELECT * FROM admin WHERE usuario='$user' and password='$pass'";
$row_consulta = mysql_fetch_assoc($consulta_browser);

// $loginok toma el valor de la cantidad de registros que cumplen la condición
$loginok = mysql_num_rows($consulta_browser);

echo $consulta; //mostramos la consulta para efectos de aprendizaje

// Si $loginok tiene algun valor, entonces nos muestra que pudimos loguearnos,
// de lo contrario nos muestra el mensaje de error
if ($loginok) {
echo "LOGIN OK!!!";
}
else
{
echo "USER/PASSWORD ERRONEOS";
}
+++++++++++++++++++++++

Vemos una aplicación normal que no verifica/sanitiza (quita caracteres que pudieran afectar la seguridad), hemos dejado la opción que muestre como queda la consulta para verificar como podemos inyectar nuestro código.


La parte que nos interesa es la siguiente:

SELECT * FROM admin WHERE usuario='$user' and password='$pass'


Si ponemos al usuario “admin” y password “clave” la sentencia queda de la siguiente forma:

SELECT * FROM admin WHERE usuario='admin' and password='clave'


No nos permite loguearnos ya que no hay ningun usuario que cumpla esas condiciones, el resultado no es verdadero.


Pero que pasa si ponemos en ambos campos algo como:

admin' (comilla simple)


Lo que nos da:

SELECT * FROM admin WHERE usuario='admin'' and password='admin''


Nos da error porque no es una sentencia valida, ya que queda una comilla simple extra, asi que buscamos la forma de generar una sentencia válida, pero que a la vez nos genere un resultado verdadero, por ejemplo:

admin' or 'x'='x


Lo que nos daria la siguiente sentencia:

SELECT * FROM admin WHERE usuario='admin' or 'x'='x' and password='admin' or 'x'='x'


Nos da LOGIN OK!!!

Veamos por qué: verifica si existe un usuario que sea admin o que x sea igual a x, no existe un usuario admin (o tal vez si, en realidad no importa) pero la condicion x=x siempre será verdadera, asi que cumple esta parte de la condición, lo mismo sucede con el password.

Otras opciones que nos dan el mismo resultado (resultado siempre verdadero):

' OR ''=' (todas son comillas simples)

' OR 1=1 – (los dos guiones al final hace que el resto de la sentencia quede “comentada” y no la toma en cuenta)

' –

admin' –


Las variantes pueden ser muchas, lo importante es lograr un sentencia válida que siempre devuelva un valor verdadero.


Después de este breve ejemplo nos vendría bien algo de humor... si no lo entienden después de este texto, igual leanlo bien ya que nos sirve de introducción para la segunda entrega, les aseguro que tendrá más forma:

http://imgs.xkcd.com/comics/exploits_of_a_mom.png


Llaman por teléfono a una madre desde el colegio de su hijo:

Colegio: Hola, es del colegio de su hijo, estamos teniendo algunos problemas con los ordenadores.


Madre: ¡Dios mío! ¿Ha roto algo mi hijo?
Colegio: Más o menos...

Colegio: ¿Su hijo se llama de verdad "Robert'); DROP TABLE Students; --"?
Madre: Oh, sí, lo llamamos "Rebertito tablas"

Colegio: Pues bien, hemos perdido todos los datos de los estudiantes de este año. Espero que esté feliz.
Madre: Y yo espero que haya aprendido a verificar las entradas a su base de datos.

Saludos!

p.d.
Correcciones, chascarrios, notas, etc... a los comentarios!


SQL Injection básico II/II



4 comentarios:

TuxRacer @ SV dijo...

Hehehe... lo *básico* cuando el hijo tiene unos padres tipo "Computer Geeks" ;)

Saludos Cisko!

ciskosv dijo...

@TuxRacer: no se entendió?

TuxRacer @ SV dijo...

Oops... No me di a entender bien :P

Lo "básico" es porque cuando el "niño" tiene padres que son (del tipo): "Computer Geeks", le han enseñado al niño a inyectar su nombre en los campos de registro electrónico de su colegio:

"Colegio: Pues bien, hemos perdido todos los datos de los estudiantes de este año. Espero que esté feliz.

Madre: Y yo espero que haya aprendido a verificar las entradas a su base de datos."

La madre sabe que deben sanitizarse las entradas de campos para las consultas a la BBDD, ella (la madre) ha de ser (es) una madre computer geek por haber dado una respuesta de ese tipo! xD

Espero hoy si haberme dado a entender ;)

Saludos, y a ver cuando seguimos "estudiando" :)
(a las 5:15pm aprox cierran el colegio ¬¬)

Bytes!

ciskosv dijo...

@TuxRacer: ahh ok!