Esempio n. 1
0
 def _create_or_update_indicator(self, indicator: Indicator):
     """
     Creates or updates a STIX-2 Indicator in OpenCTI
     @param indicator The STIX-2 Indicator
     """
     ioc_dct = json.loads(indicator.serialize())
     ioc_dct["name"] = ioc_dct.get("name", indicator.id)  #  default to UUID
     ioc_dct["stix_id"] = indicator.id
     del ioc_dct["id"]
     obs_type = ioc_dct.get("x_opencti_main_observable_type", "Unknown")
     ioc_dct["x_opencti_main_observable_type"] = obs_type
     resp = self.opencti_helper.api.indicator.create(**ioc_dct)
     self.opencti_helper.log_info(f"Created or added to indicator: {resp}")
    def test_zmq_app_plugin_message_roundtrip(self):
        """
        Backend-agnostic message passing scenario. Sends a fixed amount of
        messages via the threatbus ZeroMQ app plugin, subscribes to Threat Bus,
        and checks if the initially sent messages can be retrieved back.
        """
        result_q = queue.Queue()
        items = 2
        topics = ["stix2/indicator", "stix2/sighting"]
        rec = threading.Thread(target=zmq_receiver.forward,
                               args=(items, topics, result_q),
                               daemon=False)
        rec.start()
        ioc = Indicator(pattern_type="stix",
                        pattern="[ipv4-addr:value = '6.6.6.6']")
        zmq_sender.send(
            "stix2/indicator",
            ioc.serialize(),
            port=13372,
            bind=False,
        )
        sighting = Sighting(sighting_of_ref=ioc.id)
        zmq_sender.send(
            "stix2/sighting",
            sighting.serialize(),
            port=13372,
            bind=False,
        )
        time.sleep(1)
        self.assertEqual(result_q.qsize(), items)

        event = result_q.get(timeout=1)
        self.assertIsNotNone(event)
        self.assertEqual(parse(event), ioc)
        result_q.task_done()

        event = result_q.get(timeout=1)
        self.assertIsNotNone(event)
        self.assertEqual(parse(event), sighting)
        result_q.task_done()

        self.assertEqual(0, result_q.qsize())
        result_q.join()
        rec.join(timeout=1)
Esempio n. 3
0
 def _ingest_indicator(self, indicator: Indicator):
     """
     Ingests a STIX-2 Indicator into OpenCTI. Does nothing in case the
     indicator already exists.
     @param indicator The STIX-2 Indicator object to ingest
     """
     if type(indicator) is not Indicator:
         self.opencti_helper.log_error(
             f"Error ingesting indicator from Threat Bus. Expected a STIX-2 Indicator: {indicator}"
         )
         return
     ioc_dct = json.loads(indicator.serialize())
     ioc_dct["name"] = ioc_dct.get("name", indicator.id)  #  default to UUID
     ioc_dct["stix_id"] = indicator.id
     del ioc_dct["id"]
     obs_type = ioc_dct.get("x_opencti_main_observable_type", "Unknown")
     ioc_dct["x_opencti_main_observable_type"] = obs_type
     resp = self.opencti_helper.api.indicator.create(**ioc_dct)
     self.opencti_helper.log_info(f"Created or added to indicator: {resp}")
Esempio n. 4
0
import zmq
import time
from stix2 import Indicator, Sighting


def send(topic, msg, host="127.0.0.1", port=50000, bind=True):
    """Sends a single, user specified message"""
    socket = zmq.Context().socket(zmq.PUB)
    if bind is True:
        socket.bind(f"tcp://{host}:{port}")
        time.sleep(0.5)
    else:
        socket.connect(f"tcp://{host}:{port}")
        time.sleep(0.5)
    # print(f"send string: {topic} {msg}")
    socket.send_string(f"{topic} {msg}")
    time.sleep(0.5)


if __name__ == "__main__":
    indicator = Indicator(
        pattern="[domain-name:value = 'evil.com']", pattern_type="stix"
    )
    sighting = Sighting(
        sighting_of_ref="indicator--629a6400-8817-4bcb-aee7-8c74fc57482c",
        custom_properties={"x_threatbus_source": "VAST"},
    )
    send("stix2/indicator", indicator.serialize(), port=13372, bind=False)
    send("stix2/sighting", sighting.serialize(), port=13372, bind=False)
Esempio n. 5
0
class TestMessageMapping(unittest.TestCase):
    def setUp(self):
        self.ts = datetime.now(timezone.utc).astimezone()
        self.indicator_id = "indicator--de0c3d3f-02ee-4086-88f1-51200ac831f7"
        self.point_ioc = "evil.com"
        self.pattern = f"[domain-name:value = '{self.point_ioc}']"
        self.indicator = Indicator(
            id=self.indicator_id,
            created=self.ts,
            modified=self.ts,
            pattern_type="stix",
            pattern=self.pattern,
        )
        self.module_namespace = "TestNamespace"
        self.logger = getLogger("test")

    def test_invalid_indicator_inputs(self):
        self.assertIsNone(
            map_indicator_to_broker_event(None, None, self.logger))
        self.assertIsNone(map_indicator_to_broker_event(None, "", self.logger))
        self.assertIsNone(
            map_indicator_to_broker_event(None, self.module_namespace,
                                          self.logger))
        self.assertIsNone(
            map_indicator_to_broker_event(42, self.module_namespace,
                                          self.logger))
        self.assertIsNone(
            map_indicator_to_broker_event(object, self.module_namespace,
                                          self.logger))
        self.assertIsNone(
            map_indicator_to_broker_event(
                Sighting(sighting_of_ref=self.indicator_id),
                self.module_namespace,
                self.logger,
            ))

    def test_invalid_zeek_inputs(self):
        broker_data = broker.zeek.Event("Hello")  # unknown event
        self.assertIsNone(
            map_broker_event_to_sighting(broker_data, None, self.logger))
        self.assertIsNone(
            map_broker_event_to_sighting(broker_data, self.module_namespace,
                                         self.logger))
        self.assertIsNone(
            map_management_message(broker_data, None, self.logger))
        self.assertIsNone(
            map_management_message(broker_data, self.module_namespace,
                                   self.logger))

        # not enough arguments provided
        broker_data = broker.zeek.Event("sighting", 1, 2)
        self.assertIsNone(
            map_broker_event_to_sighting(broker_data, None, self.logger))
        self.assertIsNone(
            map_broker_event_to_sighting(broker_data, self.module_namespace,
                                         self.logger))
        self.assertIsNone(
            map_management_message(broker_data, None, self.logger))
        self.assertIsNone(
            map_management_message(broker_data, self.module_namespace,
                                   self.logger))

        broker_data = broker.zeek.Event("intel", 42, {})
        self.assertIsNone(
            map_broker_event_to_sighting(broker_data, None, self.logger))
        self.assertIsNone(
            map_broker_event_to_sighting(broker_data, self.module_namespace,
                                         self.logger))
        self.assertIsNone(
            map_management_message(broker_data, None, self.logger))
        self.assertIsNone(
            map_management_message(broker_data, self.module_namespace,
                                   self.logger))

        broker_data = broker.zeek.Event("subscribe", "topic")
        self.assertIsNone(
            map_broker_event_to_sighting(broker_data, None, self.logger))
        self.assertIsNone(
            map_broker_event_to_sighting(broker_data, self.module_namespace,
                                         self.logger))
        self.assertIsNone(
            map_management_message(broker_data, None, self.logger))
        self.assertIsNone(
            map_management_message(broker_data, self.module_namespace,
                                   self.logger))

        broker_data = broker.zeek.Event("unsubscribe")
        self.assertIsNone(
            map_broker_event_to_sighting(broker_data, None, self.logger))
        self.assertIsNone(
            map_broker_event_to_sighting(broker_data, self.module_namespace,
                                         self.logger))
        self.assertIsNone(
            map_management_message(broker_data, None, self.logger))
        self.assertIsNone(
            map_management_message(broker_data, self.module_namespace,
                                   self.logger))

    def test_valid_indicator(self):
        # test indicator added
        broker_msg = map_indicator_to_broker_event(self.indicator,
                                                   self.module_namespace, None)
        self.assertEqual(broker_msg.name(), self.module_namespace + "::intel")
        self.assertEqual(
            broker_msg.args(),
            [(self.ts, self.indicator_id, "DOMAIN", self.point_ioc, "ADD")],
        )

        # test indicator removed
        # deep copy indicator, add custom property that indicates deletion
        i_dct = json.loads(self.indicator.serialize())  # deep copy
        i_dct[ThreatBusSTIX2Constants.X_THREATBUS_UPDATE.
              value] = Operation.REMOVE.value
        indicator_copy = parse(json.dumps(i_dct), allow_custom=True)

        broker_msg = map_indicator_to_broker_event(indicator_copy,
                                                   self.module_namespace, None)
        self.assertEqual(broker_msg.name(), self.module_namespace + "::intel")
        self.assertEqual(
            broker_msg.args(),
            [(self.ts, self.indicator_id, "DOMAIN", self.point_ioc, "REMOVE")],
        )

    def test_valid_zeek_sighting(self):
        context = {"last_seen": 1234, "count": 13, "source": "Zeek"}
        # without namespace:
        event = broker.zeek.Event("sighting", self.ts, self.indicator_id,
                                  context)
        sighting = map_broker_event_to_sighting(event, self.module_namespace,
                                                None)
        self.assertEqual(type(sighting), Sighting)
        self.assertEqual(sighting.last_seen, self.ts)
        self.assertEqual(sighting.sighting_of_ref, self.indicator_id)
        self.assertTrue(ThreatBusSTIX2Constants.X_THREATBUS_SIGHTING_CONTEXT.
                        value in sighting)
        self.assertEqual(sighting.x_threatbus_sighting_context, context)

        # with namespace:
        event = broker.zeek.Event(self.module_namespace + "::sighting",
                                  self.ts, self.indicator_id, context)
        sighting = map_broker_event_to_sighting(event, self.module_namespace,
                                                None)
        self.assertEqual(type(sighting), Sighting)
        self.assertEqual(sighting.last_seen, self.ts)
        self.assertEqual(sighting.sighting_of_ref, self.indicator_id)
        self.assertTrue(ThreatBusSTIX2Constants.X_THREATBUS_SIGHTING_CONTEXT.
                        value in sighting)
        self.assertEqual(sighting.x_threatbus_sighting_context, context)

    def test_valid_subscription(self):
        td = timedelta(days=5)
        topic = "some/topic"
        expected = Subscription(topic, td)

        # without namespace
        event = broker.zeek.Event("subscribe", topic, td)
        subscription = map_management_message(event, self.module_namespace,
                                              self.logger)
        self.assertEqual(subscription, expected)

        # with namespace:
        event = broker.zeek.Event(self.module_namespace + "::subscribe", topic,
                                  td)
        subscription = map_management_message(event, self.module_namespace,
                                              self.logger)
        self.assertEqual(subscription, expected)

    def test_valid_unsubscription(self):
        topic = "some/topic"
        expected = Unsubscription(topic)

        # without namespace
        event = broker.zeek.Event("unsubscribe", topic)
        unsubscription = map_management_message(event, self.module_namespace,
                                                self.logger)
        self.assertEqual(unsubscription, expected)

        # with namespace:
        event = broker.zeek.Event(self.module_namespace + "::unsubscribe",
                                  topic)
        unsubscription = map_management_message(event, self.module_namespace,
                                                self.logger)
        self.assertEqual(unsubscription, expected)
Esempio n. 6
0
    def test_intel_sighting_roundtrip(self):
        """
        Backend-agnostic roundtrip scenario, that starts a Zeek subprocess which
        activates the threatbus.zeek "app" script.
        The test sends an IoC with a malicious hostname via Threat Bus, using
        the ZMQ app plugin. Meanwhile, the Zeek subprocess reads a PCAP trace
        which contains exactly that malicious hostname from the IoC.
        If all goes well, Zeek subscribes to Threat Bus successfully, receives
        the IoC and hence reading the PCAP file results in a sighting. Zeek
        forwards that sighting to the Threat Bus Zeek plugin, where it is
        converted to a valid STIX-2 Sighting.
        The integration test subscribes a ZMQ receiver to the `stix2/sighting`
        topic and verifies all Zeek communication was handled correctly. I.e.,
        Zeek matched the IoC and reported the correct sighting.
        """
        # Start a ZMQ receiver that subscribes to the `stix2/sighting` topic and
        # forward exactly 1 item to a result queue
        result_q = queue.Queue()
        rec = threading.Thread(
            target=zmq_receiver.forward,
            args=(1, ["stix2/sighting"], result_q),
            daemon=True,
        )
        rec.start()

        # Spawn a Zeek subprocess that runs the `apps/zeek/threatbus.zeek`
        # script and reads a prepared PCAP trace that contains a network
        # connection to `example.com`
        zeek_process = RunZeek()
        if not zeek_process:
            self.fail("Error starting Zeek container.")

        # Let Zeek start up...
        time.sleep(1)

        # Send a new indicator (IoC) via the ZMQ test-util, which will be
        # forwarded to Zeek because Zeek subscribes to `stix2/indicator`
        ioc_id = "indicator--42d31a5b-2da0-4bdd-9823-1723a98fc2fb"
        ioc = Indicator(
            id=ioc_id,
            pattern_type="stix",
            pattern="[domain-name:value = 'example.com']",
        )
        zmq_sender.send("stix2/indicator",
                        ioc.serialize(),
                        port=13372,
                        bind=False)

        # Wait for Zeek to ingest the IoC into its Intel framework, read the
        # PCAP trace and report back the sighting
        raw_msg = result_q.get(timeout=10)
        sighting = parse(raw_msg, allow_custom=True)
        result_q.task_done()

        self.assertIsNotNone(sighting)
        self.assertEqual(sighting.sighting_of_ref, ioc_id)
        self.assertTrue(ThreatBusSTIX2Constants.X_THREATBUS_SIGHTING_CONTEXT.
                        value in sighting)
        self.assertEqual(sighting.x_threatbus_sighting_context,
                         {"noisy": False})

        rec.join()
        result_q.join()
        zeek_process.kill()
        self.assertTrue(StopZeek())
Esempio n. 7
0
import pika
from stix2 import Indicator

## Dummy intel data
pattern = "[ipv4-addr:value = '6.6.6.6']"
pattern_type = "stix2"
indicator = Indicator(pattern=pattern, pattern_type=pattern_type)
indicator_json = indicator.serialize()
## rabbitmq
host = "localhost"
port = "5672"
vhost = "/"
credentials = pika.PlainCredentials("guest", "guest")
conn_params = pika.ConnectionParameters(host, port, vhost, credentials)

connection = pika.BlockingConnection(conn_params)
channel = connection.channel()

for i in range(100):
    channel.basic_publish(exchange="threatbus",
                          routing_key="",
                          body=indicator_json)