mirror of
https://github.com/Security-Onion-Solutions/securityonion.git
synced 2026-04-25 22:17:49 +02:00
echotrail api no longer available
This commit is contained in:
@@ -1,24 +0,0 @@
|
||||
# EchoTrail
|
||||
|
||||
|
||||
## Description
|
||||
Submit a filename, hash, commandline to EchoTrail for analysis
|
||||
|
||||
## Configuration Requirements
|
||||
|
||||
In SOC, navigate to `Administration`, toggle `Show all configurable settings, including advanced settings.`, and navigate to `sensoroni` -> `analyzers` -> `echotrail`.
|
||||

|
||||
|
||||
|
||||
The following configuration options are available for:
|
||||
|
||||
``api_key`` - API key used for communication with the Echotrail API (Required)
|
||||
|
||||
This value should be set in the ``sensoroni`` pillar, like so:
|
||||
|
||||
```
|
||||
sensoroni:
|
||||
analyzers:
|
||||
echotrail:
|
||||
api_key: $yourapikey
|
||||
```
|
||||
@@ -1,10 +0,0 @@
|
||||
{
|
||||
"name": "Echotrail",
|
||||
"version": "0.1",
|
||||
"author": "Security Onion Solutions",
|
||||
"description": "This analyzer queries Echotrail to see if a related filename, hash, or commandline is considered malicious.",
|
||||
"supportedTypes" : ["filename","hash","commandline"],
|
||||
"baseUrl": "https://api.echotrail.io/insights/"
|
||||
}
|
||||
|
||||
|
||||
@@ -1,67 +0,0 @@
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
import requests
|
||||
import helpers
|
||||
import argparse
|
||||
|
||||
|
||||
# for test usage:
|
||||
# python3 echotrail.py '{"artifactType":"hash", "value":"438b6ccd84f4dd32d9684ed7d58fd7d1e5a75fe3f3d12ab6c788e6bb0ffad5e7"}'
|
||||
# You will need to provide an API key in the .yaml file.
|
||||
def checkConfigRequirements(conf):
|
||||
if not conf['api_key']:
|
||||
sys.exit(126)
|
||||
else:
|
||||
return True
|
||||
|
||||
|
||||
def sendReq(conf, observ_value):
|
||||
# send a get requests using a user-provided API key and the API url
|
||||
url = conf['base_url'] + observ_value
|
||||
headers = {'x-api-key': conf['api_key']}
|
||||
response = requests.request('GET', url=url, headers=headers)
|
||||
return response.json()
|
||||
|
||||
|
||||
def prepareResults(raw):
|
||||
# checking for the 'filenames' key alone does
|
||||
# not work when querying by filename.
|
||||
# So, we can account for a hash query, a filename query,
|
||||
# and anything else with these if statements.
|
||||
if 'filenames' in raw.keys():
|
||||
summary = raw['filenames'][0][0]
|
||||
elif 'tags' in raw.keys():
|
||||
summary = raw['tags'][0][0]
|
||||
else:
|
||||
summary = 'inconclusive'
|
||||
status = 'info'
|
||||
return {'response': raw, 'summary': summary, 'status': status}
|
||||
|
||||
|
||||
def analyze(conf, input):
|
||||
# put all of our methods together and return a properly formatted output.
|
||||
checkConfigRequirements(conf)
|
||||
meta = helpers.loadMetadata(__file__)
|
||||
data = helpers.parseArtifact(input)
|
||||
helpers.checkSupportedType(meta, data['artifactType'])
|
||||
response = sendReq(conf, data['value'])
|
||||
return prepareResults(response)
|
||||
|
||||
|
||||
def main():
|
||||
dir = os.path.dirname(os.path.realpath(__file__))
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Search Echotrail for a given artifact')
|
||||
parser.add_argument(
|
||||
'artifact', help='the artifact represented in JSON format')
|
||||
parser.add_argument('-c', '--config', metavar='CONFIG_FILE', default=dir + '/echotrail.yaml',
|
||||
help='optional config file to use instead of the default config file')
|
||||
args = parser.parse_args()
|
||||
if args.artifact:
|
||||
results = analyze(helpers.loadConfig(args.config), args.artifact)
|
||||
print(json.dumps(results))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -1,3 +0,0 @@
|
||||
base_url: "{{ salt['pillar.get']('sensoroni:analyzers:echotrail:base_url', 'https://api.echotrail.io/insights/') }}"
|
||||
api_key: "{{ salt['pillar.get']('sensoroni:analyzers:echotrail:api_key', '') }}"
|
||||
|
||||
@@ -1,78 +0,0 @@
|
||||
from io import StringIO
|
||||
import sys
|
||||
from unittest.mock import patch, MagicMock
|
||||
import unittest
|
||||
import echotrail
|
||||
|
||||
|
||||
class TestEchoTrailMethods(unittest.TestCase):
|
||||
def test_main_success(self):
|
||||
with patch('sys.stdout', new=StringIO()) as mock_cmd:
|
||||
with patch('echotrail.analyze', new=MagicMock(return_value={'test': 'val'})) as mock:
|
||||
sys.argv = ["test", "test"]
|
||||
echotrail.main()
|
||||
expected = '{"test": "val"}\n'
|
||||
self.assertEqual(mock_cmd.getvalue(), expected)
|
||||
mock.assert_called_once()
|
||||
|
||||
def test_main_missing_input(self):
|
||||
with patch('sys.exit', new=MagicMock()) as sysmock:
|
||||
with patch('sys.stderr', new=StringIO()) as mock_stderr:
|
||||
sys.argv = ["cmd"]
|
||||
echotrail.main()
|
||||
self.assertEqual(mock_stderr.getvalue(), "usage: cmd [-h] [-c CONFIG_FILE] artifact\ncmd: error: the following arguments are required: artifact\n")
|
||||
sysmock.assert_called_once()
|
||||
|
||||
def test_checkConfigRequirements(self):
|
||||
conf = {'base_url': 'https://www.randurl.xyz/', 'api_key': ''}
|
||||
with self.assertRaises(SystemExit) as cm:
|
||||
echotrail.checkConfigRequirements(conf)
|
||||
self.assertEqual(cm.exception.code, 126)
|
||||
|
||||
def test_sendReq(self):
|
||||
with patch('requests.request', new=MagicMock(return_value=MagicMock())) as mock:
|
||||
response = echotrail.sendReq(conf={'base_url': 'https://www.randurl.xyz/', 'api_key': 'randkey'}, observ_value='example_data')
|
||||
self.assertIsNotNone(response)
|
||||
mock.assert_called_once()
|
||||
|
||||
def test_prepareResults_noinput(self):
|
||||
raw = {}
|
||||
sim_results = {'response': raw,
|
||||
'status': 'info', 'summary': 'inconclusive'}
|
||||
results = echotrail.prepareResults(raw)
|
||||
self.assertEqual(results, sim_results)
|
||||
|
||||
def test_prepareResults_none(self):
|
||||
raw = {'query_status': 'no_result'}
|
||||
sim_results = {'response': raw,
|
||||
'status': 'info', 'summary': 'inconclusive'}
|
||||
results = echotrail.prepareResults(raw)
|
||||
self.assertEqual(results, sim_results)
|
||||
|
||||
def test_prepareResults_filenames(self):
|
||||
raw = {'filenames': [["abc.exe", "def.exe"], ["abc.exe", "def.exe"]]}
|
||||
sim_results = {'response': raw,
|
||||
'status': 'info', 'summary': 'abc.exe'}
|
||||
results = echotrail.prepareResults(raw)
|
||||
self.assertEqual(results, sim_results)
|
||||
|
||||
def test_prepareResults_tags(self):
|
||||
raw = {'tags': [["tag1", "tag2"], ["tag1", "tag2"]]}
|
||||
sim_results = {'response': raw,
|
||||
'status': 'info', 'summary': 'tag1'}
|
||||
results = echotrail.prepareResults(raw)
|
||||
self.assertEqual(results, sim_results)
|
||||
|
||||
def test_analyze(self):
|
||||
sendReqOutput = {'threat': 'no_result'}
|
||||
input = '{"artifactType":"hash", "value":"1234"}'
|
||||
prepareResultOutput = {'response': '',
|
||||
'summary': 'inconclusive', 'status': 'info'}
|
||||
conf = {"api_key": "xyz"}
|
||||
|
||||
with patch('echotrail.sendReq', new=MagicMock(return_value=sendReqOutput)) as mock:
|
||||
with patch('echotrail.prepareResults', new=MagicMock(return_value=prepareResultOutput)) as mock2:
|
||||
results = echotrail.analyze(conf, input)
|
||||
self.assertEqual(results["summary"], "inconclusive")
|
||||
mock2.assert_called_once()
|
||||
mock.assert_called_once()
|
||||
@@ -1,2 +0,0 @@
|
||||
requests>=2.31.0
|
||||
pyyaml>=6.0
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Reference in New Issue
Block a user