miércoles, 13 de julio de 2016

Siguiendo la pista a los spammers

Motivado por la charla de Defcon de este vĂ­deo:

https://www.youtube.com/watch?v=ytDamqTjPwg

hice este script para hacer grĂ¡ficas como la que se ve en el minuto 17. En esta charla explican cĂ³mo de forma manual categorizaron miles de correos para hacer esas grĂ¡ficas, ya que el contenido era visual y no era fĂ¡cil buscar texto en ellos de forma automĂ¡tica (aunque... ¿no podrĂ­an haber cogido la direcciĂ³n de quien lo enviaba, y la fecha?)

Eso es lo que hace el siguiente programa precisamente, busca en la carpeta de spam de un correo usando SMTP y saca todos los correos existentes y hace grĂ¡ficas de nĂºmero de correos acumulados por fecha para cada uno de los distintos spammers. Es de destacar que a veces entran en la carpeta de spam correos legĂ­timos, y estos tambiĂ©n aparecerĂ¡n en la grĂ¡fica.

Lo interesante de la charla de Defcon es que a partir de las pendientes de estas grĂ¡ficas eran capaces de caracterizar la frecuencia con la que los bots de cada spammer enviaban correo basura.

Llevo probĂ¡ndolo poco tiempo, un mes, como para tener grĂ¡ficas muy significativas, pero se ve una pendiente clara, por ejemplo en los correos enviados por facebook, lo que indica que tienen un bot que envĂ­a cada dĂ­a correos con aproximadamente la misma frecuencia. Por eso llegan tantos correos de facebook sobre cosas que no tienen nada que ver con mi actividad.

El script en python cuenta con varias funciones que hacen tareas especĂ­ficas, llamadas por una funciĂ³n principal. Por lo que se ve, sacar informaciĂ³n de los correos en el servidor es lento y no hay forma de sacar Ăºnicamente la cabecera, hay que extraerla del mensaje completo. Por esto, el script crea un archivo donde guarda los remitentes y fechas.Hay dos funciones para extraer correo del servidor: la primera, read_server_fast() cuenta cuĂ¡ntos spams hay leĂ­dos previamente, y almacenados en el archivo de texto (cuyo nombre es el nombre de usuario de la cuenta de email en cuestiĂ³n). Entonces empieza a recoger correos del servidor tomando como correo inicial aquĂ©l cuya id coincida con el nĂºmero de correos en el archivo de texto +1.
La funciĂ³n read_server_slow() no hace falta a menos que se haya borrado manualmente algo del servidor. Lo que hace es comparar uno a uno los correos del archivo con cada uno del servidor, para decidir si lo añade o no a la lista. Pero esta funciĂ³n es mucho mĂ¡s lenta y en principio no hay que usarla.

Antes de usar el script hay que activar IMAP en nuestro servidor. En este ejemplo, se muestra cĂ³mo hacerlo en Gmail:

1- Primero se activa IMAP en la configuraciĂ³n de la cuenta:


2- Después hay que habilitar el acceso a la cuenta desde aplicaciones que no sean la web de Gmail. Esto se encuentra en https://www.google.com/settings/security/lesssecureapps



Una vez hecho esto, el script funciona. Hace falta instalar matplotlib para las grĂ¡ficas. La variable show por defecto tiene valor False, y si se pone a True hace ademĂ¡s un grĂ¡fico tipo "tarta" para ver quiĂ©n ha enviado cuĂ¡ntos spams.

La variable crit fija un criterio para decidir si se representa o no un spammer en la grĂ¡fica en funciĂ³n del tiempo. Como se puede ver, muchos spammers solo envĂ­an un correo, asĂ­ que tendremos muchos puntos individuales que liaran mucho todo, y la leyenda quedarĂ¡ enorme. Subiendo crit a 3, por ejemplo, se deja de ver a todos estos spammers ocasionales:
criterio crit=0. Se muestran todos los spammers



Criterio crit=3. Se filtran todos los que han enviado menos de 3 correos

GrĂ¡fico "tarta" de todos los spammers, cada uno con su contribuciĂ³n.

Por Ăºltimo, un test con el buzĂ³n principal, con correo mĂ¡s antiguo y numeroso. AquĂ­ es totalmente evidente quiĂ©n envĂ­a correo con un bot, de forma regular, mĂ¡s o menos frecuente:


A continuaciĂ³n el script, pensado para extraer spam de Gmail. Para otras cuentas, habrĂ¡ que modificar los nombres de las carpetas del servidor.
IMPORTANTE: hay que añadir en las variables user y pwd, en la funciĂ³n principal, nuestras credenciales (entre comillas):

###########################################


import email, imaplib
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
import datetime as dt
import numpy as np
import math

folder = "Todos"

days = {'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'}

def readfile():
    list_senders = []
    list_dates = []
    list_lines = []
    g = open('{}-{}.txt'.format(user,folder),'r')
    for i, line in enumerate(g.readlines()):
        list_lines.append(line)
    g.close()
    return list_lines

def read_server_fast():
        m = imaplib.IMAP4_SSL("imap.gmail.com")
        m.login(user,pwd)
        print(m.list())
        a = m.select("[Gmail]/"+folder)
        lis = m.list()
        lis1 = a
        existing_lines = readfile()   

        resp, items = m.search(None, "ALL")
        items = items[0].split()
        already_there = False
      
        print(items)
      
        g = open('{}-{}.txt'.format(user,folder),'a')
        for emailid in range(len(existing_lines)+1,len(items)):
                resp, data = m.fetch(emailid, "(RFC822)")
                email_body = data[0][1]
                mail = email.message_from_string(email_body)
      
                print emailid
                try:
                    sender = mail["From"].split('<')[1].split('>')[0]
                    date = mail["Date"]
                    print(sender, date)
                    g.write(sender + ';' + date + '\n')
                except:
                    g.write('error;error\n')
                  
        g.close()

def read_server_slow():
    m = imaplib.IMAP4_SSL("imap.gmail.com")
    m.login(user,pwd)
    a = m.select("[Gmail]/"+folder)
    lis = m.list()
    lis1 = a
    existing_lines = readfile()  

    resp, items = m.search(None, "ALL")
    items = items[0].split()
    print(items)
    already_there = False
    g = open('{}.txt'.format(user),'a')
    for emailid in items:
        resp, data = m.fetch(emailid, "(RFC822)")
        email_body = data[0][1]
        mail = email.message_from_string(email_body)
  
        print emailid
        sender = mail["From"].split('<')[1].split('>')[0]
        date = mail["Date"]
        print(sender, date)
        for i in range(len(existing_lines)):
            check = existing_lines[i].split(';')
            if (sender == check[0] and date == check[1].split('\n')[0]):
                already_there = True
                print "Already in file!"
                break
            else:
                already_there = False

        if already_there == False:
            g.write(sender + ';' + date + '\n')
    g.close()

def get_sen_dat():
    """Get senders and dates from lines in file and put them in arrays senders and dates
        The senders arrays contains the number of messages per sender.
        The dates dic contains the dates when a message from a certain sender (dates.keys are the senders
        ), with all the dates in a list for each sender"""
    senders = {}
    dates = {}
    f = open('{}-{}.txt'.format(user,folder),'r')
  
    for i, line in enumerate(f.readlines()):
        sender, date = line.split(';')
        if sender in senders.keys():
            senders[sender] += 1
        else:
            senders[sender] = 1
        if sender != "error":
            if sender in dates.keys():
                dates[sender].append(date)
        else:
            dates[sender] = [date]
                  
        if i == 0: #for calculating the day span and adjust the ticks in graph
            first_date = date
        last_date = date
      
  
    f.close()
    first = first_date.split(',')[1].split(' ')
    last = last_date.split(',')[1].split(' ')
    f = first[1] + ' ' + months_dic[first[2]] + ' ' + first[3]
    l = last[1] + ' ' + months_dic[last[2]] + ' ' + last[3]
    a = dt.datetime.strptime(f, '%d %m %Y')
    b = dt.datetime.strptime(l, '%d %m %Y')
    day_interval = (b - a).days
    print(day_interval)
    return (senders, dates, day_interval)

def pie_chart(senders, show = False):
    l_labels = []
    l_values = []
  
    for sender in senders.keys():
        #print (sender, senders[sender])
        l_labels.append(sender)
        l_values.append(senders[sender])
  
    if show == True:
        plt.pie(l_values, labels=l_labels)
        plt.show()
      
    return(l_values, l_labels)
  
def time_graph(dates, l_values, crit, day_interval):
    k=0
    for value in l_values:
        if value > crit:
            k += 1
  
    num_plots = len(dates.keys())
    colormap = plt.cm.gist_ncar
    plt.gca().set_color_cycle([colormap(i) for i in np.linspace(0, 0.9, k)])
    i = 0
    sender_address = []
    ax = plt.subplot(111)
  
    for sender in dates.keys():
        dates1 = []
        vals1 = []
        val = 0
        for date_list in dates[sender]:
            week_day = date_list.split()[0].strip(',')
            if week_day in days:
                day = date_list.split()[1]
                month = months_dic[date_list.split()[2]]
                year = date_list.split()[3]
                hours = date_list.split()[4]
            else:                          
                day = date_list.split()[0]
                month = months_dic[date_list.split()[1]]
                year = date_list.split()[2]
                hours = date_list.split()[3]
            date = month + ' ' + day + ' ' + year + ' ' + hours
            date_fmt = dt.datetime.strptime(date,'%m %d %Y %H:%M:%S')
            val += 1
            dates1.append(date_fmt)
            vals1.append(val)
            sender_address.append(sender)
        if(len(vals1) > crit):
            plt.gca().xaxis.set_major_formatter(mdates.DateFormatter('%m/%d/%Y'))
            plt.gca().xaxis.set_major_locator(mdates.DayLocator(interval = int(math.floor(day_interval * 0.2))))
            plt.plot(dates1,vals1,'-o',label=sender)
            plt.gcf().autofmt_xdate()
  
    box = ax.get_position()
    ax.set_position([box.x0, box.y0, box.width * 0.6, box.height])
    #plt.legend(loc='center left',bbox_to_anchor=(1, 0.5))
    plt.show()

if __name__ == '__main__':
    user = "user"
    pwd = "pass"

    f = open('{}-{}.txt'.format(user,folder),'a')
    f.close()

    months_dic = {
    'Jan':'1','Feb':'2', 'Mar':'3','Apr':'4',
    'May':'5','Jun':'6', 'Jul':'7','Aug':'8',
    'Sep':'9','Oct':'10', 'Nov':'11','Dec':'12'}
      
      
      
    read_server_fast()
    #read_server_slow() #use after deleting spam to start all over
    senders, dates, day_interval = get_sen_dat()
    show = False
    l_values, l_labels = pie_chart(senders, show)
    crit = 3
    time_graph(dates, l_values, crit, day_interval)
   

No hay comentarios:

Publicar un comentario