HacMe Casino es una aplicación deliberadamente vulnerable que se encuentra escrita utilizando el framework Ruby On Rails, hasta este punto se han mencionado algunas vulnerabilidades que se suelen encontrar en muchas aplicaciones web, independiente de la plataforma utilizada, tales como SQL Injections, XSS Stored/Reflected, CSRF, Session Fixation, etc. Sin embargo, para un atacante siempre resulta útil conocer como funciona la plataforma que es utilizada para servir el sitio web, tanto el lenguaje de programación como el servidor, como se ha mencionado en reiteradas ocasiones, la información es la clave, entre más información se cuente sobre un objetivo, es mucho más probable realizar ataques exitosos.

En ese orden de ideas, hemos visto de la publicación anterior que la aplicación web no está correctamente configurada para responder a mensajes de error internos del servidor (los típicos errores HTTP 500), incluyendo detalles sobre el error que se ha producido en la aplicación, así como la traza completa de la excepción. Una buena practica, suele ser incluir una página de error que se enseñe al usuario cuando se da una excepción, pero dicha practica no se ha seguido en el caso de está aplicación, donde se enseña mucha más información de la que debería enseñarse.

En el caso de Ruby OnRails, esta condición suele ser especialmente peligrosa, ya que además de la traza de error, en muchas ocasiones se incluye información sensitiva sobre el estado interno de la aplicación, incluyendo detalles tan importantes como la traza completa de la aplicación, la traza completa del framework, parámetros incluidos en la petición, Headers de la respuesta y el volcado completo de la sesión

Como el lector se imaginará, en la sesión se suelen incluir valores muy importantes sobre el funcionamiento de la aplicación y el hecho de que un atacante tenga acceso a dichos valores es una situación que puede ayudar a dicho atacante a comprometer el funcionamiento de la aplicación. Por otro lado, también es importante comprender el funcionamiento de Ruby OnRails y como es su implementación del modelo MVC, especialmente sobre la capa de Controladores.

Controladores en Ruby OnRails

Las aplicaciones en Ruby OnRails siguen el modelo MVC, donde cada una de las capas de la aplicación se encuentran delimitadas y cada una hace lo que debe de hacer, por ejemplo la capa de presentación se encarga de presentar al usuario los elementos visuales incluyendo tags HTML, Imágenes y otros recursos necesarios del lado del cliente, la capa de controladores se encarga de recibir los parámetros del usuario (capa de presentación) y realizar cualquier tipo de lógica de negocio, como por ejemplo validación de datos. Finalmente la capa persistente del modelo, recibe los datos de la capa de controladores y se encarga de realizar operaciones de acceso, consulta, modificación e inserción de datos (típicamente en una base de datos). Esto es, a grandes rasgos, el funcionamiento del módelo Vista-Controlador (MVC). Esté modelo no solamente es ampliamente utilizado para el diseño de aplicaciones en Ruby, es también bastante frecuente encontrarse con aplicaciones escritas en Java, .Net y Python que lo emplean, sin embargo, en el caso de Ruby OnRails su funcionamiento sigue las mismas pautas que siguen los servicios RESTFul. Desde el punto de vista de un atacante, resulta especialmente interesante la capa de controladores, ya que es allí donde frecuentemente reside la lógica de la aplicación y es la capa que sirve de intermediaria entre la capa de presentación y acceso a datos.

Métodos y Acciones en Ruby OnRails

Todos los controladores en Ruby OnRails son simplemente clases que extienden las funcionalidades de la clase ApplicationController y como ya se ha mencionado anteriormente, siguen las mismas pautas de los servicios RESTFul, donde se puede definir una clase y cada uno de sus métodos puede ser accedido por los clientes como si se tratará de un servicio, por ejemplo:

Class AppControler < ApplicationController

def test

end

En una definición simple de controlador donde el método test puede ser accedido desde la aplicación si el usuario accede a una ruta similar a la siguiente: http://IP_ADDRESS/app/test el usuario podrá invocar la lógica del controlador. Evidentemente, se pueden implementar ciertas restricciones de seguridad sobre quienes pueden invocar dicho método y bajo que condiciones, pero a veces pasa que no se consigue restringir adecuadamente el acceso a determinadas funcionalidades que pueden dar suficiente información a un atacante para llevar a cabo sus “hazañas”.

Ahora bien, tal como se ha visto en una publicación anterior, el formulario de registro de usuarios tiene una vulnerabilidad SQL Injection que permite realizar un bypass del mecanismo de autenticación, sin embargo, no es lo único que podemos sacar de allí, si vemos con detenimiento la información que contiene el error, podemos identificar fácilmente el nombre de la clase controlador y su correspondiente método

ControllerRoR

Como se puede apreciar, el controlador tiene por nombre AccountController y el método que se ha utilizado de dicho controlador (y ha arrojado una excepción) se llama signup Tal como se ha visto anteriormente, la forma de llamar a este controlador ha sido por medio de la siguiente url:

http://192.168.1.41:3000/account/signup

Como se puede apreciar, se ha seguido la misma nomenclatura de un servicio RESTFul, donde el nombre del servicio seria “account” (Nombre de la clase en minúsculas y removiendo la palabra Controller) y posteriormente el nombre del método.

Además, de lo anterior, un poco más abajo, justo después de la traza completa de la excepción, se puede apreciar que también es posible acceder a los valores de sesión que se han utilizado en el Controlador.

NOTA:

Es importante resaltar en este punto, que las sesiones en Ruby OnRails no son como las sesiones en otros lenguajes de programación web tales como JSP, PHP, ASP, ColdFusion, etc. Donde la sesión es una tabla hash que se encuentra almacenada en memoria, en Ruby OnRails, pueden haber varios tipos de sesiones que se diferencian por la forma en la que almacenan la información, algunos de estos mecanismos son:

  • Almacenamiento de datos en el cliente por medio de cookies.
  • Almacenamiento de datos en base de datos usando un objeto ActiveRecord.
  • Almacenamiento de datos en la cache de Rails.

En el primero de los casos, toda la información almacenada en la cookie de sesión se encuentra firmada pero no cifrada (solamente se encuentra codificada en base64 para facilitar el proceso de serialización/deserialización de objetivos, del mismo modo que lo hacen frameworks como JSF y ASP.NET) esto quiere decir, que cualquiera con acceso a dicha cookie, podrá leerla, pero si decide editarla, Rails no la aceptará como valida, ya que su hash de firmado ha sido alterado.

Continuando con el mensaje de error, la información sobre la sesión (almacenada en el cliente) y otros detalles sobre la petición, los headers de la misma y la respuesta, se enseñan en la siguiente imagen.

RORError

Como se puede ver, para está petición en concreto no hay demasiada información, pero que pasa con otros puntos de la aplicación? Además, todo lo que hemos visto, tiene un problema y es que en la medida de que una aplicación va siendo desarrollada y dependiendo de la cantidad de funcionalidades que pueda contener, el número de métodos que se van incluyendo en los controladores puede llegar a ser inmanejable, lo que al final puede resultar en que alguno que otro método que inicialmente era solamente para hacer “pruebas” termina siendo expuesto de forma accidental en un entorno productivo. Son cosas que suelen pasar y que cuando pasan… más vale que estés lejos de allí 🙂

JUGANDO Y GANANDO AL POKER

Después de considerar lo explicado anteriormente, una de las posibilidades que tiene un atacante es buscar y encontrar controladores mal configurados y que permitan acceder a información sensitiva o que con los que pueda realizar operaciones que le den algún nivel de acceso o control sobre la aplicación.

Ahora bien, cuando utilizamos algún framework para realizar pruebas unitarias (como JUnit en Java), frecuentemente se suele utilizar el prefijo “test_” y el nombre del método, es probable que se pueda encontrar algún método que sea valido para tal fin, se trata de una posibilidad valida al problema de que desde el lado del cliente no es posible saber cuales son los nombres de lo métodos validos, es necesario ser creativo y tratar de pensar como lo ha hecho el programador (algo que no es difícil para muchos de vosotros que como yo, ya sois programadores 😀 ). Otra alternativa es intentar con los nombres de métodos más frecuentes para la aplicación que estamos manejando, por ejemplo, en el caso del juego de POKER: bet, show, status, bet_max, bet_min, etc. Son nombres de métodos más que probables, por lo tanto, también es probable encontrarse alguno como test_bet, test_show, etc.

Con esto en mente, se pueden hacer algunas pruebas, en primer lugar, cuando entramos al juego de Poker, podemos ver que hacemos la siguiente petición:

http://192.168.1.41:3000/video_poker/show

Con lo cual, podemos asumir 2 cosas:

  1. El nombre del controlador es: Video_pokerController
  2. Uno de los métodos de dicho controlador es show.

Hasta esté punto, no hay novedades. Podemos jugar un poco, hacer algunas apuestas, ganar y perder un poco de dinero, etc.

ROR_BJ1

ROR_BJ2

Ahora bien, si accedemos directamente al controlador “bet” sin hacerlo por medio del correspondiente botón, que es lo que ocurre?

ror_hand1

No demasiado al parecer, simplemente se ha perdido la interfaz principal con sus frames correspondientes, pero al parecer se conservan las funcionalidades. Por otro lado, también podemos apreciar, que no es posible jugar sin antes realizar una apuesta, ya que al pinchar sobre el botón “Draw” inmediatamente se enseña un mensaje de error indicando que, no puedes jugar sin antes hacer tu apuesta.

BJ_Validation

Este control, aparentemente se realiza desde la capa de presentación, por lo tanto, cabe preguntarse, que ocurrirá, si en lugar de realizar la petición al controlador por medio de la capa de presentación, se realiza directamente al controlador? Si nuestras suposiciones son correctas, no habría ningún tipo de validación sobre la cantidad de dinero que debe apostar.

BJ_LOL

Vaya sorpresa! No solamente hemos podido saltarnos el filtro que nos obligaba a realizar una apuesta, sino que encima, nos han dado 400.000 Chips para jugar, hemos ganado un “Royal Flush”. Ha sido coincidencia? Vamos a realizar la misma petición (http://192.168.1.41:3000/video_poker/draw) unas cuantas veces más:

BJ_Win1

BJ_Win2

En todos los casos, cuando accedemos directamente al Controller, en lugar de hacerlo por medio de la capa de presentación, nos encontramos con que ganamos la partida y tenemos las mejores cartas. Lo que indica claramente, que la aplicación web, en concreto, este juego, no se encuentra debidamente securizado y que todas las validaciones y comprobaciones sobre las cartas se llevan a cabo en la capa de presentación, al saltarnos dicha capa, el controlador, siempre asume que somos “ganadores” ya que las validaciones de negocio, se hacen en la capa de presentación en lugar de hacerse donde se deben hacer, en el controlador. Este es un problema que suele ocurrir en aplicaciones que utilizan MVC, a veces es difícil diferenciar en donde deben de ir ciertas validaciones lógicas y terminan siendo puestas en el peor de los sitios (como en este caso) y desafortunadamente, se suele echar la culpa a los desarrolladores, cuando en realidad, suele ser un problema de diseños mal estructurados, pobremente definidos y mediocremente elaborados por personas que no diferencian una linea de código de una patata.

En la próxima publicación, se hablará un poco más sobre controladores inseguros en Ruby OnRails utilizando como ejemplo la aplicación HacMe Casino y analizaremos, algunas practicas inseguras en el manejo de sesiones en Ruby OnRails.