2016-07-18 15:50:41 +02:00
|
|
|
#!/usr/bin/env python2
|
|
|
|
# -*-coding:UTF-8 -*
|
|
|
|
|
|
|
|
"""
|
|
|
|
The Duplicate module
|
|
|
|
====================
|
|
|
|
|
|
|
|
This huge module is, in short term, checking duplicates.
|
2016-07-18 16:22:33 +02:00
|
|
|
Its input comes from other modules, namely:
|
2016-08-08 09:17:44 +02:00
|
|
|
Credential, CreditCard, Keys, Mails, SQLinjectionDetection, CVE and Phone
|
2016-07-18 15:50:41 +02:00
|
|
|
|
2016-07-18 16:22:33 +02:00
|
|
|
This one differ from v1 by only using redis and not json file stored on disk
|
2016-07-18 15:50:41 +02:00
|
|
|
|
2016-08-04 12:10:56 +02:00
|
|
|
Perform comparisions with ssdeep and tlsh
|
|
|
|
|
2016-07-18 15:50:41 +02:00
|
|
|
Requirements:
|
|
|
|
-------------
|
|
|
|
|
|
|
|
|
|
|
|
"""
|
|
|
|
import redis
|
|
|
|
import os
|
|
|
|
import time
|
|
|
|
from datetime import datetime, timedelta
|
|
|
|
import json
|
|
|
|
import ssdeep
|
2016-08-04 11:55:38 +02:00
|
|
|
import tlsh
|
2016-07-18 15:50:41 +02:00
|
|
|
from packages import Paste
|
|
|
|
from pubsublogger import publisher
|
|
|
|
|
|
|
|
from Helper import Process
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
publisher.port = 6380
|
|
|
|
publisher.channel = "Script"
|
|
|
|
|
|
|
|
config_section = 'Duplicates'
|
|
|
|
|
|
|
|
p = Process(config_section)
|
|
|
|
|
|
|
|
maximum_month_range = int(p.config.get("Modules_Duplicates", "maximum_month_range"))
|
2016-08-04 11:55:38 +02:00
|
|
|
threshold_duplicate_ssdeep = int(p.config.get("Modules_Duplicates", "threshold_duplicate_ssdeep"))
|
|
|
|
threshold_duplicate_tlsh = int(p.config.get("Modules_Duplicates", "threshold_duplicate_tlsh"))
|
|
|
|
threshold_set = {}
|
|
|
|
threshold_set['ssdeep'] = threshold_duplicate_ssdeep
|
|
|
|
threshold_set['tlsh'] = threshold_duplicate_tlsh
|
|
|
|
min_paste_size = float(p.config.get("Modules_Duplicates", "min_paste_size"))
|
2016-07-18 15:50:41 +02:00
|
|
|
|
|
|
|
# REDIS #
|
|
|
|
dico_redis = {}
|
|
|
|
date_today = datetime.today()
|
|
|
|
for year in xrange(2013, date_today.year+1):
|
|
|
|
for month in xrange(0, 13):
|
|
|
|
dico_redis[str(year)+str(month).zfill(2)] = redis.StrictRedis(
|
|
|
|
host=p.config.get("Redis_Level_DB", "host"), port=year,
|
|
|
|
db=month)
|
2016-08-04 11:55:38 +02:00
|
|
|
#print("dup: "+str(year)+str(month).zfill(2)+"\n")
|
2016-07-18 15:50:41 +02:00
|
|
|
|
|
|
|
# FUNCTIONS #
|
|
|
|
publisher.info("Script duplicate started")
|
|
|
|
|
|
|
|
while True:
|
|
|
|
try:
|
|
|
|
hash_dico = {}
|
|
|
|
dupl = []
|
|
|
|
dico_range_list = []
|
|
|
|
|
|
|
|
x = time.time()
|
|
|
|
|
|
|
|
message = p.get_from_set()
|
|
|
|
if message is not None:
|
|
|
|
path = message
|
|
|
|
PST = Paste.Paste(path)
|
|
|
|
else:
|
|
|
|
publisher.debug("Script Attribute is idling 10s")
|
|
|
|
time.sleep(10)
|
|
|
|
continue
|
|
|
|
|
|
|
|
# the paste is too small
|
2016-08-04 11:55:38 +02:00
|
|
|
if (PST._get_p_size() < min_paste_size):
|
2016-07-18 15:50:41 +02:00
|
|
|
continue
|
|
|
|
|
|
|
|
PST._set_p_hash_kind("ssdeep")
|
2016-08-04 11:55:38 +02:00
|
|
|
PST._set_p_hash_kind("tlsh")
|
2016-07-18 15:50:41 +02:00
|
|
|
|
|
|
|
# Assignate the correct redis connexion
|
|
|
|
r_serv1 = dico_redis[PST.p_date.year + PST.p_date.month]
|
|
|
|
|
|
|
|
# Creating the dico name: yyyymm
|
|
|
|
# Get the date of the range
|
|
|
|
date_range = date_today - timedelta(days = maximum_month_range*30.4166666)
|
|
|
|
num_of_month = (date_today.year - date_range.year)*12 + (date_today.month - date_range.month)
|
|
|
|
for diff_month in xrange(0, num_of_month+1):
|
|
|
|
curr_date_range = date_today - timedelta(days = diff_month*30.4166666)
|
|
|
|
to_append = str(curr_date_range.year)+str(curr_date_range.month).zfill(2)
|
|
|
|
dico_range_list.append(to_append)
|
2016-08-04 11:55:38 +02:00
|
|
|
|
2016-07-18 15:50:41 +02:00
|
|
|
# Use all dico in range
|
|
|
|
dico_range_list = dico_range_list[0:maximum_month_range]
|
|
|
|
|
|
|
|
# UNIQUE INDEX HASHS TABLE
|
|
|
|
yearly_index = str(date_today.year)+'00'
|
|
|
|
r_serv0 = dico_redis[yearly_index]
|
|
|
|
r_serv0.incr("current_index")
|
|
|
|
index = r_serv0.get("current_index")+str(PST.p_date)
|
2016-08-04 11:55:38 +02:00
|
|
|
|
|
|
|
# Open selected dico range
|
2016-07-18 15:50:41 +02:00
|
|
|
opened_dico = []
|
|
|
|
for dico_name in dico_range_list:
|
|
|
|
opened_dico.append([dico_name, dico_redis[dico_name]])
|
2016-08-04 11:55:38 +02:00
|
|
|
|
2016-07-18 15:50:41 +02:00
|
|
|
# retrieve hash from paste
|
2016-08-04 11:55:38 +02:00
|
|
|
paste_hashes = PST._get_p_hash()
|
|
|
|
|
2016-07-18 15:50:41 +02:00
|
|
|
# Go throught the Database of the dico (of the month)
|
|
|
|
for curr_dico_name, curr_dico_redis in opened_dico:
|
2016-08-04 11:55:38 +02:00
|
|
|
for hash_type, paste_hash in paste_hashes.iteritems():
|
|
|
|
for dico_hash in curr_dico_redis.smembers('HASHS_'+hash_type):
|
|
|
|
try:
|
2016-08-09 15:06:55 +02:00
|
|
|
if hash_type == 'ssdeep':
|
|
|
|
percent = 100-ssdeep.compare(dico_hash, paste_hash)
|
|
|
|
else:
|
|
|
|
percent = tlsh.diffxlen(dico_hash, paste_hash)
|
|
|
|
|
2016-08-04 11:55:38 +02:00
|
|
|
threshold_duplicate = threshold_set[hash_type]
|
|
|
|
if percent < threshold_duplicate:
|
2016-08-08 15:28:26 +02:00
|
|
|
percent = 100 - percent if hash_type == 'ssdeep' else percent #recovert the correct percent value for ssdeep
|
2016-08-04 11:55:38 +02:00
|
|
|
# Go throught the Database of the dico filter (month)
|
|
|
|
r_serv_dico = dico_redis[curr_dico_name]
|
|
|
|
|
|
|
|
# index of paste
|
|
|
|
index_current = r_serv_dico.get(dico_hash)
|
|
|
|
paste_path = r_serv_dico.get(index_current)
|
|
|
|
if paste_path != None:
|
|
|
|
hash_dico[dico_hash] = (hash_type, paste_path, percent)
|
|
|
|
|
|
|
|
print '['+hash_type+'] '+'comparing: ' + str(PST.p_path[44:]) + ' and ' + str(paste_path[44:]) + ' percentage: ' + str(percent)
|
|
|
|
except Exception,e:
|
|
|
|
print str(e)
|
|
|
|
#print 'hash not comparable, bad hash: '+dico_hash+' , current_hash: '+paste_hash
|
2016-07-18 15:50:41 +02:00
|
|
|
|
|
|
|
# Add paste in DB after checking to prevent its analysis twice
|
2016-08-04 12:10:56 +02:00
|
|
|
# hash_type_i -> index_i AND index_i -> PST.PATH
|
2016-07-18 15:50:41 +02:00
|
|
|
r_serv1.set(index, PST.p_path)
|
|
|
|
r_serv1.sadd("INDEX", index)
|
2016-08-04 12:10:56 +02:00
|
|
|
# Adding hashes in Redis
|
2016-08-04 11:55:38 +02:00
|
|
|
for hash_type, paste_hash in paste_hashes.iteritems():
|
|
|
|
r_serv1.set(paste_hash, index)
|
|
|
|
r_serv1.sadd("HASHS_"+hash_type, paste_hash)
|
2016-08-04 12:10:56 +02:00
|
|
|
|
2016-07-18 15:50:41 +02:00
|
|
|
##################### Similarity found #######################
|
|
|
|
|
|
|
|
# if there is data in this dictionnary
|
|
|
|
if len(hash_dico) != 0:
|
|
|
|
# paste_tuple = (paste_path, percent)
|
|
|
|
for dico_hash, paste_tuple in hash_dico.items():
|
|
|
|
dupl.append(paste_tuple)
|
|
|
|
|
|
|
|
# Creating the object attribute and save it.
|
|
|
|
to_print = 'Duplicate;{};{};{};'.format(
|
|
|
|
PST.p_source, PST.p_date, PST.p_name)
|
|
|
|
if dupl != []:
|
|
|
|
PST.__setattr__("p_duplicate", dupl)
|
|
|
|
PST.save_attribute_redis("p_duplicate", dupl)
|
|
|
|
publisher.info('{}Detected {}'.format(to_print, len(dupl)))
|
2016-07-18 16:22:33 +02:00
|
|
|
print '{}Detected {}'.format(to_print, len(dupl))
|
2016-07-18 15:50:41 +02:00
|
|
|
|
|
|
|
y = time.time()
|
|
|
|
|
|
|
|
publisher.debug('{}Processed in {} sec'.format(to_print, y-x))
|
|
|
|
#print '{}Processed in {} sec'.format(to_print, y-x)
|
2016-08-04 11:55:38 +02:00
|
|
|
|
2016-07-18 15:50:41 +02:00
|
|
|
except IOError:
|
|
|
|
to_print = 'Duplicate;{};{};{};'.format(
|
|
|
|
PST.p_source, PST.p_date, PST.p_name)
|
|
|
|
print "CRC Checksum Failed on :", PST.p_path
|
|
|
|
publisher.error('{}CRC Checksum Failed'.format(to_print))
|