Redes Sociales

martes, 24 de marzo de 2015

Preparando un repositorio de información sobre dominios ".es"

Disponiendo como tengo estos dias de dos recursos importantes como son el tiempo y un listado completo de dominios .es y dado que no existe un repositorio whois mantenido por nic.es o red.es, decidí ponerme a recabar y almacenar información sobre los servicios y servidores de los dominios .es.

Mi idea inicial fue almacenar los puertos abiertos en cada dominio, pero rápidamente se me ocurrió ampliar esta información con toda la que nos ofrece el comando "whatweb", el cual además ofrece una opción por la cual la información la escribe en formato de sentencias SQL. Perfecto para un gran repositorio de información como el que me planteo crear.

El primer paso fue crear la base de datos. Inicialmente instalé y preparé una base de datos SQLite, hasta que me di cuenta que, al no permitir concurrencia de sentencias SQL, esta BD me penalizaba mucho mas que ayudarme. Por esta razón decidi migrar lo poco que había obtenido (hasta el momento solo los nombres de los 1.7 Millones de dominios .es) a MySQL.

A día de hoy (su segundo dia), la base de datos tiene 5 tablas:

  • Domains
  • Ports
  • plugins
  • scans
  • targets

Las dos primeras tablas fueron creadas por mi para almacenar los dominios .es y sus puertos (tanto abiertos, como filtrados, como cerrados). Las tres ultimas pertenecen a la salida que genera el script whatweb.

La table Domains. Su objetivo es almacenar los dominios .es, en ella se almacena un id, el nombre de dominio, la IP, si es vulnerable a FREAK y el CIF del dominio.

+---------+---------+------+-----+---------+-------+
| Field   | Type    | Null | Key | Default | Extra |
+---------+---------+------+-----+---------+-------+
| Id      | int(11) | YES  |     | NULL    |       |
| Name    | text    | YES  |     | NULL    |       |
| IP      | text    | YES  |     | NULL    |       |
| Company | text    | YES  |     | NULL    |       |
| FREAK   | text    | YES  |     | NULL    |       |
| CIF     | text    | YES  |     | NULL    |       |
+---------+---------+------+-----+---------+-----

La tabla Ports. Rellenada a partir de la información obtenida via nmap, se incluye un id del dominio (que se casa con la tabla Domains), el numero del puerto y si es tcp o udp (con el formato "465/tcp"), el estado (Open, Close, Filtered, etc) y el servicio que hay detrás. En estos momentos tiene 59000 Puertos documentados pertenecientes a 3015 dominios. Esta es la tabla que más lentamente se rellena y tengo que echarle una pensada.

+---------+---------+------+-----+---------+-------+
| Field   | Type    | Null | Key | Default | Extra |
+---------+---------+------+-----+---------+-------+
| Id      | int(11) | YES  |     | NULL    |       |
| Port    | text    | YES  |     | NULL    |       |
| Status  | text    | YES  |     | NULL    |       |
| Service | text    | YES  |     | NULL    |       |
+---------+---------+------+-----+---------+-----

La tabla plugins es una tabla estática que almacena un id y el nombre de un plugin. ¿Y que es un plugin?. Pues es un tipo de datos que devuelve whatweb, desde un servidor web hasta una IP.

La tabla scans es la que almacena el grueso de la información devuelta por whatweb, en concreto se almacena en el campo string, por lo que el resto de campos son bastante innecesarios.

+-----------+--------------+------+-----+---------+----------------+
| Field     | Type         | Null | Key | Default | Extra          |
+-----------+--------------+------+-----+---------+----------------+
| scan_id   | int(11)      | NO   | PRI | NULL    | auto_increment |
| plugin_id | int(11)      | NO   |     | NULL    |                |
| target_id | int(11)      | NO   |     | NULL    |                |
| version   | varchar(255) | YES  |     | NULL    |                |
| os        | varchar(255) | YES  |     | NULL    |                |
| string    | varchar(767) | YES  |     | NULL    |                |
| account   | varchar(767) | YES  |     | NULL    |                |
| model     | varchar(767) | YES  |     | NULL    |                |
| firmware  | varchar(767) | YES  |     | NULL    |                |
| module    | varchar(767) | YES  |     | NULL    |                |
| filepath  | varchar(767) | YES  |     | NULL    |                |
| certainty | varchar(10)  | YES  |     | NULL    |                |
+-----------+--------------+------+-----+---------+----------------+

La tabla targets almacena los nombres de dominios, un id y el codigo devuelto al conectarse via web (un 200, un 404, un 301...). La información almacenada aqui es bastante redundante ya que también la tengo en la tabla Domains, pero dado que es la que proporciona whatweb por defecto, la dejaré así y ya veré como solucionar el problema de la duplicidad.

Una vez creadas las tablas, hay que crear los scripts que recaban la información. Un poco de shell magic y...

for i in `cat datos_originales/Full_data | cut -d'|' -f1 | sed 's/"//g'`
do 
         nmap -T4 --host-timeout 660 $i 2>/dev/null | egrep "tcp|udp" |while read line; do 
                        PORT=`echo $line | awk '{ print $1 }'`
                        STATUS=`echo $line | awk '{ print $2 }'`
                        SERVICE=`echo $line | awk '{ print $3 }'`
                        python inserta-mysql.py $i $PORT $STATUS $SERVICE
         done &  
         if [ `ps -ef | grep nmap | wc -l` -gt 90 ]
         then 
              sleep 30
         fi
done &

Este script lanza hasta 90 nmaps simultaneamente y espera 11 minutos (660 segundos) a que acaben. Despues de varias pruebas consideré que era la mejor combinación. Si todo va bien un nmap deberia tardar unos 60 sg, pero con tantos lanzandose a la vez es normal que tarden mas bien entre los 7 y los 8 minutos.

Como vemos el script tira de inserta-mysql.py, programita en python que inserta los datos en la BD MySQL y que tiene este aspecto:

import csv
import sys
import MySQLdb

DB_HOST='localhost'
DB_USER=''
DB_PASS=''
DB_NAME='whois'

datos = [DB_HOST, DB_USER, DB_PASS, DB_NAME]
con = MySQLdb.connect(*datos)
cur = con.cursor()   
i=0

domain=sys.argv[1]
port=sys.argv[2]
status=sys.argv[3]
service=sys.argv[4]

cur.execute("select id from Domains where Name='"+domain+"'")
records = [int(record[0]) for record in cur.fetchall()]
id=records[0]

query="INSERT INTO Ports VALUES('"+str(id)+"','"+port+"','"+status+"','"+service+"')" 
cur.execute(query)

con.commit()

if con:
 cur.close()
        con.close()

Ahora ya solo queda lanzar los comandos y esperar:

root@kali:~/Downloads# ./inserta.sh

Por otra parte, no me quería centrar sólo en los puertos abiertos, quería obtener más información de los dominios españoles. Whatweb es un programita escrito en Ruby que obtiene información muy variada sobre un dominio. Por ejemplo:

  • La IP
  • El código de acceso (200, 301, 404, etc)
  • El servidor web instalado y su versión (Apache, IIS 7.5,...)
  • El CMS que tienen instalado (Drupal, Joomla!, etc)
  • Las librerias Javascript, PHP instaladas, etc.

Además tiene una opción estupenda que escribe en formato SQL la salida de la herramienta. Perfecto para pasásela a continuación a MySQL. Así que con la siguiente linea de comando:

for i in `cat datos_originales/Full_data | cut -d'|' -f1 | sed 's/"//g'` 
do 
    whatweb $i --log-sql=inserta.sql & 
    if [`ps -ef | grep whatweb | wc -l` -gt 50 ]; then sleep 20; fi
done

...lanzo hasta 50 whatweb simultáneos. Whatweb no consume tanto ancho de banda como nmap por lo que podía haber aumentado el numero de whatweb simultaneos, pero con los nmap funcionando a la vez ya era demasiado para mi pobre instalación casera...

El anterior script genera un fichero SQL que lógicamente hay que meter en MySQL. El comando es muy simple:

mysql -f whois < inserta.sql

Me hubiera gustado poder recopilar información de whois de los dominios españoles, pero por desgracia España todavía no tiene un servidor de Whois público, y el servicio de whois que ofrece Red.es se basa en Java y solo permite 10 consultas por minuto.

En la próxima entrada mostraré los resultados obtenidos en apenas 4 días lanzando estos scripts.

No hay comentarios:

Publicar un comentario