jueves, 2 de abril de 2009

Mozilla Firefox 3: análisis del historial

No cabe duda que en los últimos tiempos el navegador Mozilla Firefox está arrebatando gran parte de la cuota de mercado al hasta ahora omnipresente Internet Explorer. No voy a entrar a valorar los motivos de esta rápida ascensión, al igual que no voy a discutir acerca de la seguridad que acredita éste o aquel; baste subrayar que, desde mi punto de vista y como sucede con todo, cada uno tiene sus cosas buenas y sus cosas malas.

Una vez aclarado este punto sigamos con el siguiente. Voy a intentar a lo largo de esta entrada analizar los rastros derivados del uso habitual de la última versión disponible para este popular navegador (la 3 en el momento de escribir estas líneas), pero voy a intentar también minimizar el uso de herramientas de terceros. Entiéndase esto no con la intención de desaconsejar su utilización, sino mas bién al contrario, además de conseguir evitar el tener que atarnos a una plataforma de análisis concreta.

Vaya por delante, y a modo de disclaimer, subrayar que ni soy programador ni me dedico profesionalmente al mundo del análisis forense de sistemas, que más quisiera yo. Únicamente me encanta aprender, me apasiona esta rama de la seguridad informática y, cuando lo consigo, me siento enormemente realizado por enseñar lo que buenamente puedo. Dicho todo lo anterior, entremos en materia de una vez.

Perfiles en Firefox

Para almacenar el historial, al igual que la mayoría de ficheros utilizados por firefox, se utiliza el concepto de perfil de usuario. Cada cuenta de usuario tiene asignado un perfil por defecto, pero puede disponer de más, de forma que cada uno de ellos almacene una configuración diferente. Podemos gestionar los perfiles arrancando el navegador mediante el flag "-p".

Independientemente del número de perfiles existentes siempre habrá uno de ellos configurado como perfil predeterminado, de forma que será el utilizado para iniciar el navegador, a menos que se especifique lo contrario durante el lanzamiento de la aplicación, precediendo el nombre asignado al perfil con la opción "-P".

El perfil predeterminado para una cuenta de usuario en un sistema Windows XP se almacena en la siguiente ubicación:

%UserProfile%\Datos de programa\Mozilla\Firefox\Profiles\[cadena].default

Para un sistema Windows Vista:
%UserProfile%\AppData\Roaming\Mozilla\Firefox\Profiles\[cadena].default

Y para un sistema Linux:
$HOME/.mozilla/firefox/[cadena].default

También podremos comprobar cual es el perfil por defecto utilizado en Firefox así como la ubicación y nombre asignado al resto de perfiles existentes en el fichero profiles.ini, ubicado dentro del directorio Firefox de la ruta indicada para cada uno de los diferentes sistemas analizados.

Allí se almacenará toda la información que compone el perfil de usuario en cuestión, bueno, toda no. La caché de navegacion se almacenará en otra ubicación pero eso, quizás, sea tema para otro artículo.

Firefox y sqlite

Sqlite es un sistema de base de datos SQL transaccional, que no precisa de un servidor y, que por lo tanto, no requiere configuración. Cada base de datos sqlite consta de un único fichero que puede ser accedido de forma concurrente por varias aplicaciones siempre que éstas tengan permisos de acceso, a nivel del sistema operativo, sobre el mismo. ¿Y qué relación tiene sqlite con el "zorro de fuego"?

Históricamente, y hasta la aparición de la versión 3 de Firefox, toda la información relativa al historial de navegación del usuario así como los datos introducidos en los formularios se almacenaban en ficheros con formato Mork. Este tipo de formato, a pesar de contener la información en texto plano no resultaba fácilmente extensible, lo que hizo volver la vista al grupo de desarrollo de Firefox hacia otras alternativas, y aquí es donde entra en juego sqlite.

Si analizamos el contenido del perfil predeterminado o cualquiera de los existentes en el sistema, en el caso de existir más de uno, encontraremos varios ficheros con extensión sqlite, requisito no imprescindible pero sí bastante esclarecedor. La lista estaría compuesta, en orden alfabético, por los siguientes elementos:
  • content-prefs.sqlite
    Almacena configuraciones específicas establecidas por el usuario para las diferentes páginas visitadas.

  • cookies.sqlite
    Guarda informacion sobre las cookies descargadas en el sistema durante las sesiones de navegacion del usuario.

  • downloads.sqlite
    Guarda información sobre las últimas descargas realizadas utilizando el navegador, siempre y cuando el usuario no haya pulsado el botón "Limpiar lista".

  • formhistory.sqlite
    Mantiene los datos introducidos por el usuario en los campos de los diferentes formularios.

  • permissions.sqlite
    Almacena todos aquellos sitios web a los que se permite abrir pop-ups.

  • places.sqlite
    Sin duda la base de datos más jugosa en lo que a un análisis forense se trata. Almacena el historial de navegación y los marcadores creados por el usuario.

  • search.sqlite
    Contiene la información relativa a los motores de búsqueda definidos en la barra de firefox.

  • urlclassifier2.sqlite
    Almacena sitios conocidos por su actividad relacionada con el phising y se actualiza de forma diaria desde un servidor de Google.

Vale, ya sabemos el formato que tienen todos estos ficheros, tenemos un poquito de idea de SQL y como además somos muy curiosos, vamos a analizar estos ficheros a pelo utilizando para ello los binarios de sqlite, disponibles tanto para Windows como para Linux. Dado que el funcionamiento de sqlite es independiente del sistema operativo subyacente vamos a ver primero como instalarlo en ambas plataformas. Primero para Linux, y más concretamente, para Ubuntu:
$ apt-get install sqlite3

Y para Windows bastará con descargar el binario de la última versión disponible desde la siguiente URL:

http://www.sqlite.org/sqlite-3_6_12.zip

Vale, ya tenemos instalado el paquete sqlite en Ubuntu (por defecto se ubicará en /usr/bin) y en Windows lo hemos desempaquetado en un directorio incluido en el path del sistema. Para abrir cualquiera de los ficheros utilizados por firefox ejecutaremos:
sqlite3 nombrefichero

En cualquier momento podemos lanzar el comando .help para acceder a la ayuda.

Lo primero, obviamente, será conocer las tablas que componen la base de datos en cuestión. Para ello, y desde el interfaz de sqlite, ejecutaremos:
sqlite> .tables

Una vez determinado este punto será adecuado conocer la estructura interna de cada una de las tablas para poder dirigir las consultas de forma adecuada. Para ello tenemos dos opciones, volcar la estructura de todas las que componen la base de datos a la vez:
sqlite> .schema

o volcar la estructura de cada una de ellas de forma independiente:
sqlite> .schema nombretabla

Ahora, y con la información obtenida mediante cualquiera de los comandos anteriores, ya podemos dirigir adecuadamente nuestras consultas. Un ejemplo de consulta genérica:
sqlite> select * from nombretabla;

Si la tabla escogida contiene gran cantidad de registros la salida obtenida como resultado de la consulta anterior no será fácilmente analizable. El formato de salida utilizado por defecto es algo críptico cuando los campos que componen cada uno de los registros tienen un tamaño considerable, dado que la sensación visual de agrupación por columnas pierde todo su sentido.

Indudablemente lo mejor sería poder almacenar el resultado obtenido en un fichero externo. Para ello utilizaremos el siguiente comando:
sqlite> .output nombrefichero

Si ahora volvemos a ejecutar el mismo comando "select" indicado con anterioridad obtendremos el resultado en nombrefichero, el cual podremos analizar tranquilamente a posteriori.

Pero si queremos rizar el rizo, el formato de salida por columnas estando delimitadas por el carácter "|" de forma predeterminada tampoco resulta el más adecuado. Repitamos el proceso anterior pero volquemos los resultados en formato csv que, como todos sabemos, podremos abrir con excel (MS Office) o scalc (OpenOffice):
sqlite> .output nombrefichero

sqlite> .mode csv
sqlite> select * from nombretabla;
sqlite> .quit

Pues esto es básicamente lo que hace el programa Firefox 3 Extractor, sólo que evitándonos el tener que aprender SQL y todos los pasos previos. Y como a mí también me gusta ahorrarme faena lo máximo posible he generado dos scripts, un fichero batch para Windows y un script de bash para Linux, que realizan el volcado del contenido de todas las bases de datos que componen el perfil de firefox de forma automatizada (al final del artículo he incluido un enlace para descargarlos).

Y ahora, programando

Primero vamos a preparar el entorno para sistemas Windows. Personalmente me gusta el entorno de desarrollo gratuito Dev-C++, y por lo tanto es el que voy a utilizar para describir el proceso.

El primer paso será descargar los ficheros necesarios para generar la librería de sqlite. Para ello utilizaremos la siguiente URL:

http://www.sqlite.org/sqlitedll-3_6_12.zip

Una vez descargado y desempaquetado obtendremos como resultado dos ficheros: sqlite.dll y sqlite.def.

Ambos ficheros son necesarios para generar la librería que posteriormente utilizará el linker para compilar nuestros programas. Si hemos realizado la instalación por defecto del IDE Dev-C++ copiaremos ambos ficheros en el siguiente directorio:
C:\Dev-Cpp\mingw32\bin

Abriremos una consola de comandos, nos desplazaremos al directorio anterior, y ejecutaremos la siguiente orden:
dlltool.exe -D sqlite3.dll -d sqlite3.def -l libsqlite3.a

Como resultado hemos obtenido el fichero libsqlite3.a, el cual copiaremos en el directorio:
C:\Dev-Cpp\lib

Ahora solo nos faltará descargar y ubicar adecuadamente el fichero de cabecera sqlite3.h. Para obtenerlo descargaremos el siguiente paquete:

http://www.sqlite.org/sqlite-amalgamation-3_6_12.zip


En su interior encontraremos el header que necesitamos, el cual copiaremos en el directorio:
C:\Dev-Cpp\include

Ahora ya tenemos todo lo que necesitamos. Para obtener un binario que utilice http://www.sqlite.org/ generaremos un nuevo proyecto para una aplicación de línea de comandos (Console Application), incluiremos el fichero de cabecera en el código fuente de nuestro programa y por último agregaremos la libreria libsqlite3.a mediante el menú Proyecto, Opciones del Proyecto y dentro de la pestaña Parámetros pulsaremos el botón Añadir Biblioteca u Objeto. Allí localizaremos la librería dentro del directorio "C:\Dev-Cpp\lib" y cerraremos la ventana de diálogo mediante el botón Aceptar. Ya tenemos adecuadamente preparado nuestro compilador en Windows.

El proceso de instalación de los paquetes necesarios en Ubuntu es mucho más sencillo, y todo gracias a la potencia de apt, en mi opinión el mejor gestor de paquetes del mundo del pingüino:
$ apt-get install sqlite3 libsqlite3-0 libsqlite3-dev

$ which sqlite3
/usr/bin/sqlite3
$ whereis libsqlite
libsqlite: /usr/lib/libsqlite3.a /usr/lib/libsqlite3.la /usr/lib/libsqlite3.so

Los últimos comandos sólo los he incluido para mostrar la ubicación de los ficheros necesarios que acabamos de instalar. Ahora, y suponiendo que ya tenemos el código fuente en el fichero codigo.c utilizaremos el siguiente comando para obtener el ejecutable desde Linux:
$ gcc -o codigo -l sqlite3 codigo.c

Ejemplo de programa en C

Vamos a analizar un programa muy sencillo que nos permitirá obtener información sobre el historial de navegación del usuario y que está basado en el ejemplo que se incluye en la propia página de sqlite. También utiliza la consulta SQL indicada en Forensics Wiki, aunque un poco modificada. Si queréis descargarlo, junto con el binario resultante, podréis encontrar un enlace al final del artículo.
#include <stdio.h>

#include <sqlite3.h>
#include <stdlib.h>

Esto es obvio, incluimos los ficheros de cabecera necesarios para las funciones utilizadas en el programa.
char *sql = "SELECT datetime(moz_h.visit_date/1000000,'unixepoch','localtime') dateTime,"

"moz_p.url FROM moz_places moz_p, moz_historyvisits moz_h"
"WHERE moz_p.id = moz_h.place_id";

Esta cadena almacena la consulta SQL que nos permitirá obtener los datos, mostrando los valores que resulten de la intersección de dos de las tablas que forman la base de datos places.sqlite.

La primera particularidad está relacionada con la forma en que Firefox almacena los valores para los timestamp, y que se conoce como PRTime. Este valor, un entero de 64 bits, representa el número de milisegundos transcurridos desde la medianoche del 1 de enero de 1970, en formato UTC. Para obtener un valor fácilmente legible utilizaremos la función datetime, proporcionada por sqlite, e indicaremos también 'localtime' para obtener el valor de tiempo local.
int main(int argc, char **argv)

{
sqlite3 *db;
char *zErrMsg = 0;
char **results;
int rows, columns, i, rc;

Función principal y variables utilizadas a lo largo del programa incluyendo un objeto de base de datos, dos punteros, uno para almacenar los posibles mensajes de error y el otro para guardar los resultados de la ejecución de la consulta SQL y varios enteros.
 rc = sqlite3_open("places.sqlite", &db);

if( rc ) {
fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));
sqlite3_close(db);
exit(1);
}

Abrimos el manejador para el objeto de base de datos. Si se produce algún error mostramos un mensaje de advertencia y terminamos la ejecución del programa.
 rc = sqlite3_get_table(db, sql, &results,&rows, &columns, &zErrMsg);

if( rc != SQLITE_OK ){
fprintf(stderr, "SQL error: %s\n", zErrMsg);
sqlite3_free_table(results);
exit(1);
}

Ejecutamos la consulta SQL y, tal cual hicimos antes, en caso de producirse un error, mostramos el mensaje adecuado y terminamos la ejecución del programa.
 for(i = 0; i < columns; i++) {

printf("%s",results[i]);
if((i % 2 ) == 0) printf(",");
else printf("\n");
}
 
for(i = 0; i < rows*columns; i++) {
printf("%s",results[columns+i]);
if((i % 2 ) == 0) printf(",");
else printf("\n");
}

Mostramos los resultados separando cada uno de los campos que componen los diferentes registros obtenidos mediante comas, de forma que, si redirigimos la salida obtenida al ejecutar este programa a un fichero, éste tendrá el archiconocido formato CSV.
 sqlite3_free_table(results);

sqlite3_close(db);
return 0;
 
}

Para finalizar el programa sólo resta liberar las variables utilizadas y cerrar el manejador para el objeto de conexión a la base de datos.

Una vez compilado el programa anterior (ya sea en linux o en Windows) al que llamaremos, por ejemplo, getHistory, lo lanzaremos mediante el siguiente comando:
getHistory places.sqlite > resultado.csv

Ahora ya tenemos un programa que funcionará perfectamente tanto en Windows como en Linux y, además, ya somos capaces de generarnos nuestras propias herramientas.

Códigos y ejecutable getHistory.exe: firefox3.zip

Referencias

Web Browser Forensics I y II

Mork file format

Mozilla Wiki: Places

Mozilla Wiki: The Places database

Mozilla Wiki: Browser History

More Firefox "Forensics" Tools

Forensics Wiki: Mozilla Firefox

Análisis forense de perfiles en Mozilla Firefox

Analisis forense en Firefox 3.x

Firefox 3.X Forensics: Using F3e

SQLite In 5 Minutes Or Less

SQLite Tutorial

6 comentarios:

m313 dijo...

Gran artículo. Hace un tiempo era tarea sencilla descodificar la tabla sqlite de los sitios maliciosos de google y eran URL's muy jugosas con las que jugar: troyanos, phpshells, etc. Lo revisaré.

Un saludo

--
m313
http://www.meleagro.es.kz

conexioninversa dijo...

Tu Dices:

"Vaya por delante, y a modo de disclaimer, subrayar que ni soy programador ni me dedico profesionalmente al mundo del análisis forense de sistemas, que más quisiera yo. Únicamente me encanta aprender, me apasiona esta rama de la seguridad informática y, cuando lo consigo, me siento enormemente realizado por enseñar lo que buenamente puedo. Dicho todo lo anterior, entremos en materia de una vez."

Y yo digo:

Aun queda, pero para el verano o septiembre, hablaremos tu y yo, tienes el perfil perfecto de analista forense informatico.

Homo libris dijo...

¡Un artículo genial!

Enhorabuena por lo exhaustivo. Te ha quedado muy bien.

Saludos.

Anónimo dijo...

"Error al iniciar la aplicacion porque no se encontró sqlite3.dll. La reinstalacion de la aplicacion puede solucionar el problema".

neofito dijo...

Tienes que descargar el siguiente fichero:

http://www.sqlite.org/sqlitedll-3_6_12.zip

y extraer su contenido en el mismo directorio donde tengas la aplicacion getHistory.

Saludos

Anónimo dijo...

Excelente post!, hace poco decidí incorporar MySQL en mis aplicaciones, pero finalmente me decidí por SqLite. El problema que tenía era en la hora de la compilación, pero ahora ese asunto está arreglado. Gracias