Skip to content

Commit

Permalink
add created_by_ref and opencti_observable_main_type custom property t…
Browse files Browse the repository at this point in the history
…o observables
  • Loading branch information
DNRRomero committed Sep 11, 2024
1 parent 78abfef commit 835c4f3
Show file tree
Hide file tree
Showing 11 changed files with 112 additions and 26 deletions.
6 changes: 4 additions & 2 deletions external-import/zerofox/src/collectors/collector.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,14 @@ def __init__(self, endpoint, mapper, client: ZeroFox):
self.mapper = mapper
self.client = client

def collect_intelligence(self, now: datetime, last_run_date: datetime, logger):
def collect_intelligence(
self, created_by, now: datetime, last_run_date: datetime, logger
):
stix_objects = []
missed_entries = 0
for entry in self.client.fetch_feed(self.endpoint, last_run_date):
try:
stix_data = self.mapper(now, entry)
stix_data = self.mapper(now, entry, created_by)
stix_objects += stix_data
except Exception as ex:
logger.debug(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@


def botnet_to_infrastructure(
now: str, entry: Botnet
_, now: str, entry: Botnet
) -> List[Union[Infrastructure, Location, Relationship]]:
"""
Creates a STIX Infrastructure/botnet object from a ZeroFOX Botnet object,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,22 @@
import ipaddress
from typing import List, Tuple, Union

from stix2 import Infrastructure, IPv4Address, IPv6Address, Relationship, Software
from open_cti import build_observable
from stix2 import (
Identity,
Infrastructure,
IPv4Address,
IPv6Address,
Relationship,
Software,
)
from zerofox.domain.c2Domains import C2Domain


def c2_domains_to_infrastructure(
now: str, entry: C2Domain
created_by: Identity,
now: str,
entry: C2Domain,
) -> List[Union[Infrastructure, Relationship, IPv4Address, IPv6Address]]:
"""
Creates a STIX Infrastructure/command-and-conrtol object from a ZeroFOX C2Domain object, along with
Expand All @@ -17,6 +27,7 @@ def c2_domains_to_infrastructure(

infrastructure = Infrastructure(
name=f"{entry.domain}",
created_by_ref=created_by,
labels=tags,
created=now,
first_seen=entry.created_at,
Expand All @@ -28,7 +39,7 @@ def c2_domains_to_infrastructure(
entry, infrastructure, tags_obtained_observables
)
ip_addresses = (
[build_ip_stix_object(ip) for ip in entry.ip_addresses]
[build_ip_stix_object(created_by, ip) for ip in entry.ip_addresses]
if entry.ip_addresses
else []
)
Expand Down Expand Up @@ -80,11 +91,11 @@ def _get_observables_relationships(
]


def build_ip_stix_object(ip):
def build_ip_stix_object(created_by, ip):
version = ipaddress.ip_address(ip).version
if version == 4:
return IPv4Address(value=ip)
return build_observable(created_by=created_by, cls=IPv4Address, value=ip)
elif version == 6:
return IPv6Address(value=ip)
return build_observable(created_by=created_by, cls=IPv6Address, value=ip)
else:
raise ValueError(f"Invalid IP address: {ip}")
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,15 @@


def exploit_to_tool(
now: str, entry: Exploit
created_by, now: str, entry: Exploit
) -> List[Union[Tool, Vulnerability, Relationship]]:
"""
Creates a STIX Tool/exploitation object from a ZeroFOX Exploit object, along with
- A Vulnerability object for the tagetted CVE
"""
tool = Tool(
name=f"{entry.cve}",
created_by_ref=created_by,
description=f"```{entry.exploit}```",
created=now,
external_references=[
Expand Down
19 changes: 15 additions & 4 deletions external-import/zerofox/src/collectors/mappers/malwareToMalware.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from typing import List, Union

from open_cti import build_observable
from stix2 import URL, File, Indicator
from stix2 import Malware as stixMalware
from stix2 import Relationship
Expand All @@ -8,7 +9,7 @@


def malware_to_malware(
now: str, entry: Malware
created_by, _: str, entry: Malware
) -> List[Union[URL, File, Indicator, Relationship, stixMalware]]:
"""
Based on a ZeroFox Malware object, creates the following STIX objects:
Expand All @@ -17,9 +18,16 @@ def malware_to_malware(
- A set of Malware family objects for each of the malware families associated with the malware entry
- An Indicator observable object pointing the file hash to each of the malware families it is based on
"""
urls = [URL(value=c2) for c2 in entry.c2] if entry.c2 else []
urls = (
[build_observable(created_by=created_by, cls=URL, value=c2) for c2 in entry.c2]
if entry.c2
else []
)
malware_families = (
[stixMalware(name=family, is_family=True) for family in entry.family]
[
stixMalware(name=family, is_family=True, created_by_ref=created_by)
for family in entry.family
]
if entry.family
else []
)
Expand All @@ -32,7 +40,9 @@ def malware_to_malware(
if entry.md5:
present_hashes["MD5"] = entry.md5

file = File(
file = build_observable(
created_by=created_by,
cls=File,
name=entry.sha256,
hashes={
"SHA-256": entry.sha256,
Expand All @@ -46,6 +56,7 @@ def malware_to_malware(
pattern_string += "]"

indicator = Indicator(
created_by_ref=created_by,
name=entry.sha256,
pattern_type=PATTERN_TYPE_STIX,
pattern=pattern_string,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from typing import List, Union

from open_cti import build_observable
from stix2 import (
URL,
AutonomousSystem,
Expand All @@ -11,7 +12,7 @@
from zerofox.domain.phishing import Phishing


def phishing_to_infrastructure(now: str, entry: Phishing) -> List[
def phishing_to_infrastructure(created_by, now: str, entry: Phishing) -> List[
Union[
Infrastructure,
Relationship,
Expand All @@ -30,15 +31,18 @@ def phishing_to_infrastructure(now: str, entry: Phishing) -> List[
"""
phishing = Infrastructure(
created_by_ref=created_by,
name=f"{entry.domain}",
created=now,
infrastructure_types=["phishing"],
first_seen=entry.scanned,
external_references=[],
)
certificate_objects = build_certificate_objects(entry, phishing)
certificate_objects = build_certificate_objects(created_by, entry, phishing)

url = URL(
url = build_observable(
created_by=created_by,
cls=URL,
value=entry.url,
)

Expand All @@ -49,7 +53,9 @@ def phishing_to_infrastructure(now: str, entry: Phishing) -> List[
start_time=entry.scanned,
)

ip = IPv4Address(
ip = build_observable(
created_by=created_by,
cls=IPv4Address,
value=entry.host.ip,
)

Expand All @@ -60,7 +66,9 @@ def phishing_to_infrastructure(now: str, entry: Phishing) -> List[
start_time=entry.scanned,
)

asn = AutonomousSystem(
asn = build_observable(
created_by=created_by,
cls=AutonomousSystem,
number=entry.host.asn,
)

Expand All @@ -82,10 +90,12 @@ def phishing_to_infrastructure(now: str, entry: Phishing) -> List[
] + certificate_objects


def build_certificate_objects(entry: Phishing, stix_phishing):
def build_certificate_objects(created_by, entry: Phishing, stix_phishing):
if not entry.cert or not entry.cert.authority:
return []
certificate = X509Certificate(
certificate = build_observable(
created_by=created_by,
cls=X509Certificate,
issuer=entry.cert.authority,
hashes={"SHA-1": entry.cert.fingerprint} if entry.cert.fingerprint else None,
)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from typing import List, Union

from open_cti import build_observable
from stix2 import File, Indicator
from stix2 import Malware as stixMalware
from stix2 import Relationship
Expand All @@ -8,7 +9,7 @@


def ransomware_to_malware(
now: str, entry: Ransomware
created_by, now: str, entry: Ransomware
) -> List[Union[Relationship, Indicator, File, stixMalware]]:
"""
Based on a ZeroFox Ransomware object, creates the following STIX objects:
Expand All @@ -27,6 +28,7 @@ def ransomware_to_malware(

malware = stixMalware(
name=f"{ransomware_name}",
created_by_ref=created_by,
description=f"```{entry.ransom_note}```",
labels=[tag for tag in entry.tags if not tag.startswith("family:")],
first_seen=entry.created_at,
Expand All @@ -44,7 +46,9 @@ def ransomware_to_malware(
if entry.md5:
present_hashes["MD5"] = entry.md5

file = File(
file = build_observable(
created_by=created_by,
cls=File,
name=entry.sha256,
hashes={
"SHA-256": entry.sha256,
Expand All @@ -59,6 +63,7 @@ def ransomware_to_malware(

indicator = Indicator(
name=entry.sha256,
created_by_ref=created_by,
pattern_type=PATTERN_TYPE_STIX,
pattern=pattern_string,
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@


def vulnerability_to_vulnerability(
now: str, entry: Vulnerability
created_by, now: str, entry: Vulnerability
) -> List[StixVulnerability]:
"""
Based on a ZeroFox Vulnerability object, creates a STIX Vulnerability object.
Expand All @@ -16,6 +16,7 @@ def vulnerability_to_vulnerability(

vulnerability = StixVulnerability(
name=entry.cve,
created_by_ref=created_by,
description=f"{entry.description}{summary}{remediation}",
external_references=[
ExternalReference(source_name="cve", external_id=entry.cve)
Expand Down
15 changes: 13 additions & 2 deletions external-import/zerofox/src/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ def __init__(self):
feeds=os.environ.get("ZEROFOX_COLLECTORS", None),
logger=self.helper.connector_logger,
)
self.author = stix2.Identity(
name="ZeroFox Connector",
identity_class="organization",
)

def _validate_interval(self, env_var, interval):
self.helper.log_info(
Expand Down Expand Up @@ -142,9 +146,13 @@ def collect_intelligence_for_endpoint(
self.helper.log_debug(
f"{self.helper.connect_name} connector is starting the collection of objects..."
)
self.send_bundle(work_id=work_id, bundle_objects=[self.author])
self.helper.log_info(f"Running collector: {collector_name}")
missed_entries, bundle_objects = collector.collect_intelligence(
now, datetime.fromtimestamp(last_run, UTC), self.helper.connector_logger
created_by=self.author,
now=now,
last_run_date=datetime.fromtimestamp(last_run, UTC),
logger=self.helper.connector_logger,
)
if missed_entries > 0:
self.helper.log_warning(
Expand Down Expand Up @@ -201,7 +209,10 @@ def run(self) -> None:
)

self.collect_intelligence_for_endpoint(
current_time, last_run, collector_name, collector
current_time=current_time,
last_run=last_run,
collector_name=collector_name,
collector=collector,
)

except (KeyboardInterrupt, SystemExit):
Expand Down
3 changes: 3 additions & 0 deletions external-import/zerofox/src/open_cti/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from open_cti.observables import build_observable

__all__ = ["build_observable"]
31 changes: 31 additions & 0 deletions external-import/zerofox/src/open_cti/observables.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
from typing import Type

from stix2 import Identity
from stix2.base import _Observable

main_observable_type = "x_opencti_main_observable_type"
created_by_ref = "x_opencti_created_by_ref"


def _get_observable_type(cls: Type[_Observable]) -> str:
_type = {
"IPv4-Address": "IPv4-Addr",
"IPv6-Address": "IPv6-Addr",
"File": "StixFile",
"URL": "Url",
"AutonomousSystem": "Autonomous-System",
"X509Certificate": "X509-Certificate",
}.get(cls.__name__)
if not _type:
raise ValueError(f"Unsupported observable type: {cls.__name__}")
return _type


def build_observable(created_by: Identity, cls: Type[_Observable], **kwargs):
return cls(
**kwargs,
custom_properties={
main_observable_type: _get_observable_type(cls),
created_by_ref: created_by,
},
)

0 comments on commit 835c4f3

Please sign in to comment.