Extraer información de perfiles Firefox con python

Hoy vamos a ver como extraer información relevante de las bases de datos que Mozilla Firefox utiliza para sus perfiles de usuario. Lo vamos ha hacer bajo el sistema operativo Linux Mint Rosa, Firefox 46.0.1 y Python. En caso de que queráis hacerlo desde otra plataforma o lenguaje, las variaciones no deberían suponer ningún reto. Así mismo, la forma en que mozilla almacena sus datos podría variar sustancialmente en futuras versiones al iguas que en versiones más antiguas (downloads.sqlite, signons, etc…).

Empezaremos por decir que en nuestro perfil de usuario de firefox (el cuál podremos encontrar en: /home/{USER_NAME}/.mozilla/firefox/{PROFILE_NAME}) se almacena mucha información. Aquí vamos a centrarnos en los siguientes archivos:

–  cookies.sqlite
–  formhistory.sqlite
–  places.sqlite

Estos archivos son bases de datos SQLite, y accederemos a ellos desde python para obtener la información que necesitemos. Empecemos por el primero de ellos ‘cookies.sqlite’. Para conectarnos a la base de datos abriremos una consola python en la carpeta de nuestro perfil e importaremos sqlite3. Luego estableceremos la conexión y obtendremos el nombre de las tablas contenidas en dicha base de datos:

conn = sqlite3.connect('cookies.sqlite')
c = conn.cursor()
c.execute('SELECT name FROM sqlite_master WHERE type="table"')

Hecho esto podremos ver las tablas obtenidas y obtener información de ellas. En este caso la BD solo contiene una tabla llamada moz_cookies de la que podemos obtener un esquema orientativo de la siguiente forma:

c.execute('PRAGMA table_info(moz_cookies)')

Con ello sabremos que el esquema de dicha tabla es:

(0, u'id', u'INTEGER', 0, None, 1)
(1, u'baseDomain', u'TEXT', 0, None, 0)
(2, u'originAttributes', u'TEXT', 1, u"''", 0)
(3, u'name', u'TEXT', 0, None, 0)
(4, u'value', u'TEXT', 0, None, 0)
(5, u'host', u'TEXT', 0, None, 0)
(6, u'path', u'TEXT', 0, None, 0)
(7, u'expiry', u'INTEGER', 0, None, 0)
(8, u'lastAccessed', u'INTEGER', 0, None, 0)
(9, u'creationTime', u'INTEGER', 0, None, 0)
(10, u'isSecure', u'INTEGER', 0, None, 0)
(11, u'isHttpOnly', u'INTEGER', 0, None, 0)
(12, u'appId', u'INTEGER', 0, u'0', 0)
(13, u'inBrowserElement', u'INTEGER', 0, u'0', 0)

y prodemos obtener la información que queramos haciendo las consultas necesarias.

En la base de datos ‘formhistory.sqlite’, de la misma forma que en la anterior, obtendremos que consta de dos tablas: ‘moz_formhistory’ y ‘moz_deleted_formhistory’. Cuyos esquemas son, respectivamente:

'moz_formhistory'
(0, u'id', u'INTEGER', 0, None, 1)
(1, u'fieldname', u'TEXT', 1, None, 0)
(2, u'value', u'TEXT', 1, None, 0)
(3, u'timesUsed', u'INTEGER', 0, None, 0)
(4, u'firstUsed', u'INTEGER', 0, None, 0)
(5, u'lastUsed', u'INTEGER', 0, None, 0)
(6, u'guid', u'TEXT', 0, None, 0)
'moz_deleted_formhistory'
(0, u'id', u'INTEGER', 0, None, 1)
(1, u'timeDeleted', u'INTEGER', 0, None, 0)
(2, u'guid', u'TEXT', 0, None, 0)

 

De estas tablas podremos extraer información de los campos almacenados al rellenar formularios, etc.

Y nos queda hablar de la más extensa de las tres, ‘places.sqlite’. Siguiendo los métodos ya citados, podemos comprobar que contiene múltiples tablas: moz_places, moz_historyvisits, moz_inputhistory, moz_bookmarks, moz_bookmarks_roots, moz_keywords, moz_favicons, moz_annos, moz_anno_attributes, moz_items_annos, sqlite_sequence, sqlite_stat1. Podemos intuir que para extraer información sobre los marcadores utilizaremos las tablas ‘moz_bookmarks’ y ‘moz_bookmarks_roots’ por ejemplo. Muchas de ellas están enlazadas por claves foráneas como por ejemplo la tabla ‘moz_favicons’ que almacena iconos relativos a webs que se encuentran en otras tablas como ‘moz_places’.

Estas bases de datos, además de para curiosear, son muy útiles cuando un perfil se corrompe y queremos recuperar información del mismo (historial, marcadores, preferencias de sitios web, etc.). Para este caso, también podremos extraer las contraseñas guardadas en el navegador y desencriptarlas en caso de que las hayamos olvidado y no podamos acceder a nuestro perfil. Para realizar esto último se utilizan los archivos ‘logins.json’ y ‘key3.db’. En el primero se almacenan los datos de usuario y contraseña de forma encriptada mientras que en el segundo se encuentra la clave para poder desencriptarlos. El proceso para realizarlo lo dejo para otro post en el que también mostraré algunos ejemplos de lo visto aquí.

Concurrencia en C++

Hoy vamos a ver un poco de concurrencia en C++. Al ser un tema tan extenso, sólo será posible dar una primera visión general a los conceptos más básicos que nos ofrece pero, iremos ampliando contenido en futuras ocasiones. Aclarar que usaremos para los ejemplos el estándar C++11 por lo que algunos ejemplos no funcionarán si se usa uno previo. Para el uso de este estándar basta con tener instalado el compilador en nuestro sistema (pe: sudo apt-get install g++) y, al compilar, indicarle las siguientes opciones:

g++ ejemplo.cpp -o ejemplo -pthread -std=c++11

Al contrario que en Java, en C++ el hilo se lanza directamente al ser creado por lo que no es necesario expresarlo en el código más que con:

std::thread hilo();

Si existe, de forma similar a Java, el método join para la gestión de co-rutinas. Además podemos hacer referencia al hilo actual con la variable std::this_thread. Al ser lanzados los hilos con sólo crearse, lo más común es utilizar un puntero a función para la instanciación del hilo, el cual llamará a esa función al ejecutarse. En nuestro caso y por hacerlo un poco más interesante, ya que la función que contiene el código a ejecutar por el hilo es muy pequeña, utilizaremos una funcion lambda de C++:

#include<iostream>
#include<thread>
#include<vector>

int main(){
std::vector<std::thread> hilos;

for(int i = 0; i < 5; ++i){
hilos.push_back(std::thread([](){
std::cout << "Hola desde el hilo " << std::this_thread::get_id() << std::endl;
}));
}

for(&auto thread : hilos){
thread.join();
}

return 0;
}

Debido al entrelazado de instrucciones, el resultado de este programa es totalmente impredecible ya que no podemos predecir si la salida será la que esperamos o no. Para solucionar esto existen muchas formas diferentes pero vamos a utilizar un equivalente a los semáforos llamado mutex. Crearemos una estructura contador con métodos para incrementar y decrementar su valor. Al ser estas dos operaciones no atómicas, seguiremos teniendo el problema del entrelazado de instrucciones que nos llevará a resultados inesperados. Para solucionar el problema protegeremos estas operaciones para preservar la exclusión mutua y que la salida del programa sea la correcta y predecible.

Por un lado vamos a tener la estructura contador segura como sigue:

#include<mutex>

struct Contador {
std::mutex mutex;
int valor;

Contador() : valor(0) {}

void incremento(){
mutex.lock();
++valor;
mutex.unlock();
}

void decremento(){
mutex.lock();
--valor;
mutex.unlock();
}
};

Y para ilustrar su funcionamiento tenemos una función main muy parecida a la anterior:

#include <iostream>
#include <thread>
#include <vector>

int main(){
Contador contador;

std::vector<std::thread> hilos;
for(int i = 0; i < 5; ++i){
hilos.push_back(std::thread([&contador](){
for(int i = 0; i < 1000; ++i){
contador.incremento();
}
}));
}

for(auto& thread : hilos){
thread.join();
}

std::cout << contador.valor << std::endl;

return 0;
}

Se puede observar que podemos aumentar el número de hilos o el número de operaciones y se sigue preservando la exclusión mutua. Si hacéis la prueba de quitar el mutex de la estructura contador y lanzáis el programa, os daréis cuenta de los múltiples resultados diferentes que obtendréis. En el futuro iremos viendo el funcionamiento de técnicas algo más avanzadas.

sshLogger.py

Continuando con los servidores OpenSSH, ya dijimos en el post anterior que, al ponerlos en funcionamiento de forma rápida, podemos sufrir multitud de ataques (o intentos de ataque al menos). Para poder monitorizar lo que ocurre, en lo que respecta a los accesos, en nuestro servidor existen los ficheros de log. Por defecto, los eventos relativos a nuestro servidor SSH están almacenados en /var/log/auth.log.

Uno de los inconvenientes que puede tener es que la información que se encuentra en ese fichero corresponde a muchos otros servicios del sistema por lo que en algunos casos hay que recurrir a herramientas externas para poder buscar lo que nos interesa dentro del fichero. Una forma sencilla de visualizar de forma cómoda y organizada los datos relativos a nuestro servidor SSH en el fichero log antes mencionado o cualquier otro distinto, es usar el script python que podéis encontrar AQUÍ.

Está concebido para OpenSSH-Server bajo sistemas Linux y no requiere la instalación de ningún paquete externo al lenguaje. Organiza y permite ver la información interesante con facilidad y está optimizado para servidores cuyo nivel de log (‘LogLevel’) tiene el valor ‘INFO’.

sshLogger01

  • Monitoriza los siguientes tipos de entradas:

    – [Mes] [Día] [Hora] [Host] sshd: Server listening on [IP] [PORT].
    – [Mes] [Día] [Hora] [Host] sshd: Accepted password for [USER] [IP] [PORT] ssh2
    – [Mes] [Día] [Hora] [Host] sshd: Received disconnect from [IP] x: disconnected by [USER]
    – [Mes] [Día] [Hora] [Host] sshd: pam_unix(sshd:auth): authentication failure; [LOGNAME] [UID] [EUID] [TTY] [RUSER] [RHOST] [USER]
    – [Mes] [Día] [Hora] [Host] sshd: Did not receive identification string from [IP]
    – [Mes] [Día] [Hora] [Host] sshd: Accepted publickey for [USER] from [IP] [PORT] ssh2: [KEY]
    – [Mes] [Día] [Hora] [Host] sshd: message repeated [X] times: [ Failed password for [USER] from [IP] [PORT] ssh2]
    – [Mes] [Día] [Hora] [Host] sshd: reverse mapping checking getaddrinfo for [ADDR. INFO] [IP] failed – POSSIBLE BREAK-IN ATTEMPT!

sshLogger02

Permite visualizar solo las que se elijan previamente con los argumentos del script además de visualizarlas completas, una a una, no mostrarlas y/o almacenar la salida seleccionada en un fichero de nuestra elección (dicho fichero puede existir previamente y ser sobreescrito/encadenado o ser creado si es necesario).

sshLogger03

Las opciones disponibles son las siguientes:

Usage: sshLogger.py [-hspcfnkrb] [-o|-d] [-l <file>]

–version                Muestra la versión del programa
-h, –help               Muestra la ayuda

Opciones SSH:
-s, –server-up        Muestra las veces que un servidor se puso en funcionamiento
-p, –acc-passwords    Muestra las sesiones abiertas
-c, –closed-sessions  Muestra las sesiones cerradaas
-f, –failed-auth      Muestra las autentificaciones fallidas
-n, –no-idents        Muestra las identificaciones no recibidas
-k, –public-keys      Muestra las claves públicas aceptadas
-r, –repeat           Muestra los mensajes repetidos
-b, –break-in         Muestra los intentos de ataque

Display Options:
-o, –one-by-one       Muestra las entradas una por una
-d, –no-display       No muestra las entradas por la salida estándar

File Options:
-l <FILE>, –log=<FILE>   Guarda las entradas seleccionadas en <file>

Las opciones [-spcfnkrb] son compatibles entre si y es necesario que se elija, al menos, una de ellas. Ĺas opciones [od] son opcionales e incompatibles entre sí. La opción [l] es optativa y compatible con cualquiera de las anteriores opciones.

Es un script que se realizó en Python por motivos ajenos a la eficiencia. Con un script en Bash podría realizarse la misma tarea en muchas menos líneas de código pero el objetivo era desempolvar un poco Python y el tema elegido se lo debemos al post anterior. Espero que os ayude en algo.

Saludos y que no os maten los gurús.