Release catalyst

This commit is contained in:
Jonas Plum
2021-12-13 00:39:15 +01:00
commit 15cf0ebd49
339 changed files with 111677 additions and 0 deletions

View File

@@ -0,0 +1,25 @@
#!/usr/bin/env python
import subprocess
import sys
subprocess.call(
[sys.executable, "-m", "pip", "install", "requests"],
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL,
)
import json
import requests
def run(msg):
if "ticket" in msg["context"]:
headers = {"PRIVATE-TOKEN": msg["secrets"]["catalyst_apikey"]}
url = "%s/tickets/%d/comments" % (msg["secrets"]["catalyst_apiurl"], msg["context"]["ticket"]["id"])
data = {'message': msg["payload"]["default"], 'creator': 'automation'}
requests.post(url, json=data, headers=headers).json()
return {"done": True}
print(json.dumps(run(json.loads(sys.argv[1]))))

View File

@@ -0,0 +1,13 @@
#!/usr/bin/env python
import sys
import json
import hashlib
def run(msg):
sha1 = hashlib.sha1(msg['payload']['default'].encode('utf-8'))
return {"hash": sha1.hexdigest()}
print(json.dumps(run(json.loads(sys.argv[1]))))

View File

@@ -0,0 +1,630 @@
#!/usr/bin/env python
import subprocess
import sys
import json
from datetime import datetime
import io
subprocess.check_call(
[sys.executable, "-m", "pip", "install", "thehive4py", "requests", "minio"],
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL,
)
defaultschema = {
"definitions": {},
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://example.com/object1618746510.json",
"title": "Default",
"type": "object",
"required": [
"severity",
"description",
"summary",
"tlp",
"pap"
],
"properties": {
"severity": {
"$id": "#root/severity",
"title": "Severity",
"type": "string",
"default": "Medium",
"x-cols": 6,
"x-class": "pr-2",
"x-display": "icon",
"x-itemIcon": "icon",
"oneOf": [
{
"const": "Unknown",
"title": "Unknown",
"icon": "mdi-help"
},
{
"const": "Low",
"title": "Low",
"icon": "mdi-chevron-up"
},
{
"const": "Medium",
"title": "Medium",
"icon": "mdi-chevron-double-up"
},
{
"const": "High",
"title": "High",
"icon": "mdi-chevron-triple-up"
},
{
"const": "Very High",
"title": "Very High",
"icon": "mdi-exclamation"
}
]
},
"flag": {
"title": "Flag",
"type": "boolean",
"x-cols": 6,
},
"tlp": {
"$id": "#root/tlp",
"title": "TLP",
"type": "string",
"x-cols": 6,
"x-class": "pr-2",
"x-display": "icon",
"x-itemIcon": "icon",
"oneOf": [
{
"const": "White",
"title": "White",
"icon": "mdi-alpha-w"
},
{
"const": "Green",
"title": "Green",
"icon": "mdi-alpha-g"
},
{
"const": "Amber",
"title": "Amber",
"icon": "mdi-alpha-a"
},
{
"const": "Red",
"title": "Red",
"icon": "mdi-alpha-r"
}
]
},
"pap": {
"$id": "#root/pap",
"title": "PAP",
"type": "string",
"x-cols": 6,
"x-class": "pr-2",
"x-display": "icon",
"x-itemIcon": "icon",
"oneOf": [
{
"const": "White",
"title": "White",
"icon": "mdi-alpha-w"
},
{
"const": "Green",
"title": "Green",
"icon": "mdi-alpha-g"
},
{
"const": "Amber",
"title": "Amber",
"icon": "mdi-alpha-a"
},
{
"const": "Red",
"title": "Red",
"icon": "mdi-alpha-r"
}
]
},
"tags": {
"$id": "#root/tags",
"title": "Tags",
"type": "array",
"items": {
"type": "string"
}
},
"description": {
"$id": "#root/description",
"title": "Description",
"type": "string",
"x-display": "textarea",
"x-class": "pr-2"
},
"resolutionStatus": {
"$id": "#root/resolutionStatus",
"title": "Resolution Status",
"type": "string",
"x-cols": 6,
"x-class": "pr-2",
},
"endDate": {
"$id": "#root/endDate",
"title": "End Data",
"type": "string",
"format": "date-time",
"x-cols": 6,
"x-class": "pr-2",
},
"summary": {
"$id": "#root/summary",
"title": "Summary",
"type": "string",
"x-display": "textarea",
"x-class": "pr-2"
}
}
}
defaultalertschema = {
"definitions": {},
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://example.com/object1618746510.json",
"title": "Default",
"type": "object",
"required": [
"severity",
"description",
"summary",
"tlp",
"pap"
],
"properties": {
"severity": {
"$id": "#root/severity",
"title": "Severity",
"type": "string",
"default": "Medium",
"x-cols": 6,
"x-class": "pr-2",
"x-display": "icon",
"x-itemIcon": "icon",
"oneOf": [
{
"const": "Unknown",
"title": "Unknown",
"icon": "mdi-help"
},
{
"const": "Low",
"title": "Low",
"icon": "mdi-chevron-up"
},
{
"const": "Medium",
"title": "Medium",
"icon": "mdi-chevron-double-up"
},
{
"const": "High",
"title": "High",
"icon": "mdi-chevron-triple-up"
},
{
"const": "Very High",
"title": "Very High",
"icon": "mdi-exclamation"
}
]
},
"tlp": {
"$id": "#root/tlp",
"title": "TLP",
"type": "string",
"x-cols": 6,
"x-class": "pr-2",
"x-display": "icon",
"x-itemIcon": "icon",
"oneOf": [
{
"const": "White",
"title": "White",
"icon": "mdi-alpha-w"
},
{
"const": "Green",
"title": "Green",
"icon": "mdi-alpha-g"
},
{
"const": "Amber",
"title": "Amber",
"icon": "mdi-alpha-a"
},
{
"const": "Red",
"title": "Red",
"icon": "mdi-alpha-r"
}
]
},
"source": {
"$id": "#root/source",
"title": "Source",
"type": "string",
"x-cols": 4,
"x-class": "pr-2",
},
"sourceRef": {
"$id": "#root/sourceRef",
"title": "Source Ref",
"type": "string",
"x-cols": 4,
"x-class": "pr-2",
},
"type": {
"$id": "#root/type",
"title": "Type",
"type": "string",
"x-cols": 4,
"x-class": "pr-2",
},
"description": {
"$id": "#root/description",
"title": "Description",
"type": "string",
"x-display": "textarea",
"x-class": "pr-2"
}
}
}
class schema:
def __init__(self):
self.schema = defaultschema
def add_string(self, title):
self.schema["properties"][title] = { "type": "string", "x-cols": 6, "x-class": "pr-2" }
def add_boolean(self, title):
self.schema["properties"][title] = { "type": "boolean", "x-cols": 6, "x-class": "pr-2" }
def add_date(self, title):
self.schema["properties"][title] = { "type": "string", "format": "date-time", "x-cols": 6, "x-class": "pr-2" }
def add_integer(self, title):
self.schema["properties"][title] = { "type": "integer", "x-cols": 6, "x-class": "pr-2" }
def add_float(self, title):
self.schema["properties"][title] = { "type": "number", "x-cols": 6, "x-class": "pr-2" }
class alertschema:
def __init__(self):
self.schema = defaultalertschema
def maptime(hivetime):
if hivetime is None:
return None
return datetime.fromtimestamp(hivetime/1000).isoformat() + "Z"
def mapstatus(hivestatus):
if hivestatus == "Open" or hivestatus == "New":
return "open"
return "closed"
def maptlp(hivetlp):
if hivetlp == 0:
return "White"
if hivetlp == 1:
return "Green"
if hivetlp == 2:
return "Amber"
if hivetlp == 3:
return "Red"
return "White"
def mapseverity(hiveseverity):
if hiveseverity == 1:
return "Low"
if hiveseverity == 2:
return "Medium"
if hiveseverity == 3:
return "High"
if hiveseverity == 4:
return "Very High"
return "Unknown"
# {
# "_id": "~16416",
# "id": "~16416",
# "createdBy": "jonas@thehive.local",
# "updatedBy": "jonas@thehive.local",
# "createdAt": 1638704013583,
# "updatedAt": 1638704061151,
# "_type": "case",
# "caseId": 1,
# "title": "My Test 1",
# "description": "My Testcase",
# "severity": 2,
# "startDate": 1638703980000,
# "endDate": null,
# "impactStatus": null,
# "resolutionStatus": null,
# "tags": [],
# "flag": false,
# "tlp": 2,
# "pap": 2,
# "status": "Open",
# "summary": null,
# "owner": "jonas@thehive.local",
# "customFields": {},
# "stats": {},
# "permissions": [ "manageShare", "manageAnalyse", "manageTask", "manageCaseTemplate", "manageCase", "manageUser", "manageProcedure", "managePage", "manageObservable", "manageTag", "manageConfig", "manageAlert", "accessTheHiveFS", "manageAction" ]
# }
def mapcase(hivecase, url, keep_ids):
s = schema()
details = {}
for name, data in hivecase["customFields"].items():
if "string" in data and data["string"] is not None:
s.add_string(name)
details[name] = data["string"]
if "boolean" in data and data["boolean"] is not None:
s.add_boolean(name)
details[name] = data["boolean"]
if "date" in data and data["date"] is not None:
s.add_date(name)
details[name] = maptime(data["date"])
if "integer" in data and data["integer"] is not None:
s.add_integer(name)
details[name] = data["integer"]
if "float" in data and data["float"] is not None:
s.add_float(name)
details[name] = data["float"]
case = {}
if keep_ids:
case["id"] = hivecase["caseId"]
return {
"name": hivecase["title"],
"type": "incident",
"status": mapstatus(hivecase["status"]),
"owner": hivecase["owner"],
# "write": hivecase["write"],
# "read": hivecase["read"],
"schema": json.dumps(s.schema),
"details": {
"tlp": maptlp(hivecase["tlp"]),
"pap": maptlp(hivecase["pap"]),
"severity": mapseverity(hivecase["severity"]),
"description": hivecase["description"],
"summary": hivecase["summary"],
"tags": hivecase["tags"],
"endDate": maptime(hivecase["endDate"]),
"resolutionStatus": hivecase["resolutionStatus"],
"flag": hivecase["flag"],
} | details,
"references": [
{ "name": "TheHive #%d" % hivecase["caseId"], "href": "%s/index.html#!/case/~%s/details" % (url, hivecase["id"]) }
],
#
# "playbooks": hivecase["playbooks"],
#
"files": [],
"comments": [],
# creator, created, message
#
"artifacts": [],
# name, type, status, enrichment
# name, data
"created": maptime(hivecase["createdAt"]),
"modified": maptime(hivecase["updatedAt"]),
} | case
# {
# "_id": "ce2c00f17132359cb3c50dfbb1901810",
# "_type": "alert",
# "artifacts": [],
# "createdAt": 1495012062014,
# "createdBy": "myuser",
# "date": 1495012062016,
# "description": "N/A",
# "follow": true,
# "id": "ce2c00f17132359cb3c50dfbb1901810",
# "lastSyncDate": 1495012062016,
# "severity": 2,
# "source": "instance1",
# "sourceRef": "alert-ref",
# "status": "New",
# "title": "New Alert",
# "tlp": 2,
# "type": "external",
# "user": "myuser"
# }
def mapalert(hivealert, url):
s = alertschema()
details = {}
return {
"name": hivealert["title"],
"type": "alert",
"status": mapstatus(hivealert["status"]),
"owner": hivealert["user"],
"schema": json.dumps(s.schema),
"details": {
"tlp": maptlp(hivealert["tlp"]),
"severity": mapseverity(hivealert["severity"]),
"description": hivealert["description"],
"source": hivealert["source"],
"sourceRef": hivealert["sourceRef"],
"type": hivealert["type"],
} | details,
"references": [
{ "name": "TheHive Alerts", "href": "%s/index.html#!/alert/list" % url }
],
"files": [],
"comments": [],
"artifacts": [],
"created": maptime(hivealert["createdAt"]),
"modified": maptime(hivealert["lastSyncDate"]),
}
# {
# "_id": "~41152",
# "id": "~41152",
# "createdBy": "jonas@thehive.local",
# "createdAt": 1638723814523,
# "_type": "case_artifact",
# "dataType": "ip",
# "data": "2.2.2.2",
# "startDate": 1638723814523,
# "tlp": 2,
# "tags": [],
# "ioc": false,
# "sighted": false,
# "message": ".",
# "reports": {},
# "stats": {},
# "ignoreSimilarity": false
# }
def mapobservable(hiveobservable):
status = "unknown"
if hiveobservable["ioc"]:
status = "malicious"
return {
"name": hiveobservable["data"],
"type": hiveobservable["dataType"],
"status": status,
}
# {
# "id": "~12296",
# "_id": "~12296",
# "createdBy": "jonas@thehive.local",
# "createdAt": 1638704029800,
# "_type": "case_task",
# "title": "Start",
# "group": "MyTaskGroup1",
# "owner": "jonas@thehive.local",
# "status": "InProgress",
# "flag": false,
# "startDate": 1638704115667,
# "order": 0
# }
# {
# "_id": "~24656",
# "id": "~24656",
# "createdBy": "jonas@thehive.local",
# "createdAt": 1638729992590,
# "_type": "case_task_log",
# "message": "asd",
# "startDate": 1638729992590,
# "attachment": {
# "name": "Chemistry Vector.eps",
# "hashes": [
# "adf2d4cd72f4141fe7f8eb4af035596415a29c048d3039be6449008f291258e9",
# "180f66a6d22b1f09ed198afd814f701e42440e7c",
# "b28ae347371df003b76cbb8c6199c97e"
# ],
# "size": 3421842,
# "contentType": "application/postscript",
# "id": "adf2d4cd72f4141fe7f8eb4af035596415a29c048d3039be6449008f291258e9"
# },
# "status": "Ok",
# "owner": "jonas@thehive.local"
# }
def maptasklog(hivetask, hivetasklog):
message = "**" + hivetask["group"] + ": " + hivetask["title"] + "** (" + hivetask["status"] + ")\n\n"
message += hivetasklog["message"]
if 'attachment' in hivetasklog:
message += "\n\n*Attachment*: " + hivetasklog['attachment']["name"]
return {
"creator": hivetasklog["createdBy"],
"created": maptime(hivetasklog["createdAt"]),
"message": message,
}
def run(msg):
skip_files = msg["payload"]["skip_files"]
keep_ids = msg["payload"]["keep_ids"]
from thehive4py.api import TheHiveApi
import requests
from minio import Minio
headers = {"PRIVATE-TOKEN": msg["secrets"]["catalyst_apikey"]}
# minioclient = Minio("try.catalyst-soar.com:9000", access_key="minio", secret_key="password")
if not skip_files:
minioclient = Minio(
msg["secrets"]["minio_host"],
access_key=msg["secrets"]["minio_access_key"],
secret_key=msg["secrets"]["minio_secret_key"])
# url = "http://localhost:9000"
url = msg["payload"]["thehiveurl"]
# api = TheHiveApi(url, "dtUCnzY4h291GIFHJKW/Z2I2SgjTRQqo")
api = TheHiveApi(url, msg["payload"]["thehivekey"])
print("find alerts", file=sys.stderr)
alerts = []
resp = api.find_alerts(query={}, sort=['-createdAt'], range='all')
resp.raise_for_status()
for alert in resp.json():
alerts.append(mapalert(alert, url))
if alerts:
print("create %s alerts" % len(alerts), file=sys.stderr)
response = requests.post(msg["secrets"]["catalyst_apiurl"] + "/tickets/batch", json=alerts, headers=headers)
response.raise_for_status()
print("find incidents", file=sys.stderr)
incidents = []
resp = api.find_cases(query={}, sort=['-createdAt'], range='all')
resp.raise_for_status()
for case in resp.json():
incident = mapcase(case, url, keep_ids)
for observable in api.get_case_observables(case["id"]).json():
incident["artifacts"].append(mapobservable(observable))
for task in api.get_case_tasks(case["id"]).json():
for log in api.get_task_logs(task["id"]).json():
incident["comments"].append(maptasklog(task, log))
if 'attachment' in log and not skip_files:
incident["files"].append({ "key": log['attachment']["id"], "name": log['attachment']["name"] })
bucket_name = "catalyst-%d" % incident["id"]
if not minioclient.bucket_exists(bucket_name):
minioclient.make_bucket(bucket_name)
response = api.download_attachment(log["attachment"]["id"])
data = io.BytesIO(response.content)
minioclient.put_object(bucket_name, log["attachment"]["id"], data, length=-1, part_size=10*1024*1024)
incidents.append(incident)
if incidents:
if keep_ids:
print("delete incidents", file=sys.stderr)
for incident in incidents:
requests.delete(msg["secrets"]["catalyst_apiurl"] + "/tickets/%d" % incident["id"], headers=headers)
print("create %d incidents" % len(incidents), file=sys.stderr)
response = requests.post(msg["secrets"]["catalyst_apiurl"] + "/tickets/batch", json=incidents, headers=headers)
response.raise_for_status()
return {"done": True}
print(json.dumps(run(json.loads(sys.argv[1]))))

View File

@@ -0,0 +1,22 @@
#!/usr/bin/env python
import subprocess
import sys
subprocess.call(
[sys.executable, "-m", "pip", "install", "requests"],
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL,
)
import json
import requests
def run(msg):
api_key = msg['secrets']['vt_api_key'].encode('utf-8')
resource = msg['payload']['default'].encode('utf-8')
params = {'apikey': api_key, 'resource': resource}
return requests.get("https://www.virustotal.com/vtapi/v2/file/report", params=params).json()
print(json.dumps(run(json.loads(sys.argv[1]))))

View File

@@ -0,0 +1,27 @@
package migrations
import _ "embed"
//go:embed templates/default.json
var DefaultTemplateSchema string
//go:embed automations/hash.sha1.py
var SHA1HashAutomation string
//go:embed automations/vt.hash.py
var VTHashAutomation string
//go:embed automations/thehive.py
var TheHiveAutomation string
//go:embed automations/comment.py
var CommentAutomation string
//go:embed playbooks/malware.yml
var MalwarePlaybook string
//go:embed playbooks/phishing.yml
var PhishingPlaybook string
//go:embed playbooks/simple.yaml
var SimplePlaybook string

View File

@@ -0,0 +1,217 @@
package migrations
import (
"context"
"fmt"
"github.com/arangodb/go-driver"
"github.com/SecurityBrewery/catalyst/database/busdb"
"github.com/SecurityBrewery/catalyst/generated/models"
"github.com/SecurityBrewery/catalyst/pointer"
)
const MigrationCollection string = "migrations"
type Migration interface {
MID() string
Migrate(ctx context.Context, driver driver.Database) error
}
func generateMigrations() ([]Migration, error) {
// content here should never change
return []Migration{
&createCollection{ID: "create-log-collection", Name: "logs", DataType: "log", Schema: `{"properties":{"created":{"format":"date-time","type":"string"},"creator":{"type":"string"},"message":{"type":"string"},"reference":{"type":"string"}},"required":["created","creator","message","reference"],"type":"object"}`},
&createCollection{ID: "create-ticket-collection", Name: "tickets", DataType: "ticket", Schema: `{"properties":{"artifacts":{"items":{"properties":{"enrichments":{"additionalProperties":{"properties":{"created":{"format":"date-time","type":"string"},"data":{"example":{"hash":"b7a067a742c20d07a7456646de89bc2d408a1153"},"properties":{},"type":"object"},"name":{"example":"hash.sha1","type":"string"}},"required":["created","data","name"],"type":"object"},"type":"object"},"name":{"example":"2.2.2.2","type":"string"},"status":{"example":"Unknown","type":"string"},"type":{"type":"string"}},"required":["name"],"type":"object"},"type":"array"},"comments":{"items":{"properties":{"created":{"format":"date-time","type":"string"},"creator":{"type":"string"},"message":{"type":"string"}},"required":["created","creator","message"],"type":"object"},"type":"array"},"created":{"format":"date-time","type":"string"},"details":{"example":{"description":"my little incident"},"properties":{},"type":"object"},"files":{"items":{"properties":{"key":{"example":"myfile","type":"string"},"name":{"example":"notes.docx","type":"string"}},"required":["key","name"],"type":"object"},"type":"array"},"modified":{"format":"date-time","type":"string"},"name":{"example":"WannyCry","type":"string"},"owner":{"example":"bob","type":"string"},"playbooks":{"additionalProperties":{"properties":{"name":{"example":"Phishing","type":"string"},"tasks":{"additionalProperties":{"properties":{"automation":{"type":"string"},"closed":{"format":"date-time","type":"string"},"created":{"format":"date-time","type":"string"},"data":{"properties":{},"type":"object"},"done":{"type":"boolean"},"join":{"example":false,"type":"boolean"},"payload":{"additionalProperties":{"type":"string"},"type":"object"},"name":{"example":"Inform user","type":"string"},"next":{"additionalProperties":{"type":"string"},"type":"object"},"owner":{"type":"string"},"schema":{"properties":{},"type":"object"},"type":{"enum":["task","input","automation"],"example":"task","type":"string"}},"required":["created","done","name","type"],"type":"object"},"type":"object"}},"required":["name","tasks"],"type":"object"},"type":"object"},"read":{"example":["bob"],"items":{"type":"string"},"type":"array"},"references":{"items":{"properties":{"href":{"example":"https://cve.mitre.org/cgi-bin/cvename.cgi?name=cve-2017-0144","type":"string"},"name":{"example":"CVE-2017-0144","type":"string"}},"required":["href","name"],"type":"object"},"type":"array"},"schema":{"example":"{}","type":"string"},"status":{"example":"open","type":"string"},"type":{"example":"incident","type":"string"},"write":{"example":["alice"],"items":{"type":"string"},"type":"array"}},"required":["created","modified","name","schema","status","type"],"type":"object"}`},
&createCollection{ID: "create-template-collection", Name: "templates", DataType: "template", Schema: `{"properties":{"name":{"type":"string"},"schema":{"type":"string"}},"required":["name","schema"],"type":"object"}`},
&createCollection{ID: "create-playbook-collection", Name: "playbooks", DataType: "playbook", Schema: `{"properties":{"name":{"type":"string"},"yaml":{"type":"string"}},"required":["name","yaml"],"type":"object"}`},
&createCollection{ID: "create-automation-collection", Name: "automations", DataType: "automation", Schema: `{"properties":{"image":{"type":"string"},"script":{"type":"string"}},"required":["image","script"],"type":"object"}`},
&createCollection{ID: "create-userdata-collection", Name: "userdata", DataType: "userdata", Schema: `{"properties":{"email":{"type":"string"},"image":{"type":"string"},"name":{"type":"string"},"timeformat":{"title":"Time Format (https://moment.github.io/luxon/docs/manual/formatting.html#table-of-tokens)","type":"string"}},"type":"object"}`},
&createCollection{ID: "create-tickettype-collection", Name: "tickettypes", DataType: "tickettype", Schema: `{"properties":{"default_groups":{"items":{"type":"string"},"type":"array"},"default_playbooks":{"items":{"type":"string"},"type":"array"},"default_template":{"type":"string"},"icon":{"type":"string"},"name":{"type":"string"}},"required":["default_playbooks","default_template","icon","name"],"type":"object"}`},
&createCollection{ID: "create-user-collection", Name: "users", DataType: "user", Schema: `{"properties":{"apikey":{"type":"boolean"},"blocked":{"type":"boolean"},"roles":{"items":{"type":"string"},"type":"array"},"sha256":{"type":"string"}},"required":["apikey","blocked","roles"],"type":"object"}`},
&createGraph{ID: "create-ticket-graph", Name: "Graph", EdgeDefinitions: []driver.EdgeDefinition{{Collection: "related", From: []string{"tickets"}, To: []string{"tickets"}}}},
&createDocument{ID: "create-template-default", Collection: "templates", Document: &busdb.Keyed{Key: "default", Doc: models.TicketTemplate{Schema: DefaultTemplateSchema, Name: "Default"}}},
&createDocument{ID: "create-automation-vt.hash", Collection: "automations", Document: &busdb.Keyed{Key: "vt.hash", Doc: models.Automation{Image: "docker.io/python:3", Script: VTHashAutomation}}},
&createDocument{ID: "create-automation-comment", Collection: "automations", Document: &busdb.Keyed{Key: "comment", Doc: models.Automation{Image: "docker.io/python:3", Script: CommentAutomation}}},
&createDocument{ID: "create-automation-thehive", Collection: "automations", Document: &busdb.Keyed{Key: "thehive", Doc: models.Automation{Image: "docker.io/python:3", Script: TheHiveAutomation}}},
&createDocument{ID: "create-automation-hash.sha1", Collection: "automations", Document: &busdb.Keyed{Key: "hash.sha1", Doc: models.Automation{Image: "docker.io/python:3", Script: SHA1HashAutomation}}},
&createDocument{ID: "create-playbook-malware", Collection: "playbooks", Document: &busdb.Keyed{Key: "malware", Doc: models.PlaybookTemplate{Name: "Malware", Yaml: MalwarePlaybook}}},
&createDocument{ID: "create-playbook-phishing", Collection: "playbooks", Document: &busdb.Keyed{Key: "phishing", Doc: models.PlaybookTemplate{Name: "Phishing", Yaml: PhishingPlaybook}}},
&createDocument{ID: "create-tickettype-alert", Collection: "tickettypes", Document: &busdb.Keyed{Key: "alert", Doc: models.TicketType{Name: "Alerts", Icon: "mdi-alert", DefaultTemplate: "default", DefaultPlaybooks: []string{}, DefaultGroups: nil}}},
&createDocument{ID: "create-tickettype-incident", Collection: "tickettypes", Document: &busdb.Keyed{Key: "incident", Doc: models.TicketType{Name: "Incidents", Icon: "mdi-radioactive", DefaultTemplate: "default", DefaultPlaybooks: []string{}, DefaultGroups: nil}}},
&createDocument{ID: "create-tickettype-investigation", Collection: "tickettypes", Document: &busdb.Keyed{Key: "investigation", Doc: models.TicketType{Name: "Forensic Investigations", Icon: "mdi-fingerprint", DefaultTemplate: "default", DefaultPlaybooks: []string{}, DefaultGroups: nil}}},
&createDocument{ID: "create-tickettype-hunt", Collection: "tickettypes", Document: &busdb.Keyed{Key: "hunt", Doc: models.TicketType{Name: "Threat Hunting", Icon: "mdi-target", DefaultTemplate: "default", DefaultPlaybooks: []string{}, DefaultGroups: nil}}},
&updateSchema{ID: "update-automation-collection-1", Name: "automations", DataType: "automation", Schema: `{"properties":{"image":{"type":"string"},"script":{"type":"string"}},"required":["image","script"],"type":"object"}`},
&updateDocument{ID: "update-automation-vt.hash-1", Collection: "automations", Key: "vt.hash", Document: models.Automation{Image: "docker.io/python:3", Script: VTHashAutomation, Schema: pointer.String(`{"title":"Input","type":"object","properties":{"default":{"type":"string","title":"Value"}},"required":["default"]}`), Type: []string{"global", "artifact", "playbook"}}},
&updateDocument{ID: "update-automation-comment-1", Collection: "automations", Key: "comment", Document: models.Automation{Image: "docker.io/python:3", Script: CommentAutomation, Type: []string{"playbook"}}},
&updateDocument{ID: "update-automation-thehive-1", Collection: "automations", Key: "thehive", Document: models.Automation{Image: "docker.io/python:3", Script: TheHiveAutomation, Schema: pointer.String(`{"title":"TheHive credentials","type":"object","properties":{"thehiveurl":{"type":"string","title":"TheHive URL (e.g. 'https://thehive.example.org')"},"thehivekey":{"type":"string","title":"TheHive API Key"},"skip_files":{"type":"boolean", "default": true, "title":"Skip Files (much faster)"},"keep_ids":{"type":"boolean", "default": true, "title":"Keep IDs and overwrite existing IDs"}},"required":["thehiveurl", "thehivekey", "skip_files", "keep_ids"]}`), Type: []string{"global"}}},
&updateDocument{ID: "update-automation-hash.sha1-1", Collection: "automations", Key: "hash.sha1", Document: models.Automation{Image: "docker.io/python:3", Script: SHA1HashAutomation, Schema: pointer.String(`{"title":"Input","type":"object","properties":{"default":{"type":"string","title":"Value"}},"required":["default"]}`), Type: []string{"global", "artifact", "playbook"}}},
&createCollection{ID: "create-job-collection", Name: "jobs", DataType: "job", Schema: `{"properties":{"automation":{"type":"string"},"log":{"type":"string"},"payload":{},"origin":{"properties":{"artifact_origin":{"properties":{"artifact":{"type":"string"},"ticket_id":{"format":"int64","type":"integer"}},"required":["artifact","ticket_id"],"type":"object"},"task_origin":{"properties":{"playbook_id":{"type":"string"},"task_id":{"type":"string"},"ticket_id":{"format":"int64","type":"integer"}},"required":["playbook_id","task_id","ticket_id"],"type":"object"}},"type":"object"},"output":{"properties":{},"type":"object"},"running":{"type":"boolean"},"status":{"type":"string"}},"required":["automation","running","status"],"type":"object"}`},
}, nil
}
func loadSchema(dataType, jsonschema string) (*driver.CollectionSchemaOptions, error) {
ticketCollectionSchema := &driver.CollectionSchemaOptions{Level: driver.CollectionSchemaLevelStrict, Message: fmt.Sprintf("Validation of %s failed", dataType)}
err := ticketCollectionSchema.LoadRule([]byte(jsonschema))
return ticketCollectionSchema, err
}
type migration struct {
Key string `json:"_key"`
}
func PerformMigrations(ctx context.Context, db driver.Database) error {
collection, err := db.Collection(ctx, MigrationCollection)
if err != nil {
return err
}
migrations, err := generateMigrations()
if err != nil {
return fmt.Errorf("could not generate migrations: %w", err)
}
for _, m := range migrations {
migrationRan, err := collection.DocumentExists(ctx, m.MID())
if err != nil {
return err
}
if !migrationRan {
if err := m.Migrate(ctx, db); err != nil {
return fmt.Errorf("migration %s failed: %w", m.MID(), err)
}
if _, err := collection.CreateDocument(ctx, &migration{Key: m.MID()}); err != nil {
return fmt.Errorf("could not save %s migration document: %w", m.MID(), err)
}
}
}
return nil
}
type createCollection struct {
ID string
Name string
DataType string
Schema string
}
func (m *createCollection) MID() string {
return m.ID
}
func (m *createCollection) Migrate(ctx context.Context, db driver.Database) error {
schema, err := loadSchema(m.DataType, m.Schema)
if err != nil {
return err
}
_, err = db.CreateCollection(ctx, m.Name, &driver.CreateCollectionOptions{
Schema: schema,
})
return err
}
type updateSchema struct {
ID string
Name string
DataType string
Schema string
}
func (m *updateSchema) MID() string {
return m.ID
}
func (m *updateSchema) Migrate(ctx context.Context, db driver.Database) error {
schema, err := loadSchema(m.DataType, m.Schema)
if err != nil {
return err
}
col, err := db.Collection(ctx, m.Name)
if err != nil {
return err
}
err = col.SetProperties(ctx, driver.SetCollectionPropertiesOptions{
Schema: schema,
})
return err
}
type createGraph struct {
ID string
Name string
EdgeDefinitions []driver.EdgeDefinition
}
func (m *createGraph) MID() string {
return m.ID
}
func (m *createGraph) Migrate(ctx context.Context, db driver.Database) error {
_, err := db.CreateGraph(ctx, m.Name, &driver.CreateGraphOptions{
EdgeDefinitions: m.EdgeDefinitions,
})
return err
}
type createDocument struct {
ID string
Collection string
Document interface{}
}
func (m *createDocument) MID() string {
return m.ID
}
func (m *createDocument) Migrate(ctx context.Context, driver driver.Database) error {
collection, err := driver.Collection(ctx, m.Collection)
if err != nil {
return err
}
_, err = collection.CreateDocument(ctx, m.Document)
return err
}
type updateDocument struct {
ID string
Collection string
Key string
Document interface{}
}
func (m *updateDocument) MID() string {
return m.ID
}
func (m *updateDocument) Migrate(ctx context.Context, driver driver.Database) error {
collection, err := driver.Collection(ctx, m.Collection)
if err != nil {
return err
}
exists, err := collection.DocumentExists(ctx, m.Key)
if err != nil {
return err
}
if !exists {
_, err = collection.CreateDocument(ctx, m.Document)
return err
}
_, err = collection.ReplaceDocument(ctx, m.Key, m.Document)
return err
}

View File

@@ -0,0 +1,63 @@
name: Malware
tasks:
file-or-hash:
name: Do you have the file or the hash?
type: input
schema:
title: Malware
type: object
properties:
file:
type: string
title: "I have the"
enum: [ "File", "Hash" ]
next:
enter-hash: "file == 'Hash'"
upload: "file == 'File'"
enter-hash:
name: Please enter the hash
type: input
schema:
title: Malware
type: object
properties:
hash:
type: string
title: Please enter the hash value
minlength: 32
next:
virustotal: "hash != ''"
upload:
name: Upload the malware
type: input
schema:
title: Malware
type: object
properties:
malware:
type: object
x-display: file
title: Please upload the malware
next:
hash: "malware"
hash:
name: Hash the malware
type: automation
automation: hash.sha1
payload:
default: "playbook.tasks['upload'].data['malware']"
next:
virustotal:
virustotal:
name: Send hash to VirusTotal
type: automation
automation: vt.hash
args:
hash: "playbook.tasks['enter-hash'].data['hash'] || playbook.tasks['hash'].data['hash']"
# next:
# known-malware: "score > 5"
# sandbox: "score < 6" # unknown-malware

View File

@@ -0,0 +1,85 @@
name: Phishing
tasks:
board:
name: Board Involvement?
description: Is a board member involved?
type: input
schema:
properties:
boardInvolved:
default: false
title: A board member is involved.
type: boolean
required:
- boardInvolved
title: Board Involvement?
type: object
next:
escalate: "boardInvolved == true"
mail-available: "boardInvolved == false"
escalate:
name: Escalate to CISO
description: Please escalate the task to the CISO
type: task
mail-available:
name: Mail available
type: input
schema:
oneOf:
- properties:
mail:
title: Mail
type: string
x-display: textarea
schemaKey:
const: 'yes'
type: string
required:
- mail
title: 'Yes'
- properties:
schemaKey:
const: 'no'
type: string
title: 'No'
title: Mail available
type: object
next:
block-sender: "schemaKey == 'yes'"
extract-iocs: "schemaKey == 'yes'"
search-email-gateway: "schemaKey == 'no'"
search-email-gateway:
name: Search email gateway
description: Please search email-gateway for the phishing mail.
type: task
next:
extract-iocs:
block-sender:
name: Block sender
type: task
next:
extract-iocs:
extract-iocs:
name: Extract IOCs
description: Please insert the IOCs
type: input
schema:
properties:
iocs:
items:
type: string
title: IOCs
type: array
title: Extract IOCs
type: object
next:
block-iocs:
block-iocs:
name: Block IOCs
type: task

View File

@@ -0,0 +1,37 @@
name: Simple
tasks:
input:
name: Enter something to hash
type: input
schema:
title: Something
type: object
properties:
something:
type: string
title: Something
default: ""
next:
hash: "something != ''"
hash:
name: Hash the something
type: automation
automation: hash.sha1
payload:
default: "playbook.tasks['input'].data['something']"
next:
comment: "hash != ''"
comment:
name: Comment the hash
type: automation
automation: comment
payload:
default: "playbook.tasks['hash'].data['hash']"
next:
done: "done"
done:
name: You can close this case now
type: task

View File

@@ -0,0 +1,208 @@
{
"definitions": {},
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://example.com/object1618746510.json",
"title": "Advanced",
"type": "object",
"properties": {
"severity": {
"$id": "#root/severity",
"title": "Severity",
"type": "string",
"default": "Medium",
"x-cols": 6,
"x-class": "pr-2",
"x-display": "icon",
"x-itemIcon": "icon",
"oneOf": [
{
"const": "Low",
"title": "Low",
"icon": "mdi-chevron-up"
},
{
"const": "Medium",
"title": "Medium",
"icon": "mdi-chevron-double-up"
},
{
"const": "High",
"title": "High",
"icon": "mdi-chevron-triple-up"
}
]
},
"tlp": {
"$id": "#root/tlp",
"title": "TLP",
"type": "string",
"nx-enum": [
"White",
"Green",
"Amber",
"Red"
],
"x-cols": 6,
"x-class": "pr-2",
"x-display": "icon",
"x-itemIcon": "icon",
"oneOf": [
{
"const": "White",
"title": "White",
"icon": "mdi-alpha-w"
},
{
"const": "Green",
"title": "Green",
"icon": "mdi-alpha-g"
},
{
"const": "Amber",
"title": "Amber",
"icon": "mdi-alpha-a"
},
{
"const": "Red",
"title": "Red",
"icon": "mdi-alpha-r"
}
]
},
"description": {
"$id": "#root/description",
"title": "Description",
"type": "string",
"x-display": "textarea",
"x-class": "pr-2"
},
"type": {
"type": "object",
"title": "Select an incident type",
"oneOf": [
{
"title": "Malware",
"properties": {
"schemaKey": {
"type": "string",
"const": "malware"
},
"malware_type": {
"type": "string",
"title": "Malware Type",
"enum": ["Ransomware", "Worm", "Virus"]
}
}
},
{
"title": "Phishing",
"properties": {
"schemaKey": {
"type": "string",
"const": "phishing"
},
"phishing_type": {
"type": "string",
"title": "Phishing Type",
"enum": ["Normal", "Spear", "Whale"]
}
}
}
],
"x-cols": 12
},
"apt": {
"type": "boolean",
"x-display": "switch",
"title": "APT involved?",
"x-cols": 6
},
"apt-group": {
"type": "string",
"title": "Select APT",
"enum": ["Lazarus Group", "Equation Group", "Fancy Bear (APT 28)", "OceanLotus (APT 32)", "Other"],
"x-if": "apt",
"x-cols": 6
},
"tactics": {
"type": "array",
"title": "MITRE Att&ck",
"description": "This description is used as a help message.",
"items": {
"type": "object",
"oneOf": [
{
"title": "Reconnaissance",
"properties": {
"tactic": {
"type": "string",
"const": "reconnaissance",
"title": "Tactic",
"description": "The adversary is trying to gather information they can use to plan future operations."
},
"techniques": {
"type": "array",
"title": "Techniques",
"items": {
"type": "string",
"oneOf": [
{
"const": "T1595",
"title": "Active Scanning",
"description": "Adversaries may execute active reconnaissance scans to gather information that can be used during targeting. Active scans are those where the adversary probes victim infrastructure via network traffic, as opposed to other forms of reconnaissance that do not involve direct interaction."
},
{
"const": "T1592",
"title": "Gather Victim Host Information"
}
]
},
"minItems": 1,
"uniqueItems": true
}
}
},
{
"title": "Persistence",
"properties": {
"tactic": {
"type": "string",
"const": "persistence"
},
"techniques": {
"type": "string",
"title": "Techniques",
"oneOf": [
{
"const": "T1098",
"title": "Account Manipulation"
},
{
"const": "T1197",
"title": "BITS Jobs"
}
]
}
}
}
]
},
"uniqueItems": true
},
"tags": {
"type": "array",
"title": "Tags",
"items": {
"type": "string",
"examples": [
"misp",
"external report",
"internal report"
]
}
}
},
"required": ["severity", "description", "tactics", "type"]
}

View File

@@ -0,0 +1,79 @@
{
"definitions": {},
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://example.com/object1618746510.json",
"title": "Default",
"type": "object",
"required": [
"severity",
"description",
"tlp"
],
"properties": {
"severity": {
"$id": "#root/severity",
"title": "Severity",
"type": "string",
"default": "Medium",
"x-cols": 6,
"x-class": "pr-2",
"x-display": "icon",
"x-itemIcon": "icon",
"oneOf": [
{
"const": "Low",
"title": "Low",
"icon": "mdi-chevron-up"
},
{
"const": "Medium",
"title": "Medium",
"icon": "mdi-chevron-double-up"
},
{
"const": "High",
"title": "High",
"icon": "mdi-chevron-triple-up"
}
]
},
"tlp": {
"$id": "#root/tlp",
"title": "TLP",
"type": "string",
"x-cols": 6,
"x-class": "pr-2",
"x-display": "icon",
"x-itemIcon": "icon",
"oneOf": [
{
"const": "White",
"title": "White",
"icon": "mdi-alpha-w"
},
{
"const": "Green",
"title": "Green",
"icon": "mdi-alpha-g"
},
{
"const": "Amber",
"title": "Amber",
"icon": "mdi-alpha-a"
},
{
"const": "Red",
"title": "Red",
"icon": "mdi-alpha-r"
}
]
},
"description": {
"$id": "#root/description",
"title": "Description",
"type": "string",
"x-display": "textarea",
"x-class": "pr-2"
}
}
}

File diff suppressed because it is too large Load Diff