2019-11-21 13:10:29 +01:00
#!/usr/bin/env python3
"""
2021-07-29 13:13:31 +02:00
Deprecation notice : this module will be deprecated by December 2021 , please use vmware_nsx module .
2019-11-21 13:10:29 +01:00
Module ( type " expansion " ) to submit files and URLs to Lastline for analysis .
"""
import base64
import io
import json
import zipfile
import lastline_api
misperrors = {
" error " : " Error " ,
}
mispattributes = {
" input " : [
" attachment " ,
" malware-sample " ,
" url " ,
] ,
" output " : [
" link " ,
] ,
}
moduleinfo = {
2024-08-12 11:23:10 +02:00
' version ' : ' 0.1 ' ,
' author ' : ' Stefano Ortolani ' ,
' description ' : ' Deprecation notice: this module will be deprecated by December 2021, please use vmware_nsx module. \n \n Module to submit a file or URL to Lastline. ' ,
' module-type ' : [ ' expansion ' , ' hover ' ] ,
' name ' : ' Lastline Submit ' ,
' logo ' : ' lastline.png ' ,
' requirements ' : [ ] ,
' features ' : ' The module requires a Lastline Analysis `api_token` and `key`. \n When the analysis is completed, it is possible to import the generated report by feeding the analysis link to the [lastline_query](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/expansion/lastline_query.py) module. ' ,
' references ' : [ ' https://www.lastline.com ' ] ,
' input ' : ' File or URL to submit to Lastline. ' ,
' output ' : ' Link to the report generated by Lastline. ' ,
2019-11-21 13:10:29 +01:00
}
moduleconfig = [
2019-12-28 15:57:15 +01:00
" url " ,
2019-11-21 13:10:29 +01:00
" api_token " ,
2019-12-28 15:57:15 +01:00
" key " ,
2019-11-21 13:10:29 +01:00
]
DEFAULT_ZIP_PASSWORD = b " infected "
def __unzip ( zipped_data , password = None ) :
data_file_object = io . BytesIO ( zipped_data )
with zipfile . ZipFile ( data_file_object ) as zip_file :
sample_hashname = zip_file . namelist ( ) [ 0 ]
data_zipped = zip_file . read ( sample_hashname , password )
return data_zipped
def __str_to_bool ( x ) :
return x in ( " True " , " true " , True )
def introspection ( ) :
return mispattributes
def version ( ) :
moduleinfo [ " config " ] = moduleconfig
return moduleinfo
def handler ( q = False ) :
if q is False :
return False
request = json . loads ( q )
# Parse the init parameters
try :
2019-12-28 15:57:15 +01:00
config = request . get ( " config " , { } )
auth_data = lastline_api . LastlineAbstractClient . get_login_params_from_dict ( config )
api_url = config . get ( " url " , lastline_api . DEFAULT_LL_ANALYSIS_API_URL )
2019-11-21 13:10:29 +01:00
except Exception as e :
misperrors [ " error " ] = " Error parsing configuration: {} " . format ( e )
return misperrors
# Parse the call parameters
try :
2019-12-28 15:57:15 +01:00
call_args = { }
2019-11-21 13:10:29 +01:00
if " url " in request :
# URLs are text strings
2019-12-28 15:57:15 +01:00
api_method = lastline_api . AnalysisClient . submit_url
2019-11-21 13:10:29 +01:00
call_args [ " url " ] = request . get ( " url " )
else :
data = request . get ( " data " )
# Malware samples are zip-encrypted and then base64 encoded
if " malware-sample " in request :
2019-12-28 15:57:15 +01:00
api_method = lastline_api . AnalysisClient . submit_file
2019-11-21 13:10:29 +01:00
call_args [ " file_data " ] = __unzip ( base64 . b64decode ( data ) , DEFAULT_ZIP_PASSWORD )
call_args [ " file_name " ] = request . get ( " malware-sample " ) . split ( " | " , 1 ) [ 0 ]
call_args [ " password " ] = DEFAULT_ZIP_PASSWORD
# Attachments are just base64 encoded
elif " attachment " in request :
2019-12-28 15:57:15 +01:00
api_method = lastline_api . AnalysisClient . submit_file
2019-11-21 13:10:29 +01:00
call_args [ " file_data " ] = base64 . b64decode ( data )
call_args [ " file_name " ] = request . get ( " attachment " )
else :
raise ValueError ( " Input parameters do not specify either an URL or a file " )
except Exception as e :
misperrors [ " error " ] = " Error processing input parameters: {} " . format ( e )
return misperrors
# Make the API call
try :
2019-12-28 15:57:15 +01:00
api_client = lastline_api . AnalysisClient ( api_url , auth_data )
2019-11-21 13:10:29 +01:00
response = api_method ( api_client , * * call_args )
task_uuid = response . get ( " task_uuid " )
if not task_uuid :
raise ValueError ( " Unable to process returned data " )
if response . get ( " score " ) is not None :
tags = [ " workflow:state= ' complete ' " ]
else :
tags = [ " workflow:state= ' incomplete ' " ]
except Exception as e :
misperrors [ " error " ] = " Error issuing the API call: {} " . format ( e )
return misperrors
# Assemble and return
2019-12-28 15:57:15 +01:00
analysis_link = lastline_api . get_task_link ( task_uuid , analysis_url = api_url )
2019-11-21 13:10:29 +01:00
return {
" results " : [
{
" types " : " link " ,
" categories " : [ " External analysis " ] ,
" values " : analysis_link ,
" tags " : tags ,
} ,
]
}
if __name__ == " __main__ " :
""" Test submitting a test subject to the Lastline backend. """
import argparse
import configparser
parser = argparse . ArgumentParser ( )
parser . add_argument ( " -c " , " --config-file " , dest = " config_file " )
parser . add_argument ( " -s " , " --section-name " , dest = " section_name " )
args = parser . parse_args ( )
c = configparser . ConfigParser ( )
c . read ( args . config_file )
2019-12-28 15:57:15 +01:00
a = lastline_api . LastlineAbstractClient . get_login_params_from_conf ( c , args . section_name )
2019-11-21 13:10:29 +01:00
j = json . dumps (
{
" config " : a ,
2019-12-28 15:57:15 +01:00
" url " : " https://www.google.exe.com " ,
2019-11-21 13:10:29 +01:00
}
)
print ( json . dumps ( handler ( j ) , indent = 4 , sort_keys = True ) )
with open ( " ./tests/test_files/test.docx " , " rb " ) as f :
data = f . read ( )
j = json . dumps (
{
" config " : a ,
" data " : base64 . b64encode ( data ) . decode ( " utf-8 " ) ,
" attachment " : " test.docx " ,
}
)
print ( json . dumps ( handler ( j ) , indent = 4 , sort_keys = True ) )