Ampliando las capacidades de Neto: cómo desarrollar nuevos plugins de análisis

lunes, 28 de mayo de 2018

En posts anteriores hemos introducido Neto como analizador de extensiones de navegador. La primera versión que liberamos, 0.5.x incluía un CLI, una interfaz JSON-RPC y podía ser utilizada directamente desde vuestros scripts. En la serie 0.6.x hemos ganado en estabilidad y hemos añadido además algunas características interesantes como la consola interactiva para hacer del analizador una herramienta con la que se pueda interactuar. Sin embargo, aún no hemos hablado de cómo podemos ampliar las funcionalidades de Neto para adecuarlas a nuestras necesidades.

Un sistema de plugins para ganar flexibilidad
Pese a las necesidades de investigación que podamos tener nosotros desde ElevenPaths, puede ocurrir que otros analistas de seguridad quieran también llevar a cabo otras tareas en las que nosotros no hemos pensado. Con el objetivo de flexibilizar su uso lo más posible, hemos pensado en un sistema de plugins que os permita diseñar vuestros propios módulos. Recordad en este punto que siempre podremos instalar la versión más reciente desde PyPI con:

$ pip3 install neto --user --upgrade

Pero antes, os hacemos una breve descripción de cómo funciona Neto. Cada extensión se representa en Python en un objeto que carga los métodos de análisis oficiales que hemos incluido en neto/plugins/analysis. Neto automáticamente ejecutará la función definida como runAnalysis en la que recibiremos dos parámetros diferentes que podremos usar según nuestras necesidades:
  • extensionFile. La ruta local en la que se encuentra el fichero comprimido de la extensión.
  • unzippedFiles. Un diccionario en el que las claves es la ruta relativa del fichero descomprimido encontrado en la extensión y el valor la ruta absoluta en donde se ha descomprimido en el sistema. Por defecto, se trata de una ruta temporal.
        {
            "manifest.json": "/tmp/extension/manifest.json"
            …
        }
De esta manera, en función de lo que queramos hacer podremos optar por una o por otra opción. Por ejemplo, si queremos trabajar únicamente con los archivos .png presentes en la extensión, es más fácil que lo hagamos usando unzippedFiles, pero si queremos analizar el fichero en sí mismo podemos usar extensionFile. Dependerá de nuestras necesidades. Lo que sí que tenemos que tener en cuenta es que debe devolver siempre un diccionario en el que la clave sea el nombre que damos a nuestro procedimiento y el valor los resultados del mismo. Así este nuevo atributo será añadido al resto de elementos ya obtenidos.

Para definir nuestros propios módulos de análisis en estas primeras versiones de Neto bastará con que generemos unos pequeños scripts en Python que almacenará en su carpeta local ~/.config/ElevenPaths/Neto/plugins/. Las características de estos módulos de usuario son idénticas a las de los módulos oficiales solo que se cargarán a petición del usuario.

Creando nuestro primer plugin para Neto
Para facilitarnos el proceso, hemos incluido una template de plugin con cada instalación en ~/.config/ElevenPaths/Neto/plugins/template.py.sample. Empezar a desarrollar es fácil a partir de esta plantilla y para verlo haremos un plugin sencillo que contará el número de ficheros que contiene la extensión.

def runAnalysis(**kwargs):
    """
    Method that runs an analysis

    This method is dinamically loaded by neto.lib.extensions.Extension objects
    to conduct an analysis. The analyst can choose to perform the analysis on
    kwargs["extensionFile"] or on kwargs["unzippedFiles"]. It SHOULD return a
    dictionary with the results of the analysis that will be updated to the
    features property of the Extension.

    Args:
    -----
        kwargs: It currently contain:
            - extensionFile: A string to the local path of the extension.
            - unzippedFiles: A dictionary where the key is the relative path to
                the file and the the value the absolute path to the extension.
                {
                    "manifest.json": "/tmp/extension/manifest.json"
                    …
                }
    Returns:
    --------
        A dictionary where the key is the name given to the analysis and the
            value is the result of the analysis. This result can be of any
            format.
    """
    results = {}

    # Iterate through all the files in the folder
    for f, realPath in kwargs["unzippedFiles"].items():
        if os.path.isfile(realPath):
            # TODO: Your code here for each file
            pass

    return {__name__: results}

Partiendo del código original, utilizaremos la información almacenada en kwargs["unzippedFiles"] y reutilizaremos el bucle que ya tenemos para contar aquellos elementos que sean ficheros incrementando la variable myCounter que inicializamos al principio del método.

    myCounter = 0

    # Iterate through all the files in the folder
    for f, realPath in kwargs["unzippedFiles"].items():
        if os.path.isfile(realPath):
            # TODO: Your code here for each file
            myCounter += 1

    return {"num_files": myCounter}

Ahora guardaremos el fichero en la carpeta en cuestión como ~/.config/ElevenPaths/Neto/plugins/hello_world.py por ejemplo. Solo falta arrancar Neto con una nueva extensión (por ejemplo, con el CLI) y comprobar la salida:

$ neto analyser -e ./my_demo.xpi
$ cat /home/USER/.config/ElevenPaths/Neto/data/analysis/854…78f.json | grep num_files
    "num_files": 151,

¡Ya tenemos nuestro primer plugin para Neto!

Ahora bien, ¿cómo comparto mis plugins con el resto?
Una vez que ya hayas definido tu plugin y lo hayas probado en una instancia local, te invitamos a que lo compartas con nosotros para mergearlo con el proyecto principal. Logueado con tu usuario, haz un fork del proyecto en tu plataforma y clona tu repositorio bifurcado en tu máquina. Lo hacemos así porque para prevenir circunstancias no deseadas, al pushear el contenido al repositorio principal Github te lo rechazará al no estar autorizado.

$ git clone https://github.com/USER/neto
$ cd neto

Una vez descargado, copia el fichero que ya has probado localmente al repositorio. Por ejemplo, en un sistema GNU/Linux podrás recoger el plugin de la carpeta ~/.config/ElevenPaths/Neto/plugins/hello_world.py y cópialo en la carpeta de neto/plugins/analysis.

$ cp ~/.config/ElevenPaths/Neto/plugins/hello_world.py neto/plugins/analysis

Una vez añadido el fichero, solo falta añadirlo, commitear los cambios y pushearlo a tu repositorio.

$ git add neto/plugins/analyser
$ git commit -m "Add hello_world plugin following the tutorial"
$ git push origin master

Una vez autenticado con tu usuario, solo falta hacer el pull request para que lo podamos revisar y mergear con el proyecto principal. Puede ser que en ese proceso de revisión te pidamos aclarar algunas cosas por lo que es conveniente mantener una cierta homogeneidad utilizaremos las directrices de estilo marcadas por el PEP-8 donde sea posible.

De todas formas, la única condición general es que la respuesta generada sea un diccionario en el que la key sea un elemento que identifique tu análisis de forma unívoca y no entre en conflicto con el resto de métodos ya implementados. Ten en cuenta también que en en el caso de que tu plugin dependa de algún otro paquete que no se encuentre por defecto en Python 3, será necesario actualizar el setup.py para que se satisfagan las correspondientes dependencias. Aún así, no estarás en el solo el proceso. ¿Te animas a probar?

Félix Brezo
Innovation and Laboratory Team ElevenPaths

No hay comentarios:

Publicar un comentario