Skip to content
Open
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
2 changes: 1 addition & 1 deletion connectors-sdk/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ requires-python = ">=3.11, <3.13"

dependencies = [
"limits>=5.0,<6",
"pycti==7.260715.0",
"pycti==7.260722.0",
"pydantic>=2.8.2,<3", # ISO pycti https://github.com/OpenCTI-Platform/client-python/blob/master/requirements.txt#L8
"pydantic_settings>= 2.9.1,<3", # Actual minimal version used in the connectors
"requests>=2.31.0,<3",
Expand Down
19 changes: 15 additions & 4 deletions external-import/malwarebazaar/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

| Status | Date | Comment |
|--------|------|---------|
| Community | - | - |
| Verified | - | - |

The MalwareBazaar connector imports malware samples and their hashes from abuse.ch MalwareBazaar into OpenCTI.

Expand All @@ -29,7 +29,7 @@ The MalwareBazaar connector imports malware samples and their hashes from abuse.

MalwareBazaar is a project from abuse.ch with the goal of sharing malware samples with the infosec community, AV vendors, and threat intelligence providers. The platform collects malware samples submitted by security researchers and organizations.

This connector fetches recent malware sample metadata from MalwareBazaar API and imports them as File observables with associated hashes (MD5, SHA-1, SHA-256) into OpenCTI.
This connector fetches recent malware sample metadata from MalwareBazaar API and imports them as File observables with associated hashes (MD5, SHA-1, SHA-256), along with a related Indicator and, when available, the detected Malware family, into OpenCTI.

## Installation

Expand Down Expand Up @@ -124,11 +124,14 @@ graph LR
subgraph OpenCTI
direction LR
File[File Observable]
Indicator[Indicator]
Malware[Malware]
end

API --> File
File -- related-to --> Malware
API --> Indicator
Indicator -- based-on --> File
Indicator -- indicates --> Malware
```

### Entity Mapping
Expand All @@ -141,7 +144,9 @@ graph LR
| file_name | File.name | Original filename |
| file_size | File.size | File size in bytes |
| file_type | File.mime_type | MIME type |
| sha256_hash | Indicator | STIX indicator based on the SHA-256 hash |
| signature | Malware | Detected malware family |
| sha256_hash | External Reference | Link to the MalwareBazaar sample page |
| tags | Labels | MalwareBazaar tags |
| reporter | Description | Sample submitter |

Expand All @@ -152,8 +157,14 @@ graph LR
- `x_opencti_score`: Default 100 (high confidence)
- Labels from configuration
- TLP marking
- External reference to the MalwareBazaar sample page

2. **Malware Relationship**: If signature is available, creates relationship to malware family
2. **Indicator**: A STIX indicator is created from the SHA-256 hash, linked to the File
observable with a `based-on` relationship, and carries the same TLP marking and an
external reference to the MalwareBazaar sample page.

3. **Malware**: If a signature (malware family) is available, a Malware entity is created
and linked to the Indicator with an `indicates` relationship.

### Filtering Options

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,11 @@ def _collect_intelligence(self) -> list:
stix_objects.append(entity_set_to_stix["FILE_OBSERVABLE"])
stix_objects.append(entity_set_to_stix["INDICATOR_4_FILE"])
stix_objects.append(entity_set_to_stix["FILE_2_INDICATOR_RELATIONSHIP"])
if entity_set_to_stix["MALWARE"] is not None:
stix_objects.append(entity_set_to_stix["MALWARE"])
stix_objects.append(
entity_set_to_stix["INDICATOR_2_MALWARE_RELATIONSHIP"]
)
if len(stix_objects):
stix_objects.append(self.converter_to_stix.author)
stix_objects.append(self.converter_to_stix.tlp_marking)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
import stix2
import validators
from pycti import Identity, Indicator, MarkingDefinition, StixCoreRelationship
from pycti import (
Identity,
Indicator,
Malware,
MarkingDefinition,
StixCoreRelationship,
)


class ConverterToStix:
Expand Down Expand Up @@ -57,13 +63,27 @@ def _create_tlp_marking(level) -> str:
}
return mapping[level]

@staticmethod
def create_external_reference(sha256_hash: str) -> stix2.ExternalReference:
"""
Create an External Reference pointing to the MalwareBazaar sample page
:param sha256_hash: SHA256 hash of the sample
:return: External Reference STIX2 object
"""
return stix2.ExternalReference(
source_name="MalwareBazaar",
url=f"https://bazaar.abuse.ch/sample/{sha256_hash}/",
description="MalwareBazaar sample page",
)

def create_file_observable(self, entity: dict, labels: list) -> dict:
"""
Creates File Observable object based on MalwareBazaar Entity Data
:param entity: entity dict from MalwareBazaar
:param labels: List of Labels/Tags to apply
:return: File Observable STIX2 object
"""
external_reference = self.create_external_reference(entity["sha256_hash"])
file_observable = stix2.File(
name=entity["file_name"],
size=entity["file_size"],
Expand All @@ -74,10 +94,10 @@ def create_file_observable(self, entity: dict, labels: list) -> dict:
"SHA-256": entity["sha256_hash"],
# "SHA-512": entity["sha512_hash"], # API for MalwareBazaar currently does NOT provide a SHA512
},
object_marking_refs=self.tlp_marking,
object_marking_refs=[self.tlp_marking],
custom_properties={
"x_opencti_created_by_ref": self.author["id"],
# "x_opencti_external_references": self.external_reference,
"x_opencti_external_references": [external_reference],
"x_opencti_additional_names": [
entity["sha256_hash"],
# entity["file_name"],
Expand All @@ -99,6 +119,7 @@ def create_indicator(self, entity: dict, labels: list) -> dict:
"""
indicator_pattern = f"[file:hashes.'SHA-256' = '{entity['sha256_hash']}']"
indicator_description = f"Indicator for hash SHA256 {entity['sha256_hash']}"
external_reference = self.create_external_reference(entity["sha256_hash"])
indicator = stix2.Indicator(
id=Indicator.generate_id(indicator_pattern),
name=f"{entity['sha256_hash']}",
Expand All @@ -107,7 +128,8 @@ def create_indicator(self, entity: dict, labels: list) -> dict:
description=indicator_description,
labels=labels,
created_by_ref=self.author["id"],
object_marking_refs=self.tlp_marking,
object_marking_refs=[self.tlp_marking],
external_references=[external_reference],
custom_properties={
"x_opencti_score": int(self.config.malwarebazaar.x_opencti_score),
"x_opencti_main_observable_type": "File",
Expand All @@ -116,6 +138,21 @@ def create_indicator(self, entity: dict, labels: list) -> dict:

return indicator

def create_malware(self, signature: str) -> dict:
"""
Creates Malware object based on MalwareBazaar signature (malware family)
:param signature: Malware family/signature name from MalwareBazaar
:return: Malware STIX2 object
"""
malware = stix2.Malware(
id=Malware.generate_id(signature),
name=signature,
is_family=True,
created_by_ref=self.author["id"],
object_marking_refs=[self.tlp_marking]
)
return malware

def create_relationship(
self, source_id: str, relationship_type: str, target_id: str, labels: list
) -> dict:
Expand All @@ -136,7 +173,7 @@ def create_relationship(
target_ref=target_id,
labels=labels,
created_by_ref=self.author["id"],
object_marking_refs=self.tlp_marking,
object_marking_refs=[self.tlp_marking]
# external_references=self.external_reference,
)
return relationship
Expand Down Expand Up @@ -199,6 +236,8 @@ def create_obs(self, entity: dict) -> dict:
"FILE_OBSERVABLE": None,
"INDICATOR_4_FILE": None,
"FILE_2_INDICATOR_RELATIONSHIP": None,
"MALWARE": None,
"INDICATOR_2_MALWARE_RELATIONSHIP": None,
}
if (
self._is_valid_md5(entity["md5_hash"]) is True
Expand Down Expand Up @@ -234,8 +273,24 @@ def create_obs(self, entity: dict) -> dict:
"FILE_OBSERVABLE": file_observable,
"INDICATOR_4_FILE": indicator,
"FILE_2_INDICATOR_RELATIONSHIP": relationship,
"MALWARE": None,
"INDICATOR_2_MALWARE_RELATIONSHIP": None,
}

# Create Malware from the MalwareBazaar signature (malware family) if present
signature = entity.get("signature")
if signature and len(signature) >= 1:
malware = self.create_malware(signature=signature)
indicator_2_malware = self.create_relationship(
source_id=indicator["id"],
relationship_type="indicates",
target_id=malware["id"],
labels=tags_as_labels,
)
file_observable_set["MALWARE"] = malware
file_observable_set["INDICATOR_2_MALWARE_RELATIONSHIP"] = (
indicator_2_malware
)
else:
self.helper.connector_logger.error(
"This observable entity is not a valid for ingest, missing one or more required fields (md5_hash, sha1_hash, sha256_hash, reporter, or filename): ",
Expand Down
2 changes: 1 addition & 1 deletion external-import/malwarebazaar/src/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
pycti==7.260715.0
pycti==7.260722.0
requests~=2.33.0
stix2==3.0.1
validators==0.35.0
Expand Down