mirror of https://github.com/CIRCL/AIL-framework
231 lines
9.7 KiB
Python
Executable File
231 lines
9.7 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
# -*-coding:UTF-8 -*
|
|
|
|
import os
|
|
import sys
|
|
|
|
sys.path.append(os.environ['AIL_BIN'])
|
|
##################################
|
|
# Import Project packages
|
|
##################################
|
|
from lib.ConfigLoader import ConfigLoader
|
|
|
|
config_loader = ConfigLoader()
|
|
r_metadata = config_loader.get_db_conn("Kvrocks_Correlations")
|
|
config_loader = None
|
|
|
|
##################################
|
|
# CORRELATION MIGRATION
|
|
##################################
|
|
#
|
|
# MIGRATE TO KVROCKS + Rename correlation Keys
|
|
# => Add support for correlations between subtypes
|
|
# => Common correlation engine for each objects
|
|
#
|
|
# Objects Iterations: -screenshot
|
|
# -decoded
|
|
# -subtypes
|
|
# -domains
|
|
#
|
|
# /!\ Handle reinsertion /!\
|
|
#
|
|
#
|
|
# CORRELATION DB ????? => purge if needed
|
|
#
|
|
#
|
|
#
|
|
#
|
|
#
|
|
##################################
|
|
# CORRELATION MIGRATION
|
|
##################################
|
|
|
|
CORRELATION_TYPES_BY_OBJ = {
|
|
"chat": ["chat-subchannel", "chat-thread", "image", "message", "ocr", "user-account"], # message or direct correlation like cve, bitcoin, ... ???
|
|
"chat-subchannel": ["chat", "chat-thread", "image", "message", "ocr", "user-account"],
|
|
"chat-thread": ["chat", "chat-subchannel", "image", "message", "ocr", "user-account"],
|
|
"cookie-name": ["domain"],
|
|
"cryptocurrency": ["domain", "item", "message", "ocr", "qrcode"],
|
|
"cve": ["domain", "item", "message", "ocr", "qrcode"],
|
|
"decoded": ["domain", "item", "message", "ocr", "qrcode"],
|
|
"domain": ["cve", "cookie-name", "cryptocurrency", "decoded", "etag", "favicon", "hhhash", "item", "pgp", "title", "screenshot", "username"],
|
|
"etag": ["domain"],
|
|
"favicon": ["domain", "item"], # TODO Decoded
|
|
"file-name": ["chat", "message"],
|
|
"hhhash": ["domain"],
|
|
"image": ["chat", "chat-subchannel", "chat-thread", "message", "ocr", "qrcode", "user-account"], # TODO subchannel + threads ????
|
|
"item": ["cve", "cryptocurrency", "decoded", "domain", "favicon", "pgp", "screenshot", "title", "username"], # chat ???
|
|
"message": ["chat", "chat-subchannel", "chat-thread", "cve", "cryptocurrency", "decoded", "file-name", "image", "ocr", "pgp", "user-account"],
|
|
"ocr": ["chat", "chat-subchannel", "chat-thread", "cve", "cryptocurrency", "decoded", "image", "message", "pgp", "user-account"],
|
|
"pgp": ["domain", "item", "message", "ocr"],
|
|
"qrcode": ["chat", "cve", "cryptocurrency", "decoded", "domain", "image", "message", "screenshot"], # "chat-subchannel", "chat-thread" ?????
|
|
"screenshot": ["domain", "item", "qrcode"],
|
|
"title": ["domain", "item"],
|
|
"user-account": ["chat", "chat-subchannel", "chat-thread", "image", "message", "ocr", "username"],
|
|
"username": ["domain", "item", "message", "user-account"],
|
|
}
|
|
|
|
def get_obj_correl_types(obj_type):
|
|
return CORRELATION_TYPES_BY_OBJ.get(obj_type)
|
|
|
|
def sanityze_obj_correl_types(obj_type, correl_types, sanityze=True):
|
|
if not sanityze:
|
|
return correl_types
|
|
obj_correl_types = get_obj_correl_types(obj_type)
|
|
if correl_types:
|
|
correl_types = set(correl_types).intersection(obj_correl_types)
|
|
if not correl_types:
|
|
correl_types = obj_correl_types
|
|
if not correl_types:
|
|
return []
|
|
return correl_types
|
|
|
|
def get_nb_correlation_by_correl_type(obj_type, subtype, obj_id, correl_type):
|
|
return r_metadata.scard(f'correlation:obj:{obj_type}:{subtype}:{correl_type}:{obj_id}')
|
|
|
|
def get_nb_correlations(obj_type, subtype, obj_id, filter_types=[]):
|
|
if subtype is None:
|
|
subtype = ''
|
|
obj_correlations = {}
|
|
filter_types = sanityze_obj_correl_types(obj_type, filter_types)
|
|
for correl_type in filter_types:
|
|
obj_correlations[correl_type] = get_nb_correlation_by_correl_type(obj_type, subtype, obj_id, correl_type)
|
|
return obj_correlations
|
|
|
|
def get_correlation_by_correl_type(obj_type, subtype, obj_id, correl_type, unpack=False):
|
|
correl = r_metadata.smembers(f'correlation:obj:{obj_type}:{subtype}:{correl_type}:{obj_id}')
|
|
if unpack:
|
|
unpacked = []
|
|
for str_correl in correl:
|
|
unpacked.append(str_correl.split(':', 1))
|
|
return unpacked
|
|
else:
|
|
return correl
|
|
|
|
def get_correlations(obj_type, subtype, obj_id, filter_types=[], unpack=False, sanityze=True):
|
|
if subtype is None:
|
|
subtype = ''
|
|
obj_correlations = {}
|
|
filter_types = sanityze_obj_correl_types(obj_type, filter_types, sanityze=sanityze)
|
|
for correl_type in filter_types:
|
|
obj_correlations[correl_type] = get_correlation_by_correl_type(obj_type, subtype, obj_id, correl_type,
|
|
unpack=unpack)
|
|
return obj_correlations
|
|
|
|
def exists_obj_correlation(obj_type, subtype, obj_id, obj2_type):
|
|
if subtype is None:
|
|
subtype = ''
|
|
return r_metadata.exists(f'correlation:obj:{obj_type}:{subtype}:{obj2_type}:{obj_id}')
|
|
|
|
def is_obj_correlated(obj_type, subtype, obj_id, obj2_type, subtype2, obj2_id):
|
|
if subtype is None:
|
|
subtype = ''
|
|
if subtype2 is None:
|
|
subtype2 = ''
|
|
try:
|
|
return r_metadata.sismember(f'correlation:obj:{obj_type}:{subtype}:{obj2_type}:{obj_id}', f'{subtype2}:{obj2_id}')
|
|
except:
|
|
return False
|
|
|
|
def get_obj_inter_correlation(obj_type1, subtype1, obj_id1, obj_type2, subtype2, obj_id2, correl_type):
|
|
return r_metadata.sinter(f'correlation:obj:{obj_type1}:{subtype1}:{correl_type}:{obj_id1}', f'correlation:obj:{obj_type2}:{subtype2}:{correl_type}:{obj_id2}')
|
|
|
|
def add_obj_correlation(obj1_type, subtype1, obj1_id, obj2_type, subtype2, obj2_id):
|
|
if subtype1 is None:
|
|
subtype1 = ''
|
|
if subtype2 is None:
|
|
subtype2 = ''
|
|
r_metadata.sadd(f'correlation:obj:{obj1_type}:{subtype1}:{obj2_type}:{obj1_id}', f'{subtype2}:{obj2_id}')
|
|
r_metadata.sadd(f'correlation:obj:{obj2_type}:{subtype2}:{obj1_type}:{obj2_id}', f'{subtype1}:{obj1_id}')
|
|
|
|
|
|
def delete_obj_correlation(obj1_type, subtype1, obj1_id, obj2_type, subtype2, obj2_id):
|
|
if subtype1 is None:
|
|
subtype1 = ''
|
|
if subtype2 is None:
|
|
subtype2 = ''
|
|
r_metadata.srem(f'correlation:obj:{obj1_type}:{subtype1}:{obj2_type}:{obj1_id}', f'{subtype2}:{obj2_id}')
|
|
r_metadata.srem(f'correlation:obj:{obj2_type}:{subtype2}:{obj1_type}:{obj2_id}', f'{subtype1}:{obj1_id}')
|
|
|
|
def delete_obj_correlations(obj_type, subtype, obj_id):
|
|
obj_correlations = get_correlations(obj_type, subtype, obj_id)
|
|
for correl_type in obj_correlations:
|
|
for str_obj in obj_correlations[correl_type]:
|
|
subtype2, obj2_id = str_obj.split(':', 1)
|
|
delete_obj_correlation(obj_type, subtype, obj_id, correl_type, subtype2, obj2_id)
|
|
|
|
# # bypass max result/objects ???
|
|
# def get_correlation_depht(obj_type, subtype, obj_id, filter_types=[], level=1, nb_max=300):
|
|
# objs = set()
|
|
# _get_correlation_depht(objs, obj_type, subtype, obj_id, filter_types, level, nb_max)
|
|
# return objs
|
|
#
|
|
# def _get_correlation_depht(objs, obj_type, subtype, obj_id, filter_types, level, nb_max, previous_str_obj=''):
|
|
# obj_str_id = get_obj_str_id(obj_type, subtype, obj_id)
|
|
# objs.add(obj_str_id)
|
|
#
|
|
# obj_correlations = get_correlations(obj_type, subtype, obj_id, filter_types=filter_types)
|
|
# for correl_type in obj_correlations:
|
|
# for str_obj in obj_correlations[correl_type]:
|
|
# subtype2, obj2_id = str_obj.split(':', 1)
|
|
# obj2_str_id = get_obj_str_id(correl_type, subtype2, obj2_id)
|
|
#
|
|
# if obj2_str_id == previous_str_obj:
|
|
# continue
|
|
#
|
|
# if len(nodes) > nb_max:
|
|
# break
|
|
# objs.add(obj2_str_id)
|
|
#
|
|
# if level > 0:
|
|
# next_level = level - 1
|
|
# _get_correlation_depht(objs, correl_type, subtype2, obj2_id, filter_types, next_level, nb_max,
|
|
# previous_str_obj=obj_str_id)
|
|
|
|
def get_obj_str_id(obj_type, subtype, obj_id):
|
|
if subtype is None:
|
|
subtype = ''
|
|
return f'{obj_type}:{subtype}:{obj_id}'
|
|
|
|
def get_correlations_graph_nodes_links(obj_type, subtype, obj_id, filter_types=[], max_nodes=300, level=1, objs_hidden=set(), flask_context=False):
|
|
links = set()
|
|
nodes = set()
|
|
meta = {'complete': True, 'objs': set()}
|
|
|
|
obj_str_id = get_obj_str_id(obj_type, subtype, obj_id)
|
|
|
|
_get_correlations_graph_node(links, nodes, meta, obj_type, subtype, obj_id, level, max_nodes, filter_types=filter_types, objs_hidden=objs_hidden, previous_str_obj='')
|
|
return obj_str_id, nodes, links, meta
|
|
|
|
|
|
def _get_correlations_graph_node(links, nodes, meta, obj_type, subtype, obj_id, level, max_nodes, filter_types=[], objs_hidden=set(), previous_str_obj=''):
|
|
obj_str_id = get_obj_str_id(obj_type, subtype, obj_id)
|
|
meta['objs'].add(obj_str_id)
|
|
nodes.add(obj_str_id)
|
|
|
|
obj_correlations = get_correlations(obj_type, subtype, obj_id, filter_types=filter_types)
|
|
# print(obj_correlations)
|
|
for correl_type in obj_correlations:
|
|
for str_obj in obj_correlations[correl_type]:
|
|
subtype2, obj2_id = str_obj.split(':', 1)
|
|
obj2_str_id = get_obj_str_id(correl_type, subtype2, obj2_id)
|
|
# filter objects to hide
|
|
if obj2_str_id in objs_hidden:
|
|
continue
|
|
|
|
meta['objs'].add(obj2_str_id)
|
|
|
|
if obj2_str_id == previous_str_obj:
|
|
continue
|
|
|
|
if len(nodes) > max_nodes != 0:
|
|
meta['complete'] = False
|
|
break
|
|
nodes.add(obj2_str_id)
|
|
links.add((obj_str_id, obj2_str_id))
|
|
|
|
if level > 0:
|
|
next_level = level - 1
|
|
_get_correlations_graph_node(links, nodes, meta, correl_type, subtype2, obj2_id, next_level, max_nodes, filter_types=filter_types, objs_hidden=objs_hidden, previous_str_obj=obj_str_id)
|
|
|