-
Notifications
You must be signed in to change notification settings - Fork 17
/
Copy path__init__.py
202 lines (178 loc) · 8.82 KB
/
__init__.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
from datetime import datetime
import traceback
import boto3
from botocore.exceptions import ClientError
from ...config import config
from ...log import log
class Submitter():
def __init__(self, event):
self.event = event
self.region = config.get('aws', 'region')
self.confirm_instance = config.getboolean('aws', 'confirm_instance')
def find_instance(self, instance_id, mac_address):
# Instance IDs are unique to the region, not the account, so we have to check them all
report_region = self.region
ec2instance = None
ec2_client = boto3.client("ec2", region_name=report_region)
regions = [region["RegionName"] for region in ec2_client.describe_regions()["Regions"]]
for region in regions:
ec2 = boto3.resource("ec2", region_name=region)
try:
ec2instance = ec2.Instance(instance_id)
found = False
# Confirm the mac address matches
if ec2instance.network_interfaces:
for iface in ec2instance.network_interfaces:
det_mac = mac_address.lower().replace(":", "").replace("-", "")
ins_mac = iface.mac_address.lower().replace(":", "").replace("-", "")
if det_mac == ins_mac:
found = True
if found: # pylint: disable=R1723
return region, ec2instance
except ClientError:
continue
except Exception: # pylint: disable=W0703
trace = traceback.format_exc()
log.exception(str(trace))
continue
return report_region, ec2instance
@staticmethod
def send_to_securityhub(manifest, region):
client = boto3.client('securityhub', region_name=region)
check_response = {}
found = False
try:
check_response = client.get_findings(Filters={'Id': [{'Value': manifest["Id"], 'Comparison': 'EQUALS'}]})
for _ in check_response["Findings"]:
found = True
except ClientError:
pass
import_response = False
if not found:
try:
import_response = client.batch_import_findings(Findings=[manifest])
except ClientError as err:
# Boto3 issue communicating with SH, throw the error in the log
log.exception(str(err))
return import_response
def submit(self):
log.info("Processing detection: %s", self.event.detect_description)
det_region = self.region
send = False
if self.confirm_instance:
try:
if self.event.instance_id:
det_region, instance = self.find_instance(self.event.instance_id, self.event.device_details["mac_address"])
if instance is None:
log.warning("Instance %s with MAC address %s not found in regions searched. Alert not processed.",
self.event.instance_id, self.event.device_details["mac_address"])
return
try:
if instance.network_interfaces:
for _ in instance.network_interfaces:
# Only send alerts for instances we can find
send = True
except ClientError:
# Not our instance
i_id = self.event.instance_id
mac = self.event.device_details["mac_address"]
log.info("Instance %s with MAC address %s not found in regions searched. Alert not processed.", i_id, mac)
except AttributeError:
# Instance ID was not provided by the detection
log.info("Instance ID not provided by detection. Alert not processed.")
else:
# If we're not confirming the instance, we can just send the alert
send = True
if send:
sh_payload = self.create_payload(det_region)
response = self.send_to_securityhub(sh_payload, det_region)
if not response:
log.info("Detection already submitted to Security Hub. Alert not processed.")
else:
if response["SuccessCount"] > 0:
submit_msg = f"Detection submitted to Security Hub. (Request ID: {response['ResponseMetadata']['RequestId']})"
log.info(submit_msg)
def create_payload(self, instance_region):
region = self.region
try:
account_id = boto3.client("sts", region_name=region).get_caller_identity().get('Account')
except KeyError:
# Failed to get endpoint_resolver the first time, try it again
account_id = boto3.client("sts", region_name=region).get_caller_identity().get('Account')
severity_original = self.event.severity
severity_label = severity_original.upper()
if "gov" in region:
ARN = "arn:aws-us-gov:securityhub:{}:358431324613:product/crowdstrike/crowdstrike-falcon".format(region)
else:
ARN = "arn:aws:securityhub:{}:517716713836:product/crowdstrike/crowdstrike-falcon".format(region)
payload = {
"SchemaVersion": "2018-10-08",
"ProductArn": f"{ARN}",
"AwsAccountId": account_id,
"SourceUrl": self.event.falcon_link,
"GeneratorId": "Falcon Host",
"CreatedAt": datetime.utcfromtimestamp(float(self.event.event_create_time) / 1000.).isoformat() + 'Z',
"UpdatedAt": ((datetime.utcfromtimestamp(datetime.timestamp(datetime.now()))).isoformat() + 'Z'),
"RecordState": "ACTIVE",
"Severity": {"Label": severity_label, "Original": severity_original},
"ProductFields": {"crowdstrike/crowdstrike-falcon/cid": self.event.cid},
}
# Instance ID based detail
try:
payload["Id"] = f"{self.event.instance_id}{self.event.event_id}"
payload["Title"] = "Falcon Alert. Instance: %s" % self.event.instance_id
payload["Resources"] = [{"Type": "AwsEc2Instance", "Id": self.event.instance_id, "Region": instance_region}]
except AttributeError:
payload["Id"] = f"UnknownInstanceID:{self.event.event_id}"
payload["Title"] = "Falcon Alert"
payload["Resources"] = [{"Type": "Other",
"Id": f"UnknownInstanceId:{self.event.event_id}",
"Region": region
}]
# Description
aws_id = ""
if self.event.cloud_provider_account_id:
aws_id = f"| AWS Account for alerting instance: {self.event.cloud_provider_account_id}"
payload["Description"] = f"{self.event.detect_description} {aws_id}"
# TTPs
try:
payload["Types"] = ["Namespace: TTPs",
"Category: %s" % self.event.original_event["event"]["Tactic"],
"Classifier: %s" % self.event.original_event["event"]["Technique"]
]
except KeyError:
payload.pop("Types", None)
# Running process detail
try:
payload["Process"] = {}
payload["Process"]["Name"] = self.event.original_event["event"]["FileName"]
payload["Process"]["Path"] = self.event.original_event["event"]["FilePath"]
except KeyError:
payload.pop("Process", None)
# Network detail
try:
payload['Network'] = self.network_payload()
except KeyError:
pass
return payload
def network_payload(self):
net = {}
net['Direction'] = \
"IN" if self.event.original_event['event']['NetworkAccesses'][0]['ConnectionDirection'] == 0 else 'OUT'
net['Protocol'] = self.event.original_event['event']['NetworkAccesses'][0]['Protocol']
net['SourceIpV4'] = self.event.original_event['event']['NetworkAccesses'][0]['LocalAddress']
net['SourcePort'] = self.event.original_event['event']['NetworkAccesses'][0]['LocalPort']
net['DestinationIpV4'] = self.event.original_event['event']['NetworkAccesses'][0]['RemoteAddress']
net['DestinationPort'] = self.event.original_event['event']['NetworkAccesses'][0]['RemotePort']
return net
class Runtime():
RELEVANT_EVENT_TYPES = ['EppDetectionSummaryEvent']
def __init__(self):
log.info("AWS Backend is enabled.")
def is_relevant(self, falcon_event):
if falcon_event.cloud_provider is not None:
return falcon_event.cloud_provider[:3].upper() == 'AWS'
return False
def process(self, falcon_event):
Submitter(falcon_event).submit()
__all__ = ['Runtime']