Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add stairwell expansion module and update misp-objects to a193e03 #660

Merged
merged 2 commits into from
May 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ For more information: [Extending MISP with Python modules](https://www.misp-proj
* [Socialscan](misp_modules/modules/expansion/socialscan.py) - a hover module to check if an email address or a username is used on different online platforms, using the [socialscan](https://github.com/iojw/socialscan) python library
* [SophosLabs Intelix](misp_modules/modules/expansion/sophoslabs_intelix.py) - SophosLabs Intelix is an API for Threat Intelligence and Analysis (free tier available). [SophosLabs](https://aws.amazon.com/marketplace/pp/B07SLZPMCS)
* [sourcecache](misp_modules/modules/expansion/sourcecache.py) - a module to cache a specific link from a MISP instance.
* [stairwell](misp_modules/modules/expansion/stairwell.py) - an expansion module to enrich hash observables with the Stairwell API
* [STIX2 pattern syntax validator](misp_modules/modules/expansion/stix2_pattern_syntax_validator.py) - a module to check a STIX2 pattern syntax.
* [ThreatCrowd](misp_modules/modules/expansion/threatcrowd.py) - an expansion module for [ThreatCrowd](https://www.threatcrowd.org/).
* [threatminer](misp_modules/modules/expansion/threatminer.py) - an expansion module to expand from [ThreatMiner](https://www.threatminer.org/).
Expand Down Expand Up @@ -577,7 +578,7 @@ cd ../

## Documentation

In order to provide documentation about some modules that require specific input / output / configuration, the [doc](doc) directory contains detailed information about the general purpose, requirements, features, input and ouput of each of these modules:
In order to provide documentation about some modules that require specific input / output / configuration, the [index.md](docs/index.md) file within the [docs](docs) directory contains detailed information about the general purpose, requirements, features, input and ouput of each of these modules:

- ***description** - quick description of the general purpose of the module, as the one given by the moduleinfo
- **requirements** - special libraries needed to make the module work
Expand Down
1 change: 1 addition & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ For more information: [Extending MISP with Python modules](https://www.circl.lu/
* [Sigma queries](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/expansion/sigma_queries.py) - Experimental expansion module querying a sigma rule to convert it into all the available SIEM signatures.
* [Sigma syntax validator](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/expansion/sigma_syntax_validator.py) - Sigma syntax validator.
* [sourcecache](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/expansion/sourcecache.py) - a module to cache a specific link from a MISP instance.
* [stairwell](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/expansion/stairwell.py) - an expansion module to enrich hash observables with the Stairwell API
* [STIX2 pattern syntax validator](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/expansion/stix2_pattern_syntax_validator.py) - a module to check a STIX2 pattern syntax.
* [ThreatCrowd](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/expansion/threatcrowd.py) - an expansion module for [ThreatCrowd](https://www.threatcrowd.org/).
* [threatminer](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/expansion/threatminer.py) - an expansion module to expand from [ThreatMiner](https://www.threatminer.org/).
Expand Down
20 changes: 20 additions & 0 deletions documentation/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1582,6 +1582,26 @@ Module to cache web pages of analysis reports, OSINT sources. The module returns

-----

#### [stairwell](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/expansion/stairwell.py)

<img src=logos/stairwell.png height=60>

An expansion module to enrich hash observables with the Stairwell API.
- **features**:
>This module takes a file hash as input and queries the Stairwell API. It will create a misp-object with the additional enrichment intel.
- **input**:
>MD5, SHA1, or SHA256
- **output**:
>A stairwell misp-object with additional enrichment intel.
- **references**:
>https://docs.stairwell.com
- **requirements**:
>- json
>- pymisp
>- requests

-----

#### [stix2_pattern_syntax_validator](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/expansion/stix2_pattern_syntax_validator.py)

<img src=logos/stix.png height=60>
Expand Down
Binary file added documentation/logos/stairwell.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
14 changes: 14 additions & 0 deletions documentation/website/expansion/stairwell.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"description": "Module to query the Stairwell API to get additional information about the input hash attribute",
"logo": "stairwell.png",
"requirements": [
"Access to Stairwell platform (apikey)"
],
"input": "A hash attribute (md5, sha1, sha256).",
"output": "File object related to the input attribute found on Stairwell platform.",
"references": [
"https://stairwell.com",
"https://docs.stairwell.com"
],
"features": "The module takes a hash attribute as input and queries Stariwell's API to fetch additional data about it. The result, if the payload is observed in Stariwell, is a file object describing the file the input hash is related to."
}
2 changes: 1 addition & 1 deletion misp_modules/lib/misp-objects
Submodule misp-objects updated 44 files
+21 −11 README.md
+1 −1 objects/abuseipdb/definition.json
+1 −1 objects/artifact/definition.json
+42 −0 objects/cert-pl-phishing/definition.json
+19 −4 objects/command-line/definition.json
+3 −3 objects/concordia-mtmf-intrusion-set/definition.json
+2 −2 objects/covid19-csse-daily-report/definition.json
+1 −1 objects/crowdsec-ip-context/definition.json
+90 −3 objects/cs-beacon-config/definition.json
+7 −6 objects/ddos/definition.json
+1 −1 objects/diamond/definition.json
+5 −0 objects/exploit/definition.json
+8 −1 objects/flowintel-cm-case/definition.json
+35 −0 objects/flowintel-cm-task-note/definition.json
+9 −7 objects/flowintel-cm-task/definition.json
+151 −0 objects/generalizing-persuasion-framework/definition.json
+1 −1 objects/imsi-catcher/definition.json
+2 −1 objects/instant-message/definition.json
+5 −3 objects/intelmq_event/definition.json
+2 −1 objects/intelmq_report/definition.json
+1 −1 objects/mactime-timeline-analysis/definition.json
+3 −3 objects/netflow/definition.json
+3 −3 objects/network-connection/definition.json
+2 −2 objects/network-socket/definition.json
+108 −0 objects/network-traffic/definition.json
+5 −1 objects/news-media/definition.json
+10 −2 objects/organization/definition.json
+2 −2 objects/paloalto-threat-event/definition.json
+217 −0 objects/pe-optional-header/definition.json
+78 −1 objects/pe/definition.json
+3 −2 objects/person/definition.json
+3 −3 objects/probabilistic-data-structure/definition.json
+6 −0 objects/process/definition.json
+3 −3 objects/r2graphity/definition.json
+70 −3 objects/ransomware-group-post/definition.json
+11 −6 objects/registry-key/definition.json
+1 −1 objects/scrippsco2-c13-daily/definition.json
+1 −1 objects/scrippsco2-co2-daily/definition.json
+1 −1 objects/scrippsco2-o18-daily/definition.json
+2 −2 objects/sigmf-expanded-recording/definition.json
+89 −0 objects/stairwell/definition.json
+5 −5 objects/submarine/definition.json
+179 −4 relationships/definition.json
+1 −0 schema_objects.json
2 changes: 1 addition & 1 deletion misp_modules/modules/expansion/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
'trustar_enrich', 'recordedfuture', 'html_to_markdown', 'socialscan', 'passive-ssh',
'qintel_qsentry', 'mwdb', 'hashlookup', 'mmdb_lookup', 'ipqs_fraud_and_risk_scoring',
'clamav', 'jinja_template_rendering','hyasinsight', 'variotdbs', 'crowdsec',
'extract_url_components', 'ipinfo', 'whoisfreaks', 'ip2locationio', 'vysion']
'extract_url_components', 'ipinfo', 'whoisfreaks', 'ip2locationio', 'vysion', 'stairwell']


minimum_required_fields = ('type', 'uuid', 'value')
Expand Down
149 changes: 149 additions & 0 deletions misp_modules/modules/expansion/stairwell.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
import json
import re
import requests
from pymisp import MISPEvent, MISPObject
from . import check_input_attribute, checking_error, standard_error_message


misperrors = {
'error': 'Error'
}
mispattributes = {
'input': [
'md5',
'sha1',
'sha256'
],
'format': 'misp_standard'
}
moduleinfo = {
'version': '0.1',
'author': 'goodlandsecurity',
'description': 'Enrich hash observables with the Stairwell API',
'module-type': ['expansion']
}
moduleconfig = ["apikey"]


def parse_response(response: dict):
attribute_mapping = {
'environments': {'type': 'comment', 'object_relation': 'environment', 'distribution': 5},
'imphash': {'type': 'imphash', 'object_relation': 'impash', 'distribution': 5},
'magic': {'type': 'comment', 'object_relation': 'magic', 'distribution': 5},
'malEval': {
'probabilityBucket': {'type': 'comment', 'object_relation': 'malEval-probability', 'distribution': 5},
'severity': {'type': 'comment', 'object_relation': 'malEval-severity', 'distribution': 5}
},
'md5': {'type': 'md5', 'object_relation': 'md5', 'distribution': 5},
'mimeType': {'type': 'mime-type', 'object_relation': 'mime-type', 'distribution': 5},
'sha1': {'type': 'sha1', 'object_relation': 'sha1', 'distribution': 5},
'sha256': {'type': 'sha256', 'object_relation': 'sha256', 'distribution': 5},
'shannonEntropy': {'type': 'float', 'object_relation': 'entropy', 'distribution': 5},
'size': {'type': 'size-in-bytes', 'object_relation': 'size-in-bytes', 'distribution': 5},
'stairwellFirstSeenTime': {'type': 'datetime', 'object_relation': 'stairwell-first-seen', 'distribution': 5},
'tlsh': {'type': 'tlsh', 'object_relation': 'tlsh', 'distribution': 5},
'yaraRuleMatches': {'type': 'text', 'object_relation': 'yara-rule-match', 'comment': 'matching Stairwell yara rule name', 'distribution': 5}
}
environments_mapping = {
"NCS2SM-YHB2KT-SAFUDX-JC7F6WYA": "Florian's Open Rules",
"VR9Z98-4KU7ZC-PCNFEG-FURQ66FW": "Jotti",
"D7W6M6-BA9BS4-BQ23Z4-NKCNWQ96": "Malshare",
"D4447Q-WJJL6P-W7ME89-WHXJK8TW": "Malware Bazaar",
"XAKLND-DKWP3Z-56RL88-6XJ5NH46": "Pro Rules",
"GMEELM-K226XF-F95XZL-7VEJFKZ6": "Public Samples",
"5HEG8N-9T7UPG-8SZJ7T-2J4XSDC6": "RH-ISAC",
"2NN2BJ-HDVQHS-49824H-2SEDBBLJ": "RH-ISAC Malware Sharing",
"VCZTNF-8S76AK-LUU53W-2SWFFZWJ": "Stairwell Experimental Rules",
"GEG6FU-MRARGF-TLTM6X-H6MGDT5E": "Stairwell Methodology Rules",
"EB3DXY-3ZYFVH-6HNKJQ-GAPKHESS": "Stairwell OSINT Rules",
"NQNJM6-5LSCAF-3MC5FJ-W8EKGW6N": "Stairwell Research Rules",
"TT9GM5-JUMD8H-9828FL-GAW5NNXE": "stairwell-public-verdicts",
"MKYSAR-3XN9MB-3VAK3R-888ZJUTJ": "Threat Report Feeds",
"6HP5R3-ZM7DAN-RB4732-X6QPCJ36": "Virusshare",
"TV6WCV-7Y79LE-BK79EY-C8GUEY46": "vxintel"
}

misp_event = MISPEvent()
misp_object = MISPObject('stairwell')
for feature, attribute in attribute_mapping.items():
if feature in response.keys() and response[feature]:
if feature == 'yaraRuleMatches':
for rule in response[feature]:
env_pattern = r'\b[A-Z0-9]{6}-[A-Z0-9]{6}-[A-Z0-9]{6}-[A-Z0-9]{8}\b'
env = re.findall(env_pattern, rule.split('yaraRules/')[0])[0]
misp_attribute = {
'value': rule.split('yaraRules/')[1],
'comment': f'Rule from: {environments_mapping.get(env, "Unknown UUID!")}'
}
misp_attribute.update(attribute)
misp_object.add_attribute(**misp_attribute)
elif feature == 'environments':
for env in response[feature]:
misp_attribute = {
'value': environments_mapping.get(env, f'Unknown Environment: {env}'),
'comment': 'Hash observed in'
}
misp_attribute.update(attribute)
misp_object.add_attribute(**misp_attribute)
elif feature == 'malEval':
for attr in attribute:
misp_attribute = {'value': response[feature][attr]}
misp_attribute.update(attribute[attr])
misp_object.add_attribute(**misp_attribute)
else:
misp_attribute = {'value': response[feature]}
misp_attribute.update(attribute)
attr = misp_object.add_attribute(**misp_attribute)
if feature in ('md5', 'sha1', 'sha256'):
for label in response['malEval']['labels']:
attr.add_tag(label)
misp_event.add_object(**misp_object)

event = json.loads(misp_event.to_json())
results = {'Object': event['Object']}

return {'results': results}


def handler(q=False):
if q is False:
return False
request = json.loads(q)
if not request.get('config') or not request['config'].get('apikey'):
misperrors['error'] = 'A Stairwell api key is required for this module!'
return misperrors
if not request.get('attribute') or not check_input_attribute(request['attribute'], requirements=('type', 'value')):
misperrors['error'] = f'{standard_error_message}, {checking_error}.'
return misperrors
attribute = request['attribute']
if attribute['type'] not in mispattributes['input']:
misperrors['error'] = 'Unsupported attribute type!'
return misperrors

headers = {
"Accept": "application/json",
"Authorization": request['config']['apikey'],
"User-Agent": f"misp-module {__file__} {moduleinfo['version']}"
}
url = f"https://app.stairwell.com/v1/objects/{attribute['value']}/metadata"
response = requests.get(url=url, headers=headers).json()

if response.get('code') == 16: # bad auth
return {'error': f"{response['message']} Is api key valid?"}
elif response.get('code') == 5: # not found
return {'error': f"{attribute['type']}:{attribute['value']} {response['message']}"}
elif response.get('code') == 2: # encoding/hex: invalid byte
return {'error': response['message']}
elif response.get('code'): # catchall for potential unforeseen errors
return {'error': response['message'], 'code': response['code']}
else:
return parse_response(response)


def introspection():
return mispattributes


def version():
moduleinfo['config'] = moduleconfig
return moduleinfo