forked from p15670423/monkey
Backend ScoutSuite backend code, which handles ScoutSuite data reception, parsing and storing
This commit is contained in:
parent
0b9b89f639
commit
4440027699
|
@ -22,6 +22,11 @@ STATUS_FAILED = "Failed"
|
||||||
# Don't change order! The statuses are ordered by importance/severity.
|
# Don't change order! The statuses are ordered by importance/severity.
|
||||||
ORDERED_TEST_STATUSES = [STATUS_FAILED, STATUS_VERIFY, STATUS_PASSED, STATUS_UNEXECUTED]
|
ORDERED_TEST_STATUSES = [STATUS_FAILED, STATUS_VERIFY, STATUS_PASSED, STATUS_UNEXECUTED]
|
||||||
|
|
||||||
|
MONKEY_FINDING = "monkey_finding"
|
||||||
|
SCOUTSUITE_FINDING = "scoutsuite_finding"
|
||||||
|
FINDING_TYPES = [MONKEY_FINDING, SCOUTSUITE_FINDING]
|
||||||
|
|
||||||
|
|
||||||
TEST_DATA_ENDPOINT_ELASTIC = "unencrypted_data_endpoint_elastic"
|
TEST_DATA_ENDPOINT_ELASTIC = "unencrypted_data_endpoint_elastic"
|
||||||
TEST_DATA_ENDPOINT_HTTP = "unencrypted_data_endpoint_http"
|
TEST_DATA_ENDPOINT_HTTP = "unencrypted_data_endpoint_http"
|
||||||
TEST_MACHINE_EXPLOITED = "machine_exploited"
|
TEST_MACHINE_EXPLOITED = "machine_exploited"
|
||||||
|
@ -31,6 +36,7 @@ TEST_MALICIOUS_ACTIVITY_TIMELINE = "malicious_activity_timeline"
|
||||||
TEST_SEGMENTATION = "segmentation"
|
TEST_SEGMENTATION = "segmentation"
|
||||||
TEST_TUNNELING = "tunneling"
|
TEST_TUNNELING = "tunneling"
|
||||||
TEST_COMMUNICATE_AS_NEW_USER = "communicate_as_new_user"
|
TEST_COMMUNICATE_AS_NEW_USER = "communicate_as_new_user"
|
||||||
|
TEST_SCOUTSUITE_PERMISSIVE_FIREWALL_RULES = "scoutsuite_permissive_firewall_rules"
|
||||||
TESTS = (
|
TESTS = (
|
||||||
TEST_SEGMENTATION,
|
TEST_SEGMENTATION,
|
||||||
TEST_MALICIOUS_ACTIVITY_TIMELINE,
|
TEST_MALICIOUS_ACTIVITY_TIMELINE,
|
||||||
|
@ -40,7 +46,8 @@ TESTS = (
|
||||||
TEST_DATA_ENDPOINT_HTTP,
|
TEST_DATA_ENDPOINT_HTTP,
|
||||||
TEST_DATA_ENDPOINT_ELASTIC,
|
TEST_DATA_ENDPOINT_ELASTIC,
|
||||||
TEST_TUNNELING,
|
TEST_TUNNELING,
|
||||||
TEST_COMMUNICATE_AS_NEW_USER
|
TEST_COMMUNICATE_AS_NEW_USER,
|
||||||
|
TEST_SCOUTSUITE_PERMISSIVE_FIREWALL_RULES
|
||||||
)
|
)
|
||||||
|
|
||||||
PRINCIPLE_DATA_TRANSIT = "data_transit"
|
PRINCIPLE_DATA_TRANSIT = "data_transit"
|
||||||
|
@ -165,6 +172,16 @@ TESTS_MAP = {
|
||||||
PILLARS_KEY: [PEOPLE, NETWORKS, VISIBILITY_ANALYTICS],
|
PILLARS_KEY: [PEOPLE, NETWORKS, VISIBILITY_ANALYTICS],
|
||||||
POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_FAILED, STATUS_PASSED]
|
POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_FAILED, STATUS_PASSED]
|
||||||
},
|
},
|
||||||
|
TEST_SCOUTSUITE_PERMISSIVE_FIREWALL_RULES: {
|
||||||
|
TEST_EXPLANATION_KEY: "ScoutSuite assessed cloud firewall rules and settings.",
|
||||||
|
FINDING_EXPLANATION_BY_STATUS_KEY: {
|
||||||
|
STATUS_FAILED: "ScoutSuite found overly permissive firewall rules.",
|
||||||
|
STATUS_PASSED: "ScoutSuite found no problems with cloud firewall rules."
|
||||||
|
},
|
||||||
|
PRINCIPLE_KEY: PRINCIPLE_RESTRICTIVE_NETWORK_POLICIES,
|
||||||
|
PILLARS_KEY: [NETWORKS],
|
||||||
|
POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_FAILED, STATUS_PASSED]
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
EVENT_TYPE_MONKEY_NETWORK = "monkey_network"
|
EVENT_TYPE_MONKEY_NETWORK = "monkey_network"
|
||||||
|
|
|
@ -1,5 +1,10 @@
|
||||||
# abstract, static method decorator
|
# abstract, static method decorator
|
||||||
# noinspection PyPep8Naming
|
# noinspection PyPep8Naming
|
||||||
|
import operator
|
||||||
|
from functools import reduce
|
||||||
|
from typing import List
|
||||||
|
|
||||||
|
|
||||||
class abstractstatic(staticmethod):
|
class abstractstatic(staticmethod):
|
||||||
__slots__ = ()
|
__slots__ = ()
|
||||||
|
|
||||||
|
@ -8,3 +13,15 @@ class abstractstatic(staticmethod):
|
||||||
function.__isabstractmethod__ = True
|
function.__isabstractmethod__ = True
|
||||||
|
|
||||||
__isabstractmethod__ = True
|
__isabstractmethod__ = True
|
||||||
|
|
||||||
|
|
||||||
|
def _get_value_by_path(data, path: List[str]):
|
||||||
|
return reduce(operator.getitem, path, data)
|
||||||
|
|
||||||
|
|
||||||
|
def get_object_value_by_path(data_object: object, path: List[str]):
|
||||||
|
return _get_value_by_path(data_object, path)
|
||||||
|
|
||||||
|
|
||||||
|
def get_dict_value_by_path(data_dict: dict, path: List[str]):
|
||||||
|
return _get_value_by_path(data_dict, path)
|
||||||
|
|
|
@ -2,16 +2,15 @@
|
||||||
"""
|
"""
|
||||||
Define a Document Schema for Zero Trust findings.
|
Define a Document Schema for Zero Trust findings.
|
||||||
"""
|
"""
|
||||||
from typing import List
|
from typing import Union
|
||||||
|
|
||||||
from mongoengine import Document, StringField, GenericLazyReferenceField
|
from mongoengine import Document, StringField, GenericLazyReferenceField
|
||||||
|
|
||||||
import common.common_consts.zero_trust_consts as zero_trust_consts
|
import common.common_consts.zero_trust_consts as zero_trust_consts
|
||||||
# Dummy import for mongoengine.
|
# Dummy import for mongoengine.
|
||||||
# noinspection PyUnresolvedReferences
|
# noinspection PyUnresolvedReferences
|
||||||
from monkey_island.cc.models.zero_trust.event import Event
|
|
||||||
from monkey_island.cc.models.zero_trust.monkey_finding_details import MonkeyFindingDetails
|
from monkey_island.cc.models.zero_trust.monkey_finding_details import MonkeyFindingDetails
|
||||||
from monkey_island.cc.models.zero_trust.scoutsuite_finding_details import ScoutsuiteFindingDetails
|
from monkey_island.cc.models.zero_trust.scoutsuite_finding_details import ScoutSuiteFindingDetails
|
||||||
|
|
||||||
|
|
||||||
class Finding(Document):
|
class Finding(Document):
|
||||||
|
@ -35,7 +34,8 @@ class Finding(Document):
|
||||||
# SCHEMA
|
# SCHEMA
|
||||||
test = StringField(required=True, choices=zero_trust_consts.TESTS)
|
test = StringField(required=True, choices=zero_trust_consts.TESTS)
|
||||||
status = StringField(required=True, choices=zero_trust_consts.ORDERED_TEST_STATUSES)
|
status = StringField(required=True, choices=zero_trust_consts.ORDERED_TEST_STATUSES)
|
||||||
details = GenericLazyReferenceField(choices=[MonkeyFindingDetails, ScoutsuiteFindingDetails], required=True)
|
type = StringField(required=True, choices=zero_trust_consts.FINDING_TYPES)
|
||||||
|
details = GenericLazyReferenceField(choices=[MonkeyFindingDetails, ScoutSuiteFindingDetails], required=True)
|
||||||
# http://docs.mongoengine.org/guide/defining-documents.html#document-inheritance
|
# http://docs.mongoengine.org/guide/defining-documents.html#document-inheritance
|
||||||
meta = {'allow_inheritance': True}
|
meta = {'allow_inheritance': True}
|
||||||
|
|
||||||
|
@ -48,11 +48,19 @@ class Finding(Document):
|
||||||
|
|
||||||
# Creation methods
|
# Creation methods
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def save_finding(test: str, status: str, detail_ref):
|
def save_finding(test: str,
|
||||||
finding = Finding(test=test,
|
status: str,
|
||||||
|
detail_ref: Union[MonkeyFindingDetails, ScoutSuiteFindingDetails]):
|
||||||
|
temp_finding = Finding(test=test,
|
||||||
status=status,
|
status=status,
|
||||||
details=detail_ref)
|
details=detail_ref,
|
||||||
|
type=Finding._get_finding_type_by_details(detail_ref))
|
||||||
|
temp_finding.save()
|
||||||
|
return temp_finding
|
||||||
|
|
||||||
finding.save()
|
@staticmethod
|
||||||
|
def _get_finding_type_by_details(details: Union[MonkeyFindingDetails, ScoutSuiteFindingDetails]) -> str:
|
||||||
return finding
|
if type(details) == MonkeyFindingDetails:
|
||||||
|
return zero_trust_consts.MONKEY_FINDING
|
||||||
|
else:
|
||||||
|
return zero_trust_consts.SCOUTSUITE_FINDING
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
from mongoengine import Document, DynamicField
|
||||||
|
|
||||||
|
|
||||||
|
class ScoutSuiteDataJson(Document):
|
||||||
|
"""
|
||||||
|
This model is a container for ScoutSuite report data dump.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# SCHEMA
|
||||||
|
scoutsuite_data = DynamicField(required=True)
|
||||||
|
|
||||||
|
# LOGIC
|
||||||
|
@staticmethod
|
||||||
|
def add_scoutsuite_data(scoutsuite_data: str) -> None:
|
||||||
|
current_data = ScoutSuiteDataJson.objects()
|
||||||
|
if not current_data:
|
||||||
|
current_data = ScoutSuiteDataJson()
|
||||||
|
current_data.scoutsuite_data = scoutsuite_data
|
||||||
|
current_data.save()
|
|
@ -1,17 +0,0 @@
|
||||||
from datetime import datetime
|
|
||||||
|
|
||||||
from mongoengine import DateTimeField, EmbeddedDocument, StringField
|
|
||||||
|
|
||||||
|
|
||||||
class ScoutsuiteFinding(EmbeddedDocument):
|
|
||||||
# SCHEMA
|
|
||||||
temp = StringField(required=True)
|
|
||||||
|
|
||||||
# LOGIC
|
|
||||||
@staticmethod
|
|
||||||
def create_scoutsuite_finding(title, message, event_type, timestamp=None):
|
|
||||||
scoutsuite_finding = ScoutsuiteFinding()
|
|
||||||
|
|
||||||
scoutsuite_finding.temp = "temp"
|
|
||||||
|
|
||||||
return scoutsuite_finding
|
|
|
@ -1,11 +1,9 @@
|
||||||
from typing import List
|
from mongoengine import Document, EmbeddedDocumentListField
|
||||||
|
|
||||||
from mongoengine import DateTimeField, Document, StringField, EmbeddedDocumentListField
|
from monkey_island.cc.models.zero_trust.scoutsuite_rule import ScoutSuiteRule
|
||||||
|
|
||||||
from monkey_island.cc.models.zero_trust.scoutsuite_finding import ScoutsuiteFinding
|
|
||||||
|
|
||||||
|
|
||||||
class ScoutsuiteFindingDetails(Document):
|
class ScoutSuiteFindingDetails(Document):
|
||||||
"""
|
"""
|
||||||
This model represents additional information about monkey finding:
|
This model represents additional information about monkey finding:
|
||||||
Events if monkey finding
|
Events if monkey finding
|
||||||
|
@ -13,7 +11,9 @@ class ScoutsuiteFindingDetails(Document):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# SCHEMA
|
# SCHEMA
|
||||||
scoutsuite_findings = EmbeddedDocumentListField(document_type=ScoutsuiteFinding, required=False)
|
scoutsuite_rules = EmbeddedDocumentListField(document_type=ScoutSuiteRule, required=False)
|
||||||
|
|
||||||
def add_scoutsuite_findings(self, scoutsuite_findings: List[ScoutsuiteFinding]) -> None:
|
def add_rule(self, rule: ScoutSuiteRule) -> None:
|
||||||
self.update(push_all__scoutsuite_findings=scoutsuite_findings)
|
if rule not in self.scoutsuite_rules:
|
||||||
|
self.scoutsuite_rules.append(rule)
|
||||||
|
self.save()
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
from mongoengine import StringField, EmbeddedDocument, ListField, \
|
||||||
|
IntField
|
||||||
|
|
||||||
|
from monkey_island.cc.services.zero_trust.scoutsuite.consts import rule_consts
|
||||||
|
|
||||||
|
|
||||||
|
class ScoutSuiteRule(EmbeddedDocument):
|
||||||
|
"""
|
||||||
|
This model represents additional information about monkey finding:
|
||||||
|
Events if monkey finding
|
||||||
|
Scoutsuite findings if scoutsuite finding
|
||||||
|
"""
|
||||||
|
|
||||||
|
# SCHEMA
|
||||||
|
description = StringField(required=True)
|
||||||
|
path = StringField(required=True)
|
||||||
|
level = StringField(required=True, options=rule_consts.RULE_LEVELS)
|
||||||
|
items = ListField()
|
||||||
|
dashboard_name = StringField(required=True)
|
||||||
|
checked_items = IntField(min_value=0)
|
||||||
|
flagged_items = IntField(min_value=0)
|
||||||
|
service = StringField(required=True)
|
||||||
|
rationale = StringField(required=True)
|
||||||
|
remediation = StringField(required=False)
|
||||||
|
compliance = StringField(required=False)
|
||||||
|
references = ListField(required=False)
|
|
@ -1,11 +1,12 @@
|
||||||
import http.client
|
import http.client
|
||||||
|
|
||||||
import flask_restful
|
import flask_restful
|
||||||
from flask import jsonify
|
from flask import jsonify, Response
|
||||||
|
|
||||||
|
from monkey_island.cc.models.zero_trust.scoutsuite_data_json import ScoutSuiteDataJson
|
||||||
from monkey_island.cc.resources.auth.auth import jwt_required
|
from monkey_island.cc.resources.auth.auth import jwt_required
|
||||||
from monkey_island.cc.services.reporting.report import ReportService
|
from monkey_island.cc.services.reporting.report import ReportService
|
||||||
from monkey_island.cc.services.zero_trust.monkey_finding_service import MonkeyFindingService
|
from monkey_island.cc.services.zero_trust.finding_service import FindingService
|
||||||
from monkey_island.cc.services.zero_trust.zero_trust_service import \
|
from monkey_island.cc.services.zero_trust.zero_trust_service import \
|
||||||
ZeroTrustService
|
ZeroTrustService
|
||||||
|
|
||||||
|
@ -16,6 +17,7 @@ REPORT_TYPES = [SECURITY_REPORT_TYPE, ZERO_TRUST_REPORT_TYPE]
|
||||||
REPORT_DATA_PILLARS = "pillars"
|
REPORT_DATA_PILLARS = "pillars"
|
||||||
REPORT_DATA_FINDINGS = "findings"
|
REPORT_DATA_FINDINGS = "findings"
|
||||||
REPORT_DATA_PRINCIPLES_STATUS = "principles"
|
REPORT_DATA_PRINCIPLES_STATUS = "principles"
|
||||||
|
REPORT_DATA_SCOUTSUITE = "scoutsuite"
|
||||||
|
|
||||||
__author__ = ["itay.mizeretz", "shay.nehmad"]
|
__author__ = ["itay.mizeretz", "shay.nehmad"]
|
||||||
|
|
||||||
|
@ -36,6 +38,12 @@ class Report(flask_restful.Resource):
|
||||||
elif report_data == REPORT_DATA_PRINCIPLES_STATUS:
|
elif report_data == REPORT_DATA_PRINCIPLES_STATUS:
|
||||||
return jsonify(ZeroTrustService.get_principles_status())
|
return jsonify(ZeroTrustService.get_principles_status())
|
||||||
elif report_data == REPORT_DATA_FINDINGS:
|
elif report_data == REPORT_DATA_FINDINGS:
|
||||||
return jsonify(MonkeyFindingService.get_all_monkey_findings())
|
return jsonify(FindingService.get_all_findings())
|
||||||
|
elif report_data == REPORT_DATA_SCOUTSUITE:
|
||||||
|
try:
|
||||||
|
data = ScoutSuiteDataJson.objects.get().scoutsuite_data
|
||||||
|
except Exception:
|
||||||
|
data = {}
|
||||||
|
return Response(data, mimetype='application/json')
|
||||||
|
|
||||||
flask_restful.abort(http.client.NOT_FOUND)
|
flask_restful.abort(http.client.NOT_FOUND)
|
||||||
|
|
|
@ -1,15 +1,32 @@
|
||||||
import json
|
import json
|
||||||
|
|
||||||
from monkey_island.cc.database import mongo
|
from monkey_island.cc.database import mongo
|
||||||
|
from monkey_island.cc.models.zero_trust.scoutsuite_data_json import ScoutSuiteDataJson
|
||||||
|
|
||||||
|
from monkey_island.cc.services.zero_trust.scoutsuite.consts.findings_list import SCOUTSUITE_FINDINGS
|
||||||
|
from monkey_island.cc.services.zero_trust.scoutsuite.data_parsing.rule_parsing import RuleParser
|
||||||
|
from monkey_island.cc.services.zero_trust.scoutsuite.scoutsuite_finding_service import ScoutSuiteFindingService
|
||||||
|
from monkey_island.cc.services.zero_trust.scoutsuite.scoutsuite_rule_service import ScoutSuiteRuleService
|
||||||
|
|
||||||
|
|
||||||
def process_scoutsuite_telemetry(telemetry_json):
|
def process_scoutsuite_telemetry(telemetry_json):
|
||||||
# Encode data to json, because mongo can't save it as document (invalid document keys)
|
# Encode data to json, because mongo can't save it as document (invalid document keys)
|
||||||
telemetry_json['data'] = json.dumps(telemetry_json['data'])
|
telemetry_json['data'] = json.dumps(telemetry_json['data'])
|
||||||
|
ScoutSuiteDataJson.add_scoutsuite_data(telemetry_json['data'])
|
||||||
|
scoutsuite_data = json.loads(telemetry_json['data'])['data']
|
||||||
|
create_scoutsuite_findings(scoutsuite_data)
|
||||||
update_data(telemetry_json)
|
update_data(telemetry_json)
|
||||||
|
|
||||||
|
|
||||||
|
def create_scoutsuite_findings(scoutsuite_data):
|
||||||
|
for finding in SCOUTSUITE_FINDINGS:
|
||||||
|
for rule in finding.rules:
|
||||||
|
rule_data = RuleParser.get_rule_data(scoutsuite_data, rule)
|
||||||
|
rule = ScoutSuiteRuleService.get_rule_from_rule_data(rule_data)
|
||||||
|
ScoutSuiteFindingService.process_rule(finding, rule)
|
||||||
|
|
||||||
|
|
||||||
def update_data(telemetry_json):
|
def update_data(telemetry_json):
|
||||||
mongo.db.scoutsuite.update(
|
mongo.db.scoutsuite.insert_one(
|
||||||
{'guid': telemetry_json['monkey_guid']},
|
{'guid': telemetry_json['monkey_guid']},
|
||||||
{'$push': {'results': telemetry_json['data']}})
|
{'results': telemetry_json['data']})
|
||||||
|
|
|
@ -18,7 +18,9 @@ class EventsService:
|
||||||
'latest_events': {'$slice': ['$events', -1 * EVENT_FETCH_CNT]},
|
'latest_events': {'$slice': ['$events', -1 * EVENT_FETCH_CNT]},
|
||||||
'event_count': {'$size': '$events'}}},
|
'event_count': {'$size': '$events'}}},
|
||||||
{'$unset': ['events']}]
|
{'$unset': ['events']}]
|
||||||
details = MonkeyFindingDetails.objects.aggregate(*pipeline).next()
|
details = list(MonkeyFindingDetails.objects.aggregate(*pipeline))
|
||||||
|
if details:
|
||||||
|
details = details[0]
|
||||||
details['latest_events'] = EventsService._get_events_without_overlap(details['event_count'],
|
details['latest_events'] = EventsService._get_events_without_overlap(details['event_count'],
|
||||||
details['latest_events'])
|
details['latest_events'])
|
||||||
return details
|
return details
|
||||||
|
|
|
@ -2,13 +2,24 @@ from typing import List
|
||||||
|
|
||||||
from common.common_consts import zero_trust_consts
|
from common.common_consts import zero_trust_consts
|
||||||
from monkey_island.cc.models.zero_trust.finding import Finding
|
from monkey_island.cc.models.zero_trust.finding import Finding
|
||||||
|
from monkey_island.cc.services.zero_trust.events_service import EventsService
|
||||||
|
|
||||||
|
|
||||||
class FindingService:
|
class FindingService:
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_all_findings() -> List[Finding]:
|
def get_all_findings() -> List[Finding]:
|
||||||
return list(Finding.objects)
|
findings = list(Finding.objects)
|
||||||
|
details = []
|
||||||
|
for i in range(len(findings)):
|
||||||
|
if findings[i].type == zero_trust_consts.MONKEY_FINDING:
|
||||||
|
details = EventsService.fetch_events_for_display(findings[i].details.id)
|
||||||
|
elif findings[i].type == zero_trust_consts.SCOUTSUITE_FINDING:
|
||||||
|
details = findings[i].details.fetch().to_mongo()
|
||||||
|
findings[i] = findings[i].to_mongo()
|
||||||
|
findings[i] = FindingService.get_enriched_finding(findings[i])
|
||||||
|
findings[i]['details'] = details
|
||||||
|
return findings
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_enriched_finding(finding):
|
def get_enriched_finding(finding):
|
||||||
|
@ -19,5 +30,6 @@ class FindingService:
|
||||||
'test_key': finding['test'],
|
'test_key': finding['test'],
|
||||||
'pillars': test_info[zero_trust_consts.PILLARS_KEY],
|
'pillars': test_info[zero_trust_consts.PILLARS_KEY],
|
||||||
'status': finding['status'],
|
'status': finding['status'],
|
||||||
|
'type': finding['type']
|
||||||
}
|
}
|
||||||
return enriched_finding
|
return enriched_finding
|
||||||
|
|
|
@ -6,7 +6,6 @@ from common.common_consts import zero_trust_consts
|
||||||
from monkey_island.cc.models.zero_trust.event import Event
|
from monkey_island.cc.models.zero_trust.event import Event
|
||||||
from monkey_island.cc.models.zero_trust.finding import Finding
|
from monkey_island.cc.models.zero_trust.finding import Finding
|
||||||
from monkey_island.cc.models.zero_trust.monkey_finding_details import MonkeyFindingDetails
|
from monkey_island.cc.models.zero_trust.monkey_finding_details import MonkeyFindingDetails
|
||||||
from monkey_island.cc.services.zero_trust.finding_service import FindingService
|
|
||||||
|
|
||||||
|
|
||||||
class MonkeyFindingService:
|
class MonkeyFindingService:
|
||||||
|
@ -38,17 +37,7 @@ class MonkeyFindingService:
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def add_events(finding: Finding, events: List[Event]):
|
def add_events(finding: Finding, events: List[Event]):
|
||||||
finding.details.fetch().add_events(events)
|
finding.details.fetch().add_events(events).save()
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_all_monkey_findings():
|
|
||||||
findings = FindingService.get_all_findings()
|
|
||||||
for i in range(len(findings)):
|
|
||||||
details = MonkeyFindingService.fetch_events_for_display(findings[i].details.id)
|
|
||||||
findings[i] = findings[i].to_mongo()
|
|
||||||
findings[i] = FindingService.get_enriched_finding(findings[i])
|
|
||||||
findings[i]['details'] = details
|
|
||||||
return findings
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_events_by_finding(finding_id: str) -> List[object]:
|
def get_events_by_finding(finding_id: str) -> List[object]:
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
from common.common_consts import zero_trust_consts
|
||||||
|
from common.common_consts.zero_trust_consts import NETWORKS
|
||||||
|
from monkey_island.cc.services.zero_trust.scoutsuite.consts.ec2_rules import EC2Rules
|
||||||
|
|
||||||
|
|
||||||
|
class PERMISSIVE_FIREWALL_RULES:
|
||||||
|
rules = [EC2Rules.SECURITY_GROUP_ALL_PORTS_TO_ALL, EC2Rules.SECURITY_GROUP_OPENS_TCP_PORT_TO_ALL,
|
||||||
|
EC2Rules.SECURITY_GROUP_OPENS_UDP_PORT_TO_ALL, EC2Rules.SECURITY_GROUP_OPENS_RDP_PORT_TO_ALL,
|
||||||
|
EC2Rules.SECURITY_GROUP_OPENS_SSH_PORT_TO_ALL, EC2Rules.SECURITY_GROUP_OPENS_MYSQL_PORT_TO_ALL,
|
||||||
|
EC2Rules.SECURITY_GROUP_OPENS_MSSQL_PORT_TO_ALL, EC2Rules.SECURITY_GROUP_OPENS_MONGODB_PORT_TO_ALL,
|
||||||
|
EC2Rules.SECURITY_GROUP_OPENS_ORACLE_DB_PORT_TO_ALL, EC2Rules.SECURITY_GROUP_OPENS_POSTGRESQL_PORT_TO_ALL,
|
||||||
|
EC2Rules.SECURITY_GROUP_OPENS_NFS_PORT_TO_ALL, EC2Rules.SECURITY_GROUP_OPENS_SMTP_PORT_TO_ALL,
|
||||||
|
EC2Rules.SECURITY_GROUP_OPENS_DNS_PORT_TO_ALL, EC2Rules.SECURITY_GROUP_OPENS_ALL_PORTS_TO_SELF,
|
||||||
|
EC2Rules.SECURITY_GROUP_OPENS_ALL_PORTS, EC2Rules.SECURITY_GROUP_OPENS_PLAINTEXT_PORT_FTP,
|
||||||
|
EC2Rules.SECURITY_GROUP_OPENS_PLAINTEXT_PORT_TELNET, EC2Rules.SECURITY_GROUP_OPENS_PORT_RANGE]
|
||||||
|
|
||||||
|
pillars = [NETWORKS]
|
||||||
|
|
||||||
|
test = zero_trust_consts.TEST_SCOUTSUITE_PERMISSIVE_FIREWALL_RULES
|
|
@ -0,0 +1,3 @@
|
||||||
|
from monkey_island.cc.services.zero_trust.scoutsuite.consts.findings import PERMISSIVE_FIREWALL_RULES
|
||||||
|
|
||||||
|
SCOUTSUITE_FINDINGS = [PERMISSIVE_FIREWALL_RULES]
|
|
@ -0,0 +1,4 @@
|
||||||
|
RULE_LEVEL_DANGER = 'danger'
|
||||||
|
RULE_LEVEL_WARNING = 'warning'
|
||||||
|
|
||||||
|
RULE_LEVELS = (RULE_LEVEL_DANGER, RULE_LEVEL_WARNING)
|
|
@ -0,0 +1,9 @@
|
||||||
|
from enum import Enum
|
||||||
|
|
||||||
|
|
||||||
|
SERVICES = 'services'
|
||||||
|
FINDINGS = 'findings'
|
||||||
|
|
||||||
|
|
||||||
|
class SERVICE_TYPES(Enum):
|
||||||
|
EC2 = 'ec2'
|
|
@ -0,0 +1,65 @@
|
||||||
|
from typing import List
|
||||||
|
|
||||||
|
from common.common_consts import zero_trust_consts
|
||||||
|
from monkey_island.cc.models.zero_trust.finding import Finding
|
||||||
|
from monkey_island.cc.models.zero_trust.scoutsuite_finding_details import ScoutSuiteFindingDetails
|
||||||
|
from monkey_island.cc.models.zero_trust.scoutsuite_rule import ScoutSuiteRule
|
||||||
|
from monkey_island.cc.services.zero_trust.scoutsuite.scoutsuite_rule_service import ScoutSuiteRuleService
|
||||||
|
|
||||||
|
|
||||||
|
class ScoutSuiteFindingService:
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
# TODO add type hinting like finding: Union[SCOUTSUITE_FINDINGS]?
|
||||||
|
def process_rule(finding, rule: ScoutSuiteRule):
|
||||||
|
existing_findings = Finding.objects(test=finding.test, type=zero_trust_consts.SCOUTSUITE_FINDING)
|
||||||
|
assert (len(existing_findings) < 2), "More than one finding exists for {}".format(finding.test)
|
||||||
|
|
||||||
|
if len(existing_findings) == 0:
|
||||||
|
ScoutSuiteFindingService.create_new_finding_from_rule(finding, rule)
|
||||||
|
else:
|
||||||
|
ScoutSuiteFindingService.add_rule(existing_findings[0], rule)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def create_new_finding_from_rule(finding, rule: ScoutSuiteRule):
|
||||||
|
details = ScoutSuiteFindingDetails()
|
||||||
|
details.scoutsuite_rules = [rule]
|
||||||
|
details.save()
|
||||||
|
status = ScoutSuiteFindingService.get_finding_status_from_rules(details.scoutsuite_rules)
|
||||||
|
Finding.save_finding(finding.test, status, details)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_finding_status_from_rules(rules: List[ScoutSuiteRule]) -> str:
|
||||||
|
if len(rules) == 0:
|
||||||
|
return zero_trust_consts.STATUS_UNEXECUTED
|
||||||
|
elif filter(lambda x: ScoutSuiteRuleService.is_rule_dangerous(x), rules):
|
||||||
|
return zero_trust_consts.STATUS_FAILED
|
||||||
|
elif filter(lambda x: ScoutSuiteRuleService.is_rule_warning(x), rules):
|
||||||
|
return zero_trust_consts.STATUS_VERIFY
|
||||||
|
else:
|
||||||
|
return zero_trust_consts.STATUS_PASSED
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def add_rule(finding: Finding, rule: ScoutSuiteRule):
|
||||||
|
ScoutSuiteFindingService.change_finding_status_by_rule(finding, rule)
|
||||||
|
finding.save()
|
||||||
|
finding.details.fetch().add_rule(rule)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def change_finding_status_by_rule(finding: Finding, rule: ScoutSuiteRule):
|
||||||
|
rule_status = ScoutSuiteFindingService.get_finding_status_from_rules([rule])
|
||||||
|
finding_status = finding.status
|
||||||
|
new_finding_status = ScoutSuiteFindingService.get_finding_status_from_rule_status(finding_status, rule_status)
|
||||||
|
if finding_status != new_finding_status:
|
||||||
|
finding.status = new_finding_status
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_finding_status_from_rule_status(finding_status: str, rule_status: str) -> str:
|
||||||
|
if finding_status == zero_trust_consts.STATUS_FAILED or rule_status == zero_trust_consts.STATUS_FAILED:
|
||||||
|
return zero_trust_consts.STATUS_FAILED
|
||||||
|
elif finding_status == zero_trust_consts.STATUS_VERIFY or rule_status == zero_trust_consts.STATUS_VERIFY:
|
||||||
|
return zero_trust_consts.STATUS_VERIFY
|
||||||
|
elif finding_status == zero_trust_consts.STATUS_PASSED or rule_status == zero_trust_consts.STATUS_PASSED:
|
||||||
|
return zero_trust_consts.STATUS_PASSED
|
||||||
|
else:
|
||||||
|
return zero_trust_consts.STATUS_UNEXECUTED
|
|
@ -0,0 +1,30 @@
|
||||||
|
from monkey_island.cc.models.zero_trust.scoutsuite_rule import ScoutSuiteRule
|
||||||
|
from monkey_island.cc.services.zero_trust.scoutsuite.consts import rule_consts
|
||||||
|
|
||||||
|
|
||||||
|
class ScoutSuiteRuleService:
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_rule_from_rule_data(rule_data: dict) -> ScoutSuiteRule:
|
||||||
|
rule = ScoutSuiteRule()
|
||||||
|
rule.description = rule_data['description']
|
||||||
|
rule.path = rule_data['path']
|
||||||
|
rule.level = rule_data['level']
|
||||||
|
rule.items = rule_data['items']
|
||||||
|
rule.dashboard_name = rule_data['dashboard_name']
|
||||||
|
rule.checked_items = rule_data['checked_items']
|
||||||
|
rule.flagged_items = rule_data['flagged_items']
|
||||||
|
rule.service = rule_data['service']
|
||||||
|
rule.rationale = rule_data['rationale']
|
||||||
|
rule.remediation = rule_data['remediation']
|
||||||
|
rule.compliance = rule_data['compliance']
|
||||||
|
rule.references = rule_data['references']
|
||||||
|
return rule
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def is_rule_dangerous(rule: ScoutSuiteRule):
|
||||||
|
return rule.level == rule_consts.RULE_LEVEL_DANGER and len(rule.items) != 0
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def is_rule_warning(rule: ScoutSuiteRule):
|
||||||
|
return rule.level == rule_consts.RULE_LEVEL_WARNING and len(rule.items) != 0
|
|
@ -1,3 +0,0 @@
|
||||||
|
|
||||||
class ScoutsuiteFindingService:
|
|
||||||
pass
|
|
Loading…
Reference in New Issue