diff --git a/analyzers/IRMA/irma.json b/analyzers/IRMA/irma.json new file mode 100644 index 000000000..9d4605602 --- /dev/null +++ b/analyzers/IRMA/irma.json @@ -0,0 +1,13 @@ +{ + "name": "IRMA_Scan", + "version": "0.1", + "description": "IRMA scan file", + "dataTypeList": ["file"], + "baseConfig": "IRMA", + "config": { + "check_tlp": false, + "service": "scan", + "max_tlp": 1 + }, + "command": "IRMA/irma.py" +} diff --git a/analyzers/IRMA/irma.py b/analyzers/IRMA/irma.py new file mode 100644 index 000000000..c6b5fd091 --- /dev/null +++ b/analyzers/IRMA/irma.py @@ -0,0 +1,156 @@ +#!/usr/bin/env python +# encoding: utf-8 + +import sys +import time +import hashlib +import requests +import json +import urlparse + +from cortexutils.analyzer import Analyzer + +class IRMA(Analyzer): + + def __init__(self): + Analyzer.__init__(self) + self.service = self.getParam( + 'config.service', None, 'Service parameter is missing') + self.url = self.getParam( + 'config.url', None, 'IRMA URL parameter is missing') + self.timeout = self.getParam( + 'config.timeout', 60) + self.scan = self.getParam( + 'config.scan', 1) + self.force = self.getParam( + 'config.force', 1) + self.verify = self.getParam( + 'config.verify', True) + self.time_start = time.time() + + def summary(self, raw): + result = { + "has_result": True + } + + if 'probe_results' in raw: + result['analysis_results'] = raw['probe_results'] + + return result + + """Gets antivirus signatures from IRMA for various results. + Currently obtains IRMA results for the target sample. + """ + # IRMA statuses https://github.com/quarkslab/irma-cli/blob/master/irma/apiclient.py + IRMA_FINISHED_STATUS = 50 + + def _request_json(self, url, **kwargs): + """Wrapper around doing a request and parsing its JSON output.""" + try: + r = requests.get(url, timeout=self.timeout, verify=self.verify, **kwargs) + return r.json() if r.status_code == 200 else {} + except (requests.ConnectionError, ValueError) as e: + self.unexpectedError(e) + + def _post_json(self, url, **kwargs): + """Wrapper around doing a post and parsing its JSON output.""" + try: + r = requests.post(url, timeout=self.timeout, verify=self.verify, **kwargs) + return r.json() if r.status_code == 200 else {} + except (requests.ConnectionError, ValueError) as e: + self.unexpectedError(e) + + def _scan_file(self, filepath, force): + # Initialize scan in IRMA. + init = self._post_json(urlparse.urljoin(self.url, "/api/v1.1/scans")) + + # Post file for scanning. + files = { + "files": open(filepath, "rb"), + } + url = urlparse.urljoin( + self.url, "/api/v1.1/scans/%s/files" % init.get("id") + ) + self._post_json(url, files=files, ) + + # launch posted file scan + params = { + "force": force, + } + url = urlparse.urljoin( + self.url, "/api/v1.1/scans/%s/launch" % init.get("id") + ) + requests.post(url, json=params, verify=self.verify) + + result = None + + while result is None or result.get( + "status") != self.IRMA_FINISHED_STATUS or time.time() < self.time_start + self.timeout: + url = urlparse.urljoin( + self.url, "/api/v1.1/scans/%s" % init.get("id") + ) + result = self._request_json(url) + time.sleep(10) + + return + + def _get_results(self, sha256): + # Fetch list of scan IDs. + results = self._request_json( + urlparse.urljoin(self.url, "/api/v1.1/files/%s" % sha256) + ) + + if not results.get("items"): + return + + result_id = results["items"][-1]["result_id"] + return self._request_json( + urlparse.urljoin(self.url, "/api/v1.1/results/%s" % result_id) + ) + + + + def run(self): + Analyzer.run(self) + + if self.service == 'scan': + if self.data_type == 'file': + filename = self.getParam('attachment.name', 'noname.ext') + filepath = self.getParam('file', None, 'File is missing') + hashes = self.getParam('attachment.hashes', None) + if hashes is None: + hash = hashlib.sha256(open(filepath, 'r').read()).hexdigest() + else: + # find SHA256 hash + hash = next(h for h in hashes if len(h) == 64) + + results = self._get_results(hash) + + if not self.force and not self.scan and not results: + return {} + elif self.force or (not results and self.scan): + self._scan_file(filepath, self.force) + results = self._get_results(hash) or {} + + """ FIXME! could use a proper fix here + that probably needs changes on IRMA side aswell + -- + related to https://github.com/elastic/elasticsearch/issues/15377 + entropy value is sometimes 0 and sometimes like 0.10191042566270775 + other issue is that results type changes between string and object :/ + """ + for idx, result in enumerate(results["probe_results"]): + if result["name"] == "PE Static Analyzer": + results["probe_results"][idx]["results"] = None + + self.report(results) + + + else: + self.error('Invalid data type') + else: + self.error('Invalid service') + + +if __name__ == '__main__': + IRMA().run() diff --git a/analyzers/IRMA/reference.conf b/analyzers/IRMA/reference.conf new file mode 100644 index 000000000..0822eef0d --- /dev/null +++ b/analyzers/IRMA/reference.conf @@ -0,0 +1,4 @@ +# Add the following to cortex analyzer configuration +IRMA { + url="http://some-irma-url" + } diff --git a/analyzers/IRMA/requirements.txt b/analyzers/IRMA/requirements.txt new file mode 100644 index 000000000..2cac23c03 --- /dev/null +++ b/analyzers/IRMA/requirements.txt @@ -0,0 +1,2 @@ +requests +cortexutils diff --git a/analyzers/WhoisXMLAPI/reference.conf b/analyzers/WhoisXMLAPI/reference.conf new file mode 100644 index 000000000..9ae340770 --- /dev/null +++ b/analyzers/WhoisXMLAPI/reference.conf @@ -0,0 +1,6 @@ +# Add to analyser configuration + +WhoisXMLAPI { + username="someusername" + password="somepassword" +} diff --git a/analyzers/WhoisXMLAPI/requirements.txt b/analyzers/WhoisXMLAPI/requirements.txt new file mode 100644 index 000000000..8ad52a568 --- /dev/null +++ b/analyzers/WhoisXMLAPI/requirements.txt @@ -0,0 +1 @@ +cortexutils diff --git a/analyzers/WhoisXMLAPI/whoisxmlapi.json b/analyzers/WhoisXMLAPI/whoisxmlapi.json new file mode 100644 index 000000000..221c278c6 --- /dev/null +++ b/analyzers/WhoisXMLAPI/whoisxmlapi.json @@ -0,0 +1,13 @@ +{ + "name": "WhoisXMLAPI", + "description": "Query a domain against the WhoisXMLAPI", + "dataTypeList": ["domain", "ip", "fqdn", "ip"], + "version": "0.1", + "baseConfig": "WhoisXMLAPI", + "config": { + "check_tlp": true, + "max_tlp": 3, + "service": "query" + }, + "command": "WhoisXMLAPI/whoisxmlapi.py" +} diff --git a/analyzers/WhoisXMLAPI/whoisxmlapi.py b/analyzers/WhoisXMLAPI/whoisxmlapi.py new file mode 100644 index 000000000..76e914339 --- /dev/null +++ b/analyzers/WhoisXMLAPI/whoisxmlapi.py @@ -0,0 +1,41 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +####################################### +# Author: Unit777 # +####################################### + +import urllib2 +import json +from cortexutils.analyzer import Analyzer +import logging + +class WhoisXMLAPI(Analyzer): + + def __init__(self): + Analyzer.__init__(self) + self.username = self.getParam( + 'config.username', None, 'Username parameter is missing') + self.password = self.getParam( + 'config.password', None, 'Password parameter is missing') + self.service = self.getParam( + 'config.service', None, 'Service parameter is missing') + + def run(self): + if self.data_type == "domain" or self.data_type == "ip" or self.data_type == "url" or self.data_type == "fqdn": + whoisxmlapiURL = 'https://www.whoisxmlapi.com/whoisserver/WhoisService?domainName=' + self.getData() + '&username=' + self.username + '&password=' + self.password + '&outputFormat=' + "JSON" + #TODO: Add proxy capability + result = json.loads(urllib2.urlopen(whoisxmlapiURL).read().decode('utf8')) + if 'audit' in result: + if 'createdDate' in result['audit']: + if '$' in result['audit']['createdDate']: + result['audit']['createdDate'] = js['audit']['createdDate']['$'] + if 'updatedDate' in result['audit']: + if '$' in result['audit']['updatedDate']: + result['audit']['updatedDate'] = js['audit']['updatedDate']['$'] + + self.report({'whoisxmlapi': result}) + + +if __name__ == '__main__': + WhoisXMLAPI().run() diff --git a/thehive-templates/IRMA/long.html b/thehive-templates/IRMA/long.html new file mode 100644 index 000000000..216cfbb15 --- /dev/null +++ b/thehive-templates/IRMA/long.html @@ -0,0 +1,25 @@ +
| Scanner | +Detected | +Result | +
|---|---|---|
| + {{probe.name}} + | ++ + | +{{probe.results}} | + +