def run(self, results): if not os.path.isfile(os.path.join(self.reports_path, "summary-report.html")): raise CuckooReportError( "Unable to open summary HTML report to convert to PDF: Ensure reporthtmlsummary is enabled in reporting.conf" ) if os.path.exists("/usr/bin/xvfb-run") and os.path.exists("/usr/bin/wkhtmltopdf"): call( [ "/usr/bin/xvfb-run", "--auto-servernum", "--server-num", "1", "/usr/bin/wkhtmltopdf", "-q", os.path.join(self.reports_path, "summary-report.html"), os.path.join(self.reports_path, "report.pdf"), ] ) return True if not HAVE_WEASYPRINT: raise CuckooReportError( "Failed to generate PDF report: Neither wkhtmltopdf nor Weasyprint Python library are installed" ) logger = logging.getLogger("weasyprint") logger.handlers = [] logger.setLevel(logging.ERROR) HTML(os.path.join(self.reports_path, "summary-report.html")).write_pdf(os.path.join(self.reports_path, "report.pdf")) return True
def run(self, results): if not HAVE_REQUESTS: raise CuckooOperationalError( "The Mattermost processing module requires the requests " "library (install with `pip install requests`)") sigs, urls = [], [] for sig in results.get("signatures", []): sigs.append(sig.get("name")) if sig.get("name") == "network_http": for http in sig.get("marks"): urls.append(http.get("ioc")) post = "Finished analyze ::: [{0}]({1}{0}) ::: ".format( results.get("info", {}).get("id"), self.options.get("myurl")) if results.get("info", {}).get("category") == "file": target = results.get("target", {}).get("file", {}).get("name", "") if self.options.get("hash-filename"): target = hashlib.sha256(target).hexdigest() elif results.get("info", {}).get("category") == "url": target = results.get("target", {}).get("url", "") if self.options.get("hash-url"): target = hashlib.sha256(target).hexdigest() else: target = "???" post += "Target : {0} ::: Score : **{1}** ::: ".format( target, results.get("info", {}).get("score")) if self.options.get("show-virustotal"): post += "**VT : {0} / {1}**\n".format( results.get("virustotal", {}).get("positives"), results.get("virustotal", {}).get("total"), ) if self.options.get("show-signatures"): post += "**Signatures** ::: {0} \n".format(" : ".join(sigs)) if self.options.get("show-urls"): post += "**URLS**\n`{0}`".format("\n".join(urls).replace( ".", "[.]")) data = { "username": self.options.get("username"), "text": post, } try: r = requests.post(self.options.get("url"), json=data) # note that POST max size is 4000 chars by default if r.status_code != 200: raise CuckooReportError( "Failed posting message due to : {0}".format(r.text)) except Exception as e: raise CuckooReportError( "Failed posting message to Mattermost: {0}".format(e))
def run(self, results): """Writes report. @param results: Cuckoo results dict. @raise CuckooReportError: if fails to write report. """ if not HAVE_JINJA2: raise CuckooReportError("Failed to generate summary HTML report: " "Jinja2 Python library is not installed") shots_path = os.path.join(self.analysis_path, "shots") if os.path.exists(shots_path): shots = [] counter = 1 for shot_name in os.listdir(shots_path): if not shot_name.endswith(".jpg"): continue shot_path = os.path.join(shots_path, shot_name) if os.path.getsize(shot_path) == 0: continue shot = {} shot["id"] = os.path.splitext(File(shot_path).get_name())[0] shot["data"] = base64.b64encode(open(shot_path, "rb").read()) shots.append(shot) counter += 1 shots.sort(key=lambda shot: shot["id"]) results["screenshots"] = shots else: results["screenshots"] = [] env = Environment(autoescape=True) env.loader = FileSystemLoader(os.path.join(CUCKOO_ROOT, "data", "html")) try: tpl = env.get_template("report.html") html = tpl.render({"results": results, "summary_report": True}) except Exception as e: raise CuckooReportError( "Failed to generate summary HTML report: %s" % e) try: with codecs.open(os.path.join(self.reports_path, "summary-report.html"), "w", encoding="utf-8") as report: report.write(html) except (TypeError, IOError) as e: raise CuckooReportError("Failed to write summary HTML report: %s" % e) return True
def run(self, results): """Writes report. @param results: Cuckoo results dict. @raise CuckooReportError: if fails to write report. """ if not HAVE_MAKO: raise CuckooReportError( "Failed to generate HTML report: python Mako library is not installed" ) shots_path = os.path.join(self.analysis_path, "shots") if os.path.exists(shots_path): shots = [] counter = 1 for shot_name in os.listdir(shots_path): if not shot_name.endswith(".png"): continue shot_path = os.path.join(shots_path, shot_name) if os.path.getsize(shot_path) == 0: continue shot = {} shot["id"] = os.path.splitext(File(shot_path).get_name())[0] shot["data"] = base64.b64encode(open(shot_path, "rb").read()) #shot["cum"] = "Seppia is gay" shots.append(shot) counter += 1 shots.sort(key=lambda shot: shot["id"]) results["screenshots"] = shots else: results["screenshots"] = [] lookup = TemplateLookup( directories=[os.path.join(CUCKOO_ROOT, "data", "html")], output_encoding='utf-8', encoding_errors='replace') template = lookup.get_template("report.html") try: html = template.render(**results) except Exception as e: raise CuckooReportError("Failed to generate HTML report: %s" % e) try: report = open(os.path.join(self.reports_path, "report.html"), "w") report.write(html) report.close() except (TypeError, IOError) as e: raise CuckooReportError("Failed to generate HTML report: %s" % e) return True
def create_incident(self): """Create an incident to represent the analysis in ThreatConnect. @raise CuckooReportError: if fails to write report. """ # Instantiate an incidents object incidents = self.tc.incidents() # Get todays date and the filename of the analysis target date_today = datetime.date.today().strftime('%Y%m%d') if self.results.get('target').get('file').get('name'): filename = self.results['target']['file']['name'] # Build a title for the incident title = 'Cuckoo Analysis {}: {}'.format(date_today, filename) # Add the title to the object incident = incidents.add(title, self.target_source) # Get the full timestamp for the current time and set the event date date_today_iso = datetime.datetime.now().isoformat() incident.set_event_date(date_today_iso) # Add the analysis ID to an attribute if self.results.get('info').get('id'): analysis_id = self.results.get('info').get('id') incident.add_attribute('Analysis ID', analysis_id) # Build a report link and record it in the Source attribute report_link = self.report_link_template.format(analysis_id) incident.add_attribute('Source', report_link) # Commit the changes to ThreatConnect try: incident.commit() except RuntimeError as e: raise CuckooReportError('Failed to commit incident: {}'.format(e)) else: # Load the attributes into the incident object incident.load_attributes() # Mark all Cuckoo attributes with DO NOT SHARE security label for attribute in incident.attributes: if attribute.type == 'Analysis ID' or attribute.type == 'Source': attribute.add_security_label('DO NOT SHARE') # Commit the changes to ThreatConnect try: incident.commit() except RuntimeError as e: raise CuckooReportError( 'Failed to commit incident: {}'.format(e)) else: return incident.id
def connect(self): """Connects to Elasticsearch database, loads options and set connectors. @raise CuckooReportError: if unable to connect. """ try: self.es = elastic_handler except TypeError: raise CuckooReportError( "Elasticsearch connection port must be integer") except ConnectionError: raise CuckooReportError("Cannot connect to ElasticsearchDB")
def run(self, results): """ Sends report. @param results: Cuckoo results dict. @raise CuckooReportError: if fails to write report. """ # Get server options from reporting.conf server = self.options.get("host", None) port = self.options.get("port", None) proto = self.options.get("protocol", None).lower() # A few validations... if not server: raise CuckooReportError("Syslog Server IP not defined") if not port: raise CuckooReportError("Syslog Server port not defined") if not proto: raise CuckooReportError("Syslog Protocol not defined") if proto != "tcp" and proto != "udp": raise CuckooReportError("Syslog Protocol configuration error, " "protocol must be TCP or UDP.") # Generate the syslog string try: result = self.createLog(results) except: raise CuckooReportError("Error creating syslog formatted log.") # Check if the user wants it stored in the reports directory as well do_log = self.options.get("logfile", None) if do_log: logfile = self.options.get("logname", "syslog.txt") # Log syslog results to the reports directory try: syslogfile = open(str(os.path.join(self.reports_path, logfile)), "w") syslogfile.write(result) except: raise CuckooReportError("Error writing the syslog output file.") finally: syslogfile.close() # Attempt to connect to the syslog server try: server_address = (server, port) if proto == "tcp": sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect(server_address) # Attempt to send the syslog string to the syslog server try: sock.sendall(result) except: raise CuckooReportError("Failed to send data to syslog server") finally: sock.close() elif proto == "udp": sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) try: sock.sendto(result, server_address) except: raise CuckooReportError("Failed to send data to syslog server") except (UnicodeError, TypeError, IOError) as e: raise CuckooReportError("Failed to send syslog data: %s" % e)
def run(self, results): zipmemdump = self.options.get("zipmemdump", False) zipprocdump = self.options.get("zipprocdump", False) zipmemstrings = self.options.get("zipmemstrings", False) zipprocstrings = self.options.get("zipprocstrings", False) for proc in results.get("procmemory", []) or []: dmp_path = proc.get("file", None) strings_path = proc.get("strings_path", None) for option in (zipprocdump, zipprocstrings): if option and dmp_path and dmp_path.endswith( (".dmp", ".strings")) and os.path.exists(dmp_path): try: f = zipfile.ZipFile("%s.zip" % (dmp_path), "w") f.write(dmp_path, os.path.basename(dmp_path), zipfile.ZIP_DEFLATED) f.close() os.remove(dmp_path) proc["file"] = "%s.zip" % (dmp_path) except Exception as e: raise CuckooReportError( "Error creating Process Memory Zip File %s" % e) if zipmemdump and self.memory_path and os.path.exists( self.memory_path): try: f = zipfile.ZipFile("%s.zip" % (self.memory_path), "w", allowZip64=True) f.write(self.memory_path, os.path.basename(self.memory_path), zipfile.ZIP_DEFLATED) f.close() os.remove(self.memory_path) except Exception as e: raise CuckooReportError( "Error creating Full Memory Zip File %s" % e) if zipmemstrings and self.memory_path and os.path.exists( self.memory_path + ".strings"): strings_path = self.memory_path + ".strings" try: f = zipfile.ZipFile("%s.zip" % (strings_path), "w", allowZip64=True) f.write(strings_path, os.path.basename(strings_path), zipfile.ZIP_DEFLATED) f.close() os.remove(strings_path) strings_path = "%s.zip" % (strings_path) except Exception as e: raise CuckooReportError( "Error creating Full Memory Strings Zip File %s" % e)
def connect(self): """Connects to Mongo database, loads options and set connectors. @raise CuckooReportError: if unable to connect. """ host = self.options.get("host", "127.0.0.1") port = self.options.get("port", 27017) db = self.options.get("db", "cuckoo") try: self.conn = MongoClient(host, port) self.db = self.conn[db] except TypeError: raise CuckooReportError("Mongo connection port must be integer") except ConnectionFailure: raise CuckooReportError("Cannot connect to MongoDB")
def addBundle(self): """Generates MAEC bundle structure.""" if self.results["target"]["category"] == "file": self.idMap[ "prefix"] = "maec:%s" % self.results["target"]["file"]["md5"] elif self.results["target"]["category"] == "url": self.idMap["urlmd5"] = hashlib.md5( self.results["target"]["url"]).hexdigest() self.idMap["prefix"] = "maec:%s" % self.idMap["urlmd5"] else: raise CuckooReportError("Unknown target type") # Generate bundle self.m = maec.BundleType(id="%s:bnd:1" % self.idMap["prefix"], schema_version="1.1") # Analyses self.analyses = maec.AnalysesType() self.m.set_Analyses(self.analyses) # Actions self.actions = maec.ActionsType() self.m.set_Actions(self.actions) # Behaviors self.behaviors = maec.BehaviorsType() self.m.set_Behaviors(self.behaviors) # Pools self.pools = maec.PoolsType() self.m.set_Pools(self.pools)
def run(self, results): """Writes report. @param results: Cuckoo results dict. @raise CuckooReportError: if fails to write report. """ encoding = self.options.get("encoding", "utf-8") keys_to_copy = repconf.litereport.keys_to_copy.split(" ") # lite report report only has the specific keys lite_report = {k: results[k] for k in results.keys() & keys_to_copy} # add specific keys from behavior behavior_keys_to_copy = repconf.litereport.behavior_keys_to_copy.split(" ") behavior = {k: results["behavior"][k] for k in results["behavior"].keys() & behavior_keys_to_copy} lite_report["behavior"] = behavior path = os.path.join(self.reports_path, "lite.json") try: if HAVE_ORJSON: with open(path, "wb") as report: report.write(orjson.dumps(lite_report, option=orjson.OPT_INDENT_2, default=self.default)) # orjson.OPT_SORT_KEYS | else: with open(path, "w") as report: json.dump(lite_report, report, sort_keys=False, separators=(',', ':'), ensure_ascii=False) except (UnicodeError, TypeError, IOError) as e: raise CuckooReportError("Failed to generate JSON report: %s" % e)
def run(self, results): """Writes report. @param results: Cuckoo results dict. @raise CuckooReportError: if fails to write report. """ indent = self.options.get("indent", 4) encoding = self.options.get("encoding", "utf-8") try: del results['behavior'] del results['procmemory'] for tag in results['dropped']: del tag['sha1'] del tag['crc32'] del tag['sha256'] del tag['path'] del tag['ssdeep'] del tag['sha512'] del tag['md5'] del tag['size'] path = os.path.join(self.reports_path, "report.json") with codecs.open(path, "w", "utf-8") as report: json.dump(results, report, sort_keys=False, indent=int(indent), encoding=encoding) except (UnicodeError, TypeError, IOError) as e: raise CuckooReportError("Failed to generate JSON report: %s" % e)
def apply_template(self): template_path = os.path.join( CUCKOO_ROOT, "data", "elasticsearch", "template.json" ) if not os.path.exists(template_path): return False with open(template_path, "rw") as f: try: cuckoo_template = json.loads(f.read()) except ValueError: raise CuckooReportError( "Unable to read valid JSON from the ElasticSearch " "template JSON file located at: %s" % template_path ) # Create an index wildcard based off of the index name specified # in the config file, this overwrites the settings in # template.json. cuckoo_template["template"] = self.index + "-*" self.es.indices.put_template( name="cuckoo_template", body=json.dumps(cuckoo_template) ) return True
def run(self, results): """Writes report. @param results: Cuckoo results dict. @raise CuckooReportError: if fails to write report. """ indent = self.options.get("indent", 4) encoding = self.options.get("encoding", "utf-8") ram_boost = self.options.get("ram_boost", True) try: path = os.path.join(self.reports_path, "report.json") with codecs.open(path, "w", "utf-8") as report: if ram_boost: buf = json.dumps(results, sort_keys=False, indent=int(indent), encoding=encoding, ensure_ascii=False) report.write(buf) #Add method (send_report) send_report(path, self.analysis_path) else: json.dump(results, report, sort_keys=False, indent=int(indent), encoding=encoding, ensure_ascii=False) #Add method (send_report) send_report(path, self.analysis_path) except (UnicodeError, TypeError, IOError) as e: raise CuckooReportError("Failed to generate JSON report: %s" % e)
def run(self, results): """Publishes to google pubsub results topic @param results: Cuckoo results dict. @raise CuckooReportError: if fails. """ try: processed_msg_id = results["info"]["options"].get( "pubsub_msg_id", None) if (processed_msg_id != None): pubsub_client = pubsub.Client() sample_sha256 = results["target"]["file"]["sha256"] analysis_path = self.analysis_path os.system( "python utils/networkIOCFromCuckooReport.py " + analysis_path + "/reports/report.json | grep -vf utils/networkWhiteList.txt | sed -e \"s/\./\.\./g\" > " + self.analysis_path + "/networkIOC.txt") if "function" in results["info"]["options"]: results_folder = "gs://a1s-zoombox/zOOmed/" + sample_sha256[ 0:2] + "/" + sample_sha256[ 2: 4] + "/" + sample_sha256 + "/" + processed_msg_id + "/" + results[ "info"]["package"] + "/" + results["info"][ "options"]["function"] + "/" os.system( "gsutil -m cp -r " + analysis_path + "/* gs://" + quote("a1s-zoombox/zOOmed/" + sample_sha256[0:2] + "/" + sample_sha256[2:4] + "/" + sample_sha256 + "/" + processed_msg_id + "/" + results["info"]["package"] + "/" + results["info"]["options"]["function"] + "/")) else: os.system("gsutil -m cp -r " + analysis_path + "/* gs://" + quote("a1s-zoombox/zOOmed/" + sample_sha256[0:2] + "/" + sample_sha256[2:4] + "/" + sample_sha256 + "/" + processed_msg_id + "/" + results["info"]["package"] + "/")) results_folder = "gs://a1s-zoombox/zOOmed/" + sample_sha256[ 0:2] + "/" + sample_sha256[ 2: 4] + "/" + sample_sha256 + "/" + processed_msg_id + "/" + results[ "info"]["package"] + "/" #publish completion message to topic topic = pubsub_client.topic(self.options['pubsub_topic']) message_id = topic.publish( u'Detonation complete. Results uploaded.', upload_folder=results_folder, processed_msg_id=processed_msg_id, sha256=sample_sha256) log.info( u"Message published to pubsub with message_id %s at: %s" % (message_id, results_folder)) except (UnicodeError, TypeError, IOError) as e: raise CuckooReportError("Failed to publish results: %s" % e)
def run(self, results): """Runs Malheur processing @return: Nothing. Results of this processing are obtained at an arbitrary future time. """ basedir = os.path.join(CUCKOO_ROOT, "storage", "malheur") reportsdir = os.path.join(basedir, "reports") task_id = str(results["info"]["id"]) outputfile = os.path.join(basedir, "malheur.txt." + hashlib.md5(str(random.random())).hexdigest()) try: os.makedirs(reportsdir) except: pass mist = mist_convert(results) if mist: with open(os.path.join(reportsdir, task_id + ".txt"), "w") as outfile: outfile.write(mist) # might need to prevent concurrent modifications to internal state of malheur by only allowing # one analysis to be running malheur at a time path, dirs, files = os.walk(reportsdir).next() try: subprocess.call(["malheur", "--input.format", "mist", "--input.mist_level", "2", "--cluster.reject_num", "2", "-o", outputfile, "cluster", reportsdir]) # replace previous classification state with new results atomically os.rename(outputfile, outputfile[:-33]) except Exception as e: raise CuckooReportError("Failed to perform Malheur classification: %s" % e)
def run(self, results): try: #print "Start of IOC_STIX reporting module\n"+results #pcapFile = dpkt.pcap.Reader(file(self.analysis_path+"/cut-byprocessingmodule.pcap")) goodIPs = getMicrosoftDomains(self.analysis_path) synConn = getSYNInfo(self.analysis_path, goodIPs) synackconn = getSYNACKInfo(self.analysis_path, goodIPs) ackConn = getACKInfo(self.analysis_path, goodIPs) resolvedIPsArray = resolvedIPs(self.analysis_path, goodIPs) fullHTTPArray = getFullHTTP(self.analysis_path) udpconn = getUDPData(self.analysis_path, goodIPs) dnspacket = getDNSData(self.analysis_path) icmpPacket = getICMPData(self.analysis_path) ftpconn = getFTPConn(self.analysis_path) sshconn = getSSHConn(self.analysis_path) foundIPs = findStaticIPs(results["strings"]) if synConn!=[] or synackconn!=[] or ackConn!=[] or resolvedIPsArray!=[] or fullHTTPArray!=[] or udpconn!=[] or dnspacket!=[] or icmpPacket!=[] or ftpconn!=[] or sshconn!=[] or foundIPs!=[]: gatherIOCs(self.analysis_path, synConn, synackconn, ackConn, resolvedIPsArray, results, fullHTTPArray, udpconn, dnspacket, icmpPacket, ftpconn, sshconn, foundIPs) else: print "No IOCs to create" except (UnicodeError, TypeError, IOError) as e: print "Error", e raise CuckooReportError("Failed to make STIX IOCs :(") return True
def do_bulk_index(self, bulk_reqs): try: helpers.bulk(self.es, bulk_reqs) except Exception as e: raise CuckooReportError( "Failed to save results in ElasticSearch for " "task #%d: %s" % (self.task["id"], e))
def _run_report(self, plugin, data): """Run a single report plugin. @param plugin: report plugin. @param data: results data from analysis. """ current = plugin() current.set_path(self.analysis_path) current.cfg = Config(current.conf_path) module = inspect.getmodule(current) if "." in module.__name__: module_name = module.__name__.rsplit(".", 1)[1] else: module_name = module.__name__ try: current.set_options(self.cfg.get(module_name)) except CuckooOperationalError: raise CuckooReportError( "Reporting module %s not found in configuration file" % module_name) try: # Run report, for each report a brand new copy of results is # created, to prevent a reporting module to edit global # result set and affect other reporting modules. current.run(copy.deepcopy(data)) log.debug("Executed reporting module \"%s\"" % current.__class__.__name__) except NotImplementedError: return except CuckooReportError as e: log.warning("Failed to execute reporting module \"%s\": %s" % (current.__class__.__name__, e))
def run(self, results): """Writes report. @param results: Cuckoo results dict. @raise CuckooReportError: if fails to write report. """ indent = self.options.get("indent", 4) encoding = self.options.get("encoding", "utf-8") # Determine whether we want to include the behavioral data in the # JSON report. if "json.calls" in self.task["options"]: self.calls = int(self.task["options"]["json.calls"]) else: self.calls = self.options.get("calls", True) self.erase_calls(results) try: path = os.path.join(self.reports_path, "report.json") with codecs.open(path, "w", "utf-8") as report: json.dump(results, report, default=default, sort_keys=False, indent=int(indent), encoding=encoding) except (UnicodeError, TypeError, IOError) as e: raise CuckooReportError("Failed to generate JSON report: %s" % e) finally: self.restore_calls(results)
def addMetadata(self): """Generates header for MAEC xml and root components.""" if self.results["target"]["category"] == "file": id = "cuckoo:%s" % self.results["target"]["file"]["md5"] elif self.results["target"]["category"] == "url": id = "cuckoo:%s" % hashlib.md5( self.results["target"]["url"]).hexdigest() else: raise CuckooReportError("Unknown target type") self.m = maec.malwareMetaData( version="1.1", id=id, author="Cuckoo Sandbox %s" % self.results["info"]["version"], comment= "Report created with Cuckoo Sandbox %s automated and open source malware sandbox: http://www.cuckoosandbox.org" % self.results["info"]["version"], timestamp=datetime_to_iso(self.results["info"]["started"])) # Objects self.objects = maec.objectsType() self.m.set_objects(self.objects) # Object Properties self.properties = maec.objectPropertiesType() self.m.set_objectProperties(self.properties) # Relationships self.relationships = maec.relationshipsType() self.m.set_relationships(self.relationships)
def do_mongo(self, report_mongo, report, t, node): """This fucntion will store behavior and webgui report without reprocess""" if HAVE_MONGO: try: conn = MongoClient(reporting_conf.mongodb.host, reporting_conf.mongodb.port) mongo_db = conn[reporting_conf.mongodb.db] except ConnectionFailure: raise CuckooReportError("Cannot connect to MongoDB") return if "processes" in report.get("behavior", {}): new_processes = [] for process in report.get("behavior", {}).get("processes", []): new_process = dict(process) chunk = [] chunks_ids = [] # Loop on each process call. for index, call in enumerate(process["calls"]): # If the chunk size is 100 or if the loop is completed then # store the chunk in MongoDB. if len(chunk) == 100: to_insert = { "pid": process["process_id"], "calls": chunk } chunk_id = mongo_db.calls.insert(to_insert) chunks_ids.append(chunk_id) # Reset the chunk. chunk = [] # Append call to the chunk. chunk.append(call) # Store leftovers. if chunk: to_insert = {"pid": process["process_id"], "calls": chunk} chunk_id = mongo_db.calls.insert(to_insert) chunks_ids.append(chunk_id) # Add list of chunks. new_process["calls"] = chunks_ids new_processes.append(new_process) # Store the results in the report. report_mongo["behavior"]["processes"] = new_processes #patch info.id to have the same id as in main db report_mongo["info"]["id"] = t.main_task_id mongo_db.analysis.save(report_mongo) # set complated_on time main_db.set_status(t.main_task_id, TASK_COMPLETED) # set reported time main_db.set_status(t.main_task_id, TASK_REPORTED) conn.close()
def connect(self): """Connect to Elasticsearch. @raise CuckooReportError: if unable to connect. """ hosts = [] for host in self.options.get("hosts", "127.0.0.1:9200").split(","): if host.strip(): hosts.append(host.strip()) self.index = self.options.get("index", "cuckoo") # Do not change these types without changing the elasticsearch # template as well. self.report_type = "cuckoo" self.call_type = "call" # Get the index time option and set the dated index accordingly index_type = self.options.get("index_time_pattern", "yearly") if index_type.lower() == "yearly": strf_time = "%Y" elif index_type.lower() == "monthly": strf_time = "%Y-%m" elif index_type.lower() == "daily": strf_time = "%Y-%m-%d" date_index = datetime.datetime.utcnow().strftime(strf_time) self.dated_index = "%s-%s" % (self.index, date_index) # Gets the time which will be used for indexing the document into ES # ES needs epoch time in seconds per the mapping self.report_time = int(time.time()) self.template_name = self.index + "_template" try: self.es = Elasticsearch(hosts) except TypeError: raise CuckooReportError( "Elasticsearch connection hosts must be host:port or host") except (ConnectionError, ConnectionTimeout) as e: raise CuckooReportError("Cannot connect to Elasticsearch: %s" % e) # check to see if the template exists apply it if it does not if not self.es.indices.exists_template(self.template_name): if not self.apply_template(): raise CuckooReportError("Cannot apply Elasticsearch template")
def connect(self): """Connects to Mongo database, loads options and set connectors. @raise CuckooReportError: if unable to connect. """ try: self.conn = MongoClient( self.options.get("host", "127.0.0.1"), port=self.options.get("port", 27017), username=self.options.get("username", None), password=self.options.get("password", None), authSource=self.options.get("authsource", "cuckoo"), ) self.db = self.conn[self.options.get("db", "cuckoo")] except TypeError: raise CuckooReportError("Mongo connection port must be integer") except ConnectionFailure: raise CuckooReportError("Cannot connect to MongoDB")
def run(self, results): if not HAVE_REQUESTS: raise CuckooOperationalError( "The Mattermost processing module requires the requests " "library (install with `pip install requests`)" ) sigs, urls = [], [] for sig in results.get("signatures", []): sigs.append(sig.get("name")) if sig.get("name") == "network_http": for http in sig.get("marks"): urls.append(http.get("ioc")) post = "Finished analyze ::: [{0}]({1}{0}) ::: ".format( results.get("info", {}).get("id"), self.options.get("myurl") ) filename = results.get("target", {}).get("file", {}).get("name", "") if self.options.get("hash-filename"): filename = hashlib.sha256(filename).hexdigest() post += "File : {0} ::: Score : **{1}** ::: ".format( filename, results.get("info", {}).get("score") ) if self.options.get("show-virustotal"): post += "**VT : {0} / {1}**\n".format( results.get("virustotal", {}).get("positives"), results.get("virustotal", {}).get("total"), ) if self.options.get("show-signatures"): post += "**Signatures** ::: {0} \n".format(" : ".join(sigs)) if self.options.get("show-urls"): post += "**URLS**\n`{0}`".format( "\n".join(urls).replace(".", "[.]") ) data = { "username": self.options.get("username"), "text": post, } headers = {"Content-Type": "application/json"} try: requests.post( self.options.get("url"), headers=headers, data=json.dumps(data) ) except Exception as e: raise CuckooReportError( "Failed posting message to Mattermost: %s" % e )
def connect(self): """Connects to Mongo database, loads options and set connectors. @raise CuckooReportError: if unable to connect. """ host = self.options.get("host", "127.0.0.1") port = self.options.get("port", 27017) user = self.options.get("user", "cuckoo") pwd = self.options.get("passwd", "cuckoo") dbname = self.options.get("database", "cuckoo") try: self.conn = MongoClient("mongodb://%s:%s@%s:%d/%s" % (user, pwd, host, port, dbname)) self.db = self.conn[dbname] self.fs = GridFS(self.db) except TypeError: raise CuckooReportError("Mongo connection port must be integer") except ConnectionFailure: raise CuckooReportError("Cannot connect to MongoDB")
def connect(self): """Connect to Elasticsearch. @raise CuckooReportError: if unable to connect. """ hosts = [] for host in self.options.get("hosts", "127.0.0.1:9200").split(","): if host.strip(): hosts.append(host.strip()) self.index = self.options.get("index", "cuckoo") self.type_ = self.options.get("type", "cuckoo") try: self.es = Elasticsearch(hosts) except TypeError: raise CuckooReportError( "Elasticsearch connection hosts must be host:port or host") except (ConnectionError, ConnectionTimeout) as e: raise CuckooReportError("Cannot connect to Elasticsearch: %s" % e)
def run(self, results): """Writes report. @param results: Cuckoo results dict. @raise CuckooReportError: if fails to write report. """ try: report = open(os.path.join(self.reports_path, "report.json"), "w") report.write(json.dumps(results, sort_keys=False, indent=4)) report.close() except (TypeError, IOError) as e: raise CuckooReportError("Failed to generate JSON report: %s" % e)
def do_index(self, obj): index = "%s-%d" % (self.index, self.task["id"]) try: self.es.create(index=index, doc_type=self.type_, body=obj) except Exception as e: raise CuckooReportError( "Failed to save results in ElasticSearch for " "task #%d: %s" % (self.task["id"], e)) self.idx += 1
def run(self, results): """Invokes IOCAware OpenIOC script. @param results: Cuckoo results dict @raise CuckooReportError: if fails to write report """ try: # Make call to create ioc from cuckoo results doCuckoo(results) except (UnicodeError, TypeError, IOError) as e: raise CuckooReportError("Failed to generate IOC results: %s" % e)