Configuración de honeypot

Como parte de mi investigación sobre ciberseguridad, he implementado un entorno para el análisis y la recogida de datos basados en un sistema señuelo o honeypot totalmente funcional y automatizado. Esta página contiene información detallada del proceso de instalación y configuración.

Salta el índice y el diagrama

  1. El honeypot
    1. Elección del sistema anfitrión del honeypot
    2. Instalación de las dependencias del sistema
    3. Creación de la cuenta de usuario
    4. Obtención del código de Cowrie
    5. Instalación de un entorno virtual de Python
    6. Configuración de Cowrie
    7. Personalización de Cowrie
    8. Redirección de puertos
    9. Inicialización de Cowrie
  2. El repositorio de datos
    1. Masajeado de datos
    2. Instalación de MySQL
    3. Creación de la base de datos MySQL para Cowrie
    4. Instalación de Apache, PHP y phpMyAdmin
    5. Carga de los datos de Cowrie en la base de datos mySQL
    6. Configuración del servicio REST de mySQL (MRS)
    7. Verifiación de la API de MRS
    8. Referencia: Gramática de filtrado de MRS en JSON

Mi entorno señuelo
Mi entorno señuelo

Parte 1: El honeypot

Opté por Cowrie como el componente clave de mi infraestructura de detección de tráfico no deseado. Cowrie es un fantástico honeypot de interacción media/alta diseñado para registrar intentos de fuerza bruta e interacciones con un shell remoto por parte de atacantes, tanto a través de SSH como de Telnet. Cowrie es muy popular entre investigadores y entusiastas debido a su óptima combinación de capacidades y facilidad de uso. Es un programa de código abierto y está respaldado por una activa comunidad liderada por Michel Oosterhof, el mantenedor, creador y desarrollador principal del proyecto.

1. Elección del sistema anfitrión del honeypot

El primer paso es la elección de un sistema Linux donde instalar el honeypot. Dado que Cowrie es muy eficiente en su consumo de recursos, elegí inslarlo en un Raspberry Pi 400.

2. Instalación de las dependencias del sistema

Instala las dependencias del sistema en el ordenador anfitrión (host)) de Cowrie:


$ sudo apt-get install git python3-virtualenv libssl-dev libffi-dev build-essential libpython3-dev python3-minimal authbind virtualenv 
      

3. Creación de la cuenta de usuario

Aunque instalar una cuenta de usuario sin contraseña no es absolutemente requerido, los autores de Cowrie lo recomiendan encarecidamente por motivos de seguridad:


 $ sudo adduser --disabled-password cowrie
 Adding user 'cowrie' ...
 Adding new group 'cowrie' (1002) ...
 Adding new user 'cowrie' (1002) with group 'cowrie' ... 
 Changing the user information for cowrie
 Enter the new value, or press ENTER for the default
 Full Name []:
 Room Number []:
 Work Phone []:
 Home Phone []:
 Other []:
 Is the information correct? [Y/n]

 $ sudo su - cowrie
      

4. Obtención del código de Cowrie

Clona el repositorio de código cowrie albergado en GitHub:


 $ git clone http://github.com/cowrie/cowrie
 Cloning into 'cowrie'...
 remote: Counting objects: 2965, done.
 remote: Compressing objects: 100% (1025/1025), done.
 remote: Total 2965 (delta 1908), reused 2962 (delta 1905), pack-reused 0 
 Receiving objects: 100% (2965/2965), 3.41 MiB | 2.57 MiB/s, done.
 Resolving deltas: 100% (1908/1908), done.
 Checking connectivity... done.

 $ cd cowrie
      

5. Instalación de un entorno virtual de Python

Técnicamente hablando, este paso no es necesario, pero se recomienda para garantizar que las actualizaciones de paquetes en el sistema host de Cowrie no causen incompatibilidades:


 $ pwd
 /home/cowrie/cowrie
 
 $ python -m venv cowrie-env
 New python executable in ./cowrie/cowrie-env/bin/python 
 Installing setuptools, pip, wheel...done.
      

Después de instalar el entorno virtual, actívalo e instala los paquetes requeridos:


  $ source cowrie-env/bin/activate
 (cowrie-env) $ python -m pip install --upgrade pip
 (cowrie-env) $ python -m pip install --upgrade -r requirements.txt 
      

6. Configuración de Cowrie

La configuración de Cowrie se guarda en el fichero cowrie/etc/cowrie.cfg. Para operar con una configuración estándar, no es necesario cambiar nada. De forma predeterminada, Cowrie acepta tráfico a través de SSH. Para que el honeypot acepte también tráfico por Telnet, envie sus datos a Splunk (más información sobre esto abajo) y cambie los puertos predeterminados (22 y 23), tenemos que modificar el fichero de configuración de la siguiente forma:


 [telnet]
 enabled = true
 ...
 [output_splunk]
 enabled = true
 ...
 [proxy]
 backend_ssh_port = 2022
 backend_telnet_port = 2023 
      

También quería cambiar las configuraciones de usuario predeterminadas y la lista de credenciales aceptadas para iniciar sesiones en el shell remoto. Estos cambios se realizan modificando el fichero cowrie/etc/userdb.txt. Cada línea del fichero consta de tres campos separados por un carácter ::

Como ejemplo, los siguientes valores configuran un nombre de usuario admin que acepta todas las contraseñas excepto 1) sólo caracteres numéricos, 2) la palabra admin en minúsculas y 3) la palabra honeypot insensible a mayúsculas y minúsculas:

 admin:x:!admin
 admin:x:!/^[0-9]+$/
 admin:x:!/honeypot/i 
 admin:x:*
      

7. Personalización de Cowrie

Opcionalmente, puedes cambiar la apariencia de la interfaz de Cowrie para que parezca más realista. Varios archivos te permiten hacer eso:

8. Redirección de puertos

Como vimos anteriormente, configuré Cowrie para aceptar tráfico SSH a través del puerto 2022 y tráfico Telnet a través del puerto 2023. Para preservar la fidelidad del señuelo, abrí los puertos 22 y 23 en mi enrutador y redirigí su tráfico a los puertos 2022 y 2023, respectivamente, en el sistema en el que instalé Cowrie.

9. Inicializacón de Cowrie

Inicializa el honeypot invocando el fichero ejecutable cowrie/bin/cowrie que forma parte de la distribución de Cowrie. Se conservará cualquier entorno virtual existente si se activa; de lo contrario, Cowrie intentará cargar el entorno virtual cowrie-env que creamos anteriormente:


 bin/cowrie start
 Activating virtualenv "cowrie-env"
 Starting cowrie with extra arguments [] ... 
      

Parte 2: El repositorio de datos

Escogí MySQL como almacén de los datos generados por Cowrie porque es una tecnología potente y madura, ofrece excelentes funcionalidades de búsqueda y manipulación de datos basadas en APIs, se puede instalar localmente y, lo mejor de todo, es de código abierto y gratis. Al principio, usé una solución basada en Splunk Enterprise. Aunque Splunk es una plataforma propietaria, es gratis si mantienes el volumen de generación de datos por debajo de 500 MB al día. Splunk me funconó bien durante seis meses, pero un día el señuelo detectó un pico de ataques que se tradujo en un volumen de 2.5 TB en un día. En ese momento experimenté en carne propia el lado oscuro del modelo de negocios freemium y las práctias de gestión de usuarios de Splunk que distan mucho de ser ideales: «paga o te bloqueamos los datos durante un mes». Como ambas opciones me resultaron inaceptables, decidí abandonar Splunk y migrar mis datos a MySQL. La decisión y sus ramificaciones han sido enteramente positivas y satisfactorias. Si tienes curiosidad por saber cómo preparé mi anterior entorno señuelo basado en Splunk, puedes consultar mis notas de instalación y configuración aquí.

Cowrie ofrece una serie de instrucciones para enviar los datos directamente desde el honeypot a una base de datos MySQL. Esas instrucciones son considerablemente más breves y sencillas que las que encontrarás en esta página, y son un buen punto de partida. Al final, opté por una configuración a medida que me permitiera ir más allá de las prestaciones básicas proporcionadas por Cowrie. Específicamente, necesitaba la flexibilidad de poder importar una versión modificada de la fuente de datos de Cowrie y poder analizar los datos por medio de una API. También necesitaba poder correr el servidor MySQL en un sistema huesped de Windows diferente al huesped del señuelo. Las instrucciones en esta página propocionan esas prestaciones.

1. Masajeado de datos

Puede usar la información de Cowrie tal y como viene del honeypot en forma de archivos JSON diarios. Se trata, básicamente, de una fuente de datos basada en eventos, donde cada sesión o interacción no deseada con el señuelo se desglosa en la serie de eventos que constituyen un ataque: por ejemplo, conexión, intento de inicio de sesión, ejecución de comandos en la terminal, creación o carga/descarga de archivos al señuelo, desconexión, etc.

Opté por una visión alternativa del tráfico no deseado basada en sesiones, no en eventos. Para ello, convertí la fuente de datos original de Cowrie en una nueva donde la unidad básica de información, que posteriormente se almacenará como una fila en una base de datos SQL, es la sesión, no el evento. Esto requiere fusionar todos los eventos correspondientes a la misma sesión en una sola fila. Este trabajo fue parte de la normalización de datos que realicé cuando usaba Splunk como repositorio de datos. Como recordatorio, la normalización de datos es el proceso de reorganizar o "masajear" los datos para que sea más fácil y rápido trabajar con ellos. Implica reducir o eliminar la redundancia de datos y garantizar que las dependencias entre ellos se implementen de forma que tenga en cuenta las limitaciones de la base de datos que los contiene. Esto permite consultar y analizar los datos con mayor facilidad y velocidad. Splunk no utiliza una base de datos convencional, por lo que la normalización que dio lugar a la nueva fuente basada en sesiones fue todo lo que necesitábamos. Pero MySQL, nuestra nueva solución de almacenamiento de datos, utiliza una base de datos SQL. Para los datos SQL, el proceso de normalización a menudo requiere dividir tablas grandes en tablas más pequeñas y vincularlas mediante relaciones. Y eso es exactamente lo que tuve que hacer. Para entender por qué, veamos un aspecto clave de nuestra nueva fuente de datos basada en sesiones:

Los campos de valor único son fáciles de manejar y no requieren procesamiento adicional: se implementan directamente como columnas en una tabla de base de datos SQL. Sin embargo, las reglas SQL prohíben o restringen considerablemente el uso de listas en las columnas de las tablas. La solución a este problema es realizar una normalización SQL de los datos en la fuente de Cowrie de la siguiente manera:

Observa que el campo commands es algo anómalo. Técnicamente, es una lista de comandos de Linux separados por comas. Sin embargo, como actualmente lo uso como una sola entidad, lo trato como un campo de valor único cuyo valor en una cadena larga de caracteres (es decir, como una columna en la tabla principal). Es posible que cambie esta disposición más adelante e implemente el campo de comandos como una nueva tabla secundaria, desglosando los comandos individuales

Finalmente, decidí omitir en la nueva fuente de datos de Cowrie algunos campos de la fuente original que añaden poco valor a mi investigación.

2. Instalación de MySQL

El primer paso es la instalación de la versión MySQL para Windows disponible en el área de descargas de la comunidad de MySQL. Cuando realicé mi instalación, la última versión del instalador para Windows era 8.0.36. El proceso de instalación es sencillo:

Tras el proceso de instalación anterior, el servicio de MySQL para Windows y las aplicaciones MySQL Shell y MySQL Workbench arrancarán automáticamente.

3. Creación de la base de datos MySQL para Cowrie

En esta sección se enumeran los pasos para crear una base de datos MySQL que más tarde utilizaremos para albergar los datos recopilados por el señuelo Cowrie:

¡Enhorabuena! Ya tienes una base de datos MySQL lista para aceptar los datos del señuelo Cowrie.

4. Instalación de Apache, PHP y phpMyAdmin

Este paso no es un requisito absoluto pero si, como yo, quieres tener la capacidad de visualizar tus datos en MySQL a través de una interfaz HTTP y estás acostumbrado a usar phpMyAdmin para configurar MySQL, lo deberías considerar. Quizá la forma más fácil de instalar phpMyAdmin en Windows es por medio de la distribución XAMPP (Cross Apache MariaDB PHP Perl) de Apache Friends. Puedes descargar su instalador para Windows de su sitio web. Cuando instalé mi copia, la última versión disponible era la 8.2.12. La instalación es sencilla:

Si todo fue bien, Apache, PHP y phpMyAdmin, junto con el muy últil Panel de Control de XAMP, estarán instalados en tu sistema. Ahora tenemos que conectar la anterior instalación de MySQL con la reciente instalación de phpMyAdmin:

5. Carga de los datos de Cowrie en la base de datos mySQL

En lugar de implementar una solución sofisticada basada en intermediación de mensajes, decidí construir un sistema más sencillo aprovechando ciertas características de la arquitectura de Cowrie. Como el señuelo guarda el tráfico no deseado en ficheros diarios en formato JSON, desarollé un programa que se ejecuta automáticamente una vez al día por medio del programador de tareas de Windows. Su lógica general es como sigue:

Si has seguido las instrucciones hasta aquí, tendrás la información registrada por Cowrie —formateada en sesiones fáciles de leer y analizar— disponible en una base de datos MySQL. Tómate unos minutos para celebrar lo conseguido.

6. Configuración del servicio REST de mySQL (MRS)

Llegado a este punto, tenemos una base de datos MySQL cargada con datos de conexiones de internet no deseadas recogidos por un señuelo Cowrie. Podemos interactuar con la base de datos por medio de tres distintas interfaces: MySQL Shell, MySQL Workbench y phpMyAdmin. En el siguiente paso, añadiremos una cuarta forma de visualizar and manipular los datos por medio de una extensión del popular editor Visual Studio Code. En realidad, esto no es obligatorio, pero simplificará mucho la próxima tarea de configuración de una API basada en el el servicio REST de MySQL (MRS). Brevemente, MRS is una tecnología que proporciona un acceso rápido y seguro por HTTP a datos albergados en MySQL. MRS está construido como una prestación de MySQL Router y ofrece la posibilidad de publicar servicios web basados en REST para interactuar con soluciones MySQL como, en nuestro caso, bases de datos. En mi caso particular, utilizo la API básica de MRS para extraer programáticamente los datos de Cowrie almacenados en MySQL, lo que es una parte importante de mi proceso de análisis de datos. Aunque MRS se puede configurar directamente desde MySQL Shell, es mucho más fácil hacerlo desde la extensión de Visual Studio Code. Doy por hecho que estás familiarizado con el editor Visual Studio Code, así que no cubriremos su instalación aquí.

¡Y así hemos llegado a la meta! Nuestros datos están ahora disponibles en la URL https://localhost:8443/honeypot/v1 a través de los siguientes endpoints:

7. Verifiación de la API de MRS

Como paso final, procederemos a verificar que los datos de Cowrie que importamos en la base de datos MySQL son accesibles en los endpoints. Desde Visual Studio Code, haz clic con el botón derecho del ratón en DATABASE CONNECTIONS\Cowrie\MySQL REST Service\honeypot\v1\sessions y selecciona Open REST Object Request Path in Web Browser. Tu navegador de internet abrirá una nueva pestaña mostrando, en formato JSON, las 25 primeras sesiones capturadas por Cowrie.

Para que esto funcione, asegúrate de que MySQL Router está corriendo. Puedes arrancar MySQL Router desde un terminal de comandos con la ayuda del siguiente script de Bash:


#!/bin/bash
 
declare +i -r MSRCONF="c:/Users/TU_NOMBRE_DE_USUARIO_DE_WINDOWS/AppData/Roaming/MySQL/mysqlsh-gui/plugin_data/mrs_plugin/router_configs/1/mysqlrouter"
declare +i -r MSRPATH="c:/Users/TU_NOMBRE_DE_USUARIO_DE_WINDOWS/.vscode/extensions/oracle.mysql-shell-for-vs-code-1.14.2-win32-x64/router"
declare +i    pid=""
export        PATH="${PATH}:${MSRPATH}/lib"
export        ROUTER_PID="${MSRCONF}/mysqlrouter.pid"

pid=`ps -W | grep mysqlrouter | awk '{print $1}'`
if [ ! "${pid}" = "" ]
then
   echo "MySQL Router ya está corriendo con PID = ${pid}"
   exit 0
else
   "${MSRPATH}/bin/mysqlrouter.exe" -c "${MSRCONF}/mysqlrouter.conf" > /dev/null 2>&1 &
   disown %-
   pid=`ps -W | grep mysqlrouter | awk '{print $1}'`
   if [ ! "${pid}" = "" ]
   then
      echo "MySQL Router está corriendo con PID = ${pid}"
      exit 0
   else
      echo "Error: MySQL Router no pudo arrancar"
      exit 1
   fi
fi
      
Puedes terminar el servicio MySQL Router con el seguiente programa:

#!/bin/bash
 
declare +i -r MSRCONF="c:/Users/TU_NOMBRE_DE_USUARIO_DE_WINDOWS/AppData/Roaming/MySQL/mysqlsh-gui/plugin_data/mrs_plugin/router_configs/1/mysqlrouter"
declare +i    pid=""
 
pid=`ps -W | grep mysqlrouter | awk '{print $1}'`
if [ ! "${pid}" = "" ]
then
   echo "MySQL Router está corriendo con PID = ${pid}"
   env kill -f ${pid} > /dev/null 2>&1
   pid=`ps -W | grep mysqlrouter | awk '{print $1}'`
   if [ "${pid}" = "" ]
   then
      rm -f ${MSRCONF}/mysqlrouter.pid
      echo "MySQL Router ya no está corriendo"
      exit 0
   else
      echo "Error: MySQL Router no pudo terminar"
      exit 1
   fi
else
   echo "MySQL Router no está corriendo"
   exit 0
fi
      

También podemos testar la API utilizando el comando curl desde una ventana de terminal or un script de shell. La lista siguiente proporciona ejemplos de invocaciones del comando curl para extraer información recogida por Cowrie por medio de la API del servicio REST de MySQL:


# Muestra las primeras 25 sesiones de Cowrie
curl -s -k 'https://localhost:8443/honeypot/v1/sessions' | jq
 
# Muestra los primeros 25 escaneos, filtrado en servidor
curl -s -k 'https://localhost:8443/honeypot/v1/sessions' -G --data-urlencode 'q={"type":"scan"}' | jq

# Muestra los primeros 25 ataques, filtrado en servidor
curl -s -k 'https://localhost:8443/honeypot/v1/sessions' -G --data-urlencode 'q={"type":"attack"}' | jq

# Muestra los primeros 25 ataques con acceso exitoso, filtrado en servidor
curl -s -k 'https://localhost:8443/honeypot/v1/attempts' -G --data-urlencode 'q={"login":"true"}' | jq

# Muestra los primeros 25 ataques con acceso exitoso, filtrado en cliente
curl -s -k 'https://localhost:8443/honeypot/v1/sessions' | jq '.items[] | select(.credentials?[]?.login == "true")'

# Muestra los primeros 25 ataques con acceso fallido, filtrado en cliente
curl -s -k 'https://localhost:8443/honeypot/v1/sessions' | jq '.items[] | select(.credentials? | length > 0 and all(.login == "false"))'

# Muestra las sesiones entre la 10.001 y la 10.500, filtrado en servidor
curl -s -k 'https://localhost:8443/honeypot/v1/sessions' -G --data-urlencode 'offset=10000&limit=500' | jq

# Muestra la sesión con número 12.345, filtrado en servidor
curl -s -k 'https://localhost:8443/honeypot/v1/sessions' -G --data-urlencode 'q={"id":12345}' | jq

# Muestra las primeras 25 sesiones que se originaron desde direcciones IP operando desde España, filtrado en servidor
curl -s -k 'https://localhost:8443/honeypot/v1/sessions' -G --data-urlencode 'q={"srcCountry":"Spain"}' | jq

# Muestra las primeras 25 sesiones que se originaron desde direcciones IP operando desde Singapur o Francia, filtrado en servidor
curl -s -k 'https://localhost:8443/honeypot/v1/sessions' -G --data-urlencode 'q={"$or":[{"srcCountry":"Singapore"},{"srcCountry":"France"}]}' | jq
      

Esto es todo. Ya estás listo para analizar la información recogida por el señuelo Cowrie. ¡Te deseamos una buena caza de hackers!

Referencia: Gramática de filtrado de MRS en JSON

El último ejemplo en la sección anterior muestra cómo combinar cláusulas de filtrado utilizando un operador lógico (en el ejemplo, $or). La especificación completa en JSON de la gramática de filtrado del servicio REST de MySQL es la siguiente:


 FilterObject { orderby , asof, wmembers }

 orderby
    "$orderby": {orderByMembers}

 orderByMembers
    orderByProperty
    orderByProperty , orderByMembers

 orderByProperty
    columnName : sortingValue

 sortingValue
    "ASC"
    "DESC"
    "-1"
    "1"
    -1
    1

 asof
    "$asof": date
    "$asof": "datechars"
    "$asof": scn
    "$asof": +int

 wmembers
    wpair
    wpair , wmembers

 wpair
    columnProperty
    complexOperatorProperty

 columnProperty
    columnName : string
    columnName : number
    columnName : date
    columnName : simpleOperatorObject
 
 columnName : complexOperatorObject
    columnName : [complexValues]

 columnName
    "\p{Alpha}[[\p{Alpha}]]([[\p{Alnum}]#$_])*$"

 complexOperatorProperty
    complexKey : [complexValues]
    complexKey : simpleOperatorObject 

 complexKey
    "$and"
    "$or"

 complexValues
    complexValue , complexValues

 complexValue
    simpleOperatorObject
    complexOperatorObject
    columnObject

 columnObject
    {columnProperty}

 simpleOperatorObject
    {simpleOperatorProperty}

 complexOperatorObject
    {complexOperatorProperty}

 simpleOperatorProperty
    "$eq" : string | number | date
    "$ne" : string | number | date
    "$lt" :  number | date
    "$lte" : number | date
    "$gt" : number | date
    "$gte" : number | date
    "$instr" : string 
    "$ninstr" : string
    "$like" : string
    "$null" : null
    "$notnull" : null
    "$between" : betweenValue
    "$like": string

 betweenValue
    [null , betweenNotNull]
    [betweenNotNull , null]
    [betweenRegular , betweenRegular]

 betweenNotNull
    number
    date
    
 betweenRegular
    string
    number
    date

 string 
    JSONString
 
 number
    JSONNumber
 
 date
    {"$date":"datechars"}
 
 scn
    {"$scn": +int}

 datechars is an RFC3339 date format in UTC (Z)
        
 JSONString
    ""
    " chars "
 
 chars
    char
    char chars
 
 char
    any-Unicode-character except-"-or-\-or-control-character
    \"
    \\
    \/
    \b
    \f
    \n
    \r
    \t
    \u four-hex-digits

 JSONNumber
    int
    int frac
    int exp
    int frac exp
 
 int
    digit
    digit1-9 digits 
    - digit
    - digit1-9 digits
 
 frac
    . digits
 
 exp
    e digits
 
 digits
    digit
    digit digits
 
 e
    e
    e+
    e-
    E
    E+
    E-