jueves, 27 de agosto de 2015

El GET invisible.

Hoy por casualidad he descubierto un bug en un código PHP que permite realizar cualquier operación con la aplicación sin estar autenticado, y que no se detecta con las herramientas de depuración de los navegadores web.

El programador de la aplicación realiza de forma sistemática una comprobación de la autenticación del usuario al principio de cada página PHP y en caso de no estar autenticado envía al cliente un cabecera HTTP 302 Found. Cuando el programador probaba la aplicación, con el Chrome y sus herramientas de depuración de red, podía ver perfectamente como en cada GET sin cookie de autenciación el navegador recibía un código 302 y, en el cuerpo del response, un mensaje gordo que decía "Failed to load response data", que es lo que dice siempre que no hay cuerpo en la respuesta HTTP.


Pero da la casualidad que yo siempre depuro las aplicaciones web con el Wireshark abierto y esnifando paquetes a piñón como si no hubiera mañana. Una vez que he probado una operación en la aplicación, utilizo un Follow TCP Stream para inspeccionar todos los pasos HTTP que se han realizado de forma secuencial, y fíjate mi sorpresa cuando veo que la respuesta del servidor incluye, aparte de un código 302 y su location, toda la información de la página web procesada completamente, igual que si hubiera sido un usuario autenticado.

GET /archivo/personal/ver.php?cadena_busqueda=1_996598%22():;915564&codigo=34&junta=489 HTTP/1.1
Host: fakehost.com
Connection: keep-alive
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.157 Safari/537.36
Accept-Encoding: gzip, deflate, sdch
Accept-Language: es-ES,es;q=0.8
Cookie: PHPSESSID=d397dce5d77ad37597ff94d674685b70

HTTP/1.1 302 Found
Connection: close
Date: Thu, 27 Aug 2015 12:23:57 GMT
Server: Microsoft-IIS/6.0
MicrosoftOfficeWebServer: 5.0_Pub
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Location:http://fakehost.com/archivo
Content-type: text/html
<html>
.<head>
..<title>Principal</title>
..<link href="../estilos/estilos.css" type="text/css" rel="stylesheet"
.</head>
.<body>
..<div id="pagina">
...<div id="zonaContenido">
....<div align="center">
....<div id="tituloForm" class="header">VER USUARIO</div>
....<div id="frmBusqueda">
.....<table class="fuente8" width="98%" cellspacing=0 cellpadding=3 border=0> 
Y el resto de toda la información de la búsqueda abajo ejecutada correctamente y visible a cualquier usuario no autenticado que se moleste en poner un sniffer de red.
Resulta que el navegador, en cuanto recibe el código 302, redirecciona a la nueva localización e ignora el resto del response, por lo que no se muestra en las herramientas de depuración de red, aunque esta información sí que le llega al cliente.

El fallo está en que al programador se le olvidó ejecutar exit o die después de enviar la cabecera 302, por lo que el script PHP se seguía ejecutando y la respuesta completa era enviada al usuario.

Sabiendo esto, decidí vacilarle un poco antes de notificárselo de forma definitiva (es Jueves y ya apetece jugar un poco). Me aposté unas cañas con él a que era capaz de que la aplicación hiciese lo que yo quisiese sin, ni siquiera, tener una cookie de autenticación; aunque fuese forjada o robada. El pobrecito, tan seguro de si mismo estaba que, aceptó sin dudarlo.

Como el navegador web no te permite crear tus propios POST, y no estaba autenticado para acceder a la aplicación y pulsar los botones de submit de los formularios (en esto se basaba el reto), tomé la decisión de utilizar un pequeño complemento llamado HttpRequester de Firefox que me permite forjar mis propios POST en los cuales, en todos los campos que aceptaban texto libre, puse: "Me debes una caña por cada vez que veas este mensaje". Así hasta 15 campos de texto libre encontré en diferentes paginas de la aplicación. :)

Una vez forjado cada POST; lo lanzaba contra su aplicación, la cual respondía con 302 al no tener cookie de sesión, pero el POST era ejecutado por el script PHP y terminaba almacenando en su base de datos la frase de la deuda de las cañas.

Después le pedí que hiciera una Select en la base de datos y que contase las veces que salía el mensaje para calcular el número de cañas a deber. 

Moraleja: Cañas gratis todo el mes.

No hay comentarios:

Publicar un comentario