Example #1
0
def create():
    # Start measuring execution time for evaluation purposes
    start_time = time.time()

    # Get STIX 2.1 bundle in json format
    data = request.get_json() or {}

    # Validate input
    validation = results = validate_instance(data)

    # Return errors if not valid STIX 2.1 format
    if not validation.is_valid:
        return bad_request(validation.errors[0].message)

    # Initialize kafka broker
    producer = KafkaProducer(
        bootstrap_servers=[kafka_broker],
        value_serializer=lambda x: dumps(x).encode('utf-8'))

    # Send content to broker
    producer.send(kafka_topic, value=data)

    # Finish measuring execution time for evaluation purposes
    end_time = time.time()
    ellapsed_time = end_time - start_time

    # Return status code
    print("--- %s seconds ---" % (ellapsed_time))
    return {'message': 'Success', 'time': ellapsed_time}
Example #2
0
    def create_results_connection(self, search_id, offset, length):
        #search_id is the pattern
        observations = []

        if "http_user" in self.configuration:
            response = requests.get(self.configuration["bundle_url"],
                                    auth=(self.configuration["http_user"],
                                          self.configuration["http_password"]))
        else:
            response = requests.get(self.configuration["bundle_url"])

        if response.status_code != 200:
            response.raise_for_status()

        bundle = response.json()

        if "validate" in self.configuration and self.configuration[
                "validate"] is True:
            results = validate_instance(bundle)

            if results.is_valid is not True:
                return {
                    "success": False,
                    "message": "Invalid STIX recieved: " + json.dumps(results)
                }

        for obj in bundle["objects"]:
            if obj["type"] == "observed-data":
                observations.append(obj)

        #Pattern match
        results = self.match(search_id, observations, False)

        return results[int(offset):int(offset + length)]
Example #3
0
    def transform(self, obj):
        """
        Transforms the given object in to a STIX observation based on the mapping file and transform functions

        :param obj: the datasource object that is being converted to stix
        :return: the input object converted to stix valid json
        """
        object_map = {}
        stix_type = 'observed-data'
        ds_map = self.ds_to_stix_map

        observation = {
            'id': stix_type + '--' + str(uuid.uuid4()),
            'type': stix_type,
            'created_by_ref': self.identity_id,
            'objects': {}
        }

        # create normal type objects
        if isinstance(obj, dict):
            for ds_key in obj.keys():
                self._transform(object_map, observation, ds_map, ds_key, obj)
        else:
            print("Not a dict: {}".format(obj))

        # Validate each STIX object
        if self.stix_validator:
            validated_result = validate_instance(observation)
            print_results(validated_result)

        return observation
    def create_infrastructure_object_sdo(self, infrastructure_object, enriched_ioc, indicator_id):
        try:       
            stix_type = 'infrastructure'
            DEFAULT_SPEC_VERSION = "2.1"
            now = "{}Z".format(datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%S.%f')[:-3])
            infrastructure = {                      
                    'type': stix_type,
                    'spec_version' : DEFAULT_SPEC_VERSION,
                    'id': stix_type + '--' + str(uuid.uuid4()),            
                    'created': now,
                    'modified': now,            
                    'name': 'Infrastructure related to ' + enriched_ioc,
                    'infrastructure_types': infrastructure_object['infrastructure_types'],                   
                    'description' : infrastructure_object['description'] if infrastructure_object.get('description') is not None else ','.join(infrastructure_object.get('infrastructure_types'))
                }
            infrastructure_types = self.normalized_infra_type(infrastructure['infrastructure_types'])
            infrastructure['infrastructure_types'] = infrastructure_types

            if self.stix_validator:
                options = ValidationOptions(version="2.1")
                results = validate_instance(infrastructure, options)            
                if results.is_valid is False:
                    print_results(results)
                    raise Exception(f'Invalid parameter set in infrastructure SDO. Please follow STIX 2.1 spec for properties') 

            infrastructure_array = [infrastructure]
            relationship = self.createRelationship(infrastructure_array, indicator_id)
            infrastructure_array += relationship
            return infrastructure_array
        except Exception as err:
            raise Exception(f'Exception occurred in create_infrastructure_object_sdo : {err}')
    def create_identity_sdo(self, data_source, namespace):
        try:
            DETERMINISTIC_IDENTITY_ID = uuid.uuid5(uuid.UUID(namespace), data_source['name'])      
            DEFAULT_SPEC_VERSION = '2.1'
            stix_type = 'identity'
            now = "{}Z".format(datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%S.%f')[:-3])
            stix_identity_sdo = {
                'type': stix_type,
                'name': data_source['name'],
                'spec_version': DEFAULT_SPEC_VERSION,
                'id': stix_type + '--' + str(DETERMINISTIC_IDENTITY_ID),            
                'created': now,
                'modified': now,
            }

            if data_source.get('description'): stix_identity_sdo['description'] = data_source['description']
            if data_source.get('roles'): stix_identity_sdo['roles'] = data_source['roles']
            if data_source.get('identity_class'): stix_identity_sdo['identity_class'] = data_source['identity_class']
            if data_source.get('sectors'): stix_identity_sdo['sectors'] = data_source['sectors']
            if data_source.get('sectors'): stix_identity_sdo['sectors'] = data_source['sectors']
            if data_source.get('contact_information'): stix_identity_sdo['contact_information'] = data_source['contact_information']

            if self.stix_validator:
                options = ValidationOptions(version="2.1")
                results = validate_instance(stix_identity_sdo, options)          
                if results.is_valid is False:            
                    print_results(results)
                    raise Exception(f'Invalid parameter set in identity SDO. Please follow STIX 2.1 spec for properties') 

            return [stix_identity_sdo]
        
        except Exception as err:
            raise Exception(f'Exception occurred in create_identity_sdo in BaseNormalization : {err}')  
Example #6
0
    def test_certificate_cim_to_stix(self):
        count = 1
        time = "2018-08-21T15:11:55.000+00:00"
        serial = "1234"
        version = "1"
        sig_algorithm = "md5WithRSAEncryption"
        key_algorithm = "rsaEncryption"
        issuer = "C=US, ST=California, O=www.example.com, OU=new, CN=new"
        subject = "C=US, ST=Maryland, L=Baltimore, O=John Doe, OU=ExampleCorp, CN=www.example.com/[email protected]"
        ssl_hash = "aec070645fe53ee3b3763059376134f058cc337247c978add178b6ccdfb0019f"

        data = {
            "event_count": count,
            "_time": time,
            "ssl_serial": serial,
            "ssl_version": version,
            "ssl_signature_algorithm": sig_algorithm,
            "ssl_issuer": issuer,
            "ssl_subject": subject,
            "ssl_hash": ssl_hash,
            "ssl_publickey_algorithm": key_algorithm
        }

        result_bundle = json_to_stix_translator.convert_to_stix(
            data_source, map_data, [data], get_module_transformers(MODULE),
            options)

        assert (result_bundle['type'] == 'bundle')
        result_bundle_objects = result_bundle['objects']
        observed_data = result_bundle_objects[1]

        validated_result = validate_instance(observed_data)
        assert (validated_result.is_valid == True)

        assert ('objects' in observed_data)
        objects = observed_data['objects']

        # Test objects in Stix observable data model after transform
        cert_obj = TestTransform.get_first_of_type(objects.values(),
                                                   'x509-certificate')

        assert (cert_obj is not None), 'x509-certificate object type not found'
        assert (cert_obj.keys() == {
            'type', 'serial_number', 'version', "signature_algorithm",
            "subject_public_key_algorithm", "issuer", "subject", "hashes"
        })
        assert (cert_obj['serial_number'] == "1234")
        assert (cert_obj['version'] == "1")
        assert (cert_obj['signature_algorithm'] == "md5WithRSAEncryption")
        assert (cert_obj['issuer'] ==
                "C=US, ST=California, O=www.example.com, OU=new, CN=new")
        assert (
            cert_obj['subject'] ==
            "C=US, ST=Maryland, L=Baltimore, O=John Doe, OU=ExampleCorp, CN=www.example.com/[email protected]"
        )
        assert (cert_obj['subject_public_key_algorithm'] == "rsaEncryption")
        assert (
            cert_obj['hashes']['SHA-256'] ==
            "aec070645fe53ee3b3763059376134f058cc337247c978add178b6ccdfb0019f")
        assert (objects.keys() == set(map(str, range(0, 1))))
    def create_sighting_sdo(self, sighting_object, indicator_id):
        try:
            stix_type = 'sighting'
            DEFAULT_SPEC_VERSION = "2.1"
            now = "{}Z".format(datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%S.%f')[:-3])
            
            sighting = {                      
                    'type': stix_type,
                    'spec_version' : DEFAULT_SPEC_VERSION,
                    'id': stix_type + '--' + str(uuid.uuid4()),
                    'sighting_of_ref': indicator_id,            
                    'count': sighting_object['count'],
                    'created': now,
                    'modified': now
                }        

            if self.stix_validator:
                options = ValidationOptions(version="2.1")
                results = validate_instance(sighting, options)            
                if results.is_valid is False:
                    print_results(results)
                    raise Exception(f'Invalid parameter set in sighting SDO. Please follow STIX 2.1 spec for properties') 

            return [sighting]
        except Exception as err:
            raise Exception(f'Exception occurred in create_sighting_sdo in BaseNormalization : {err}')  
Example #8
0
    def test_network_cim_to_stix(self):
        count = 2
        time = "2018-08-21T15:11:55.000+00:00"
        user = "******"
        dest_ip = "127.0.0.1"
        dest_port = "8090"
        src_ip = "2001:0db8:85a3:0000:0000:8a2e:0370:7334"
        src_port = "8080"
        transport = "http"

        data = {
            "event_count": count,
            "_time": time,
            "user": user,
            "dest_ip": dest_ip,
            "dest_port": dest_port,
            "src_ip": src_ip,
            "src_port": src_port,
            "protocol": transport
        }
        print(data)
        result_bundle = json_to_stix_translator.convert_to_stix(
            data_source, map_data, [data], get_module_transformers(MODULE),
            options)

        assert (result_bundle['type'] == 'bundle')

        result_bundle_objects = result_bundle['objects']
        observed_data = result_bundle_objects[1]

        validated_result = validate_instance(observed_data)
        assert (validated_result.is_valid == True)
        assert ('objects' in observed_data)
        objects = observed_data['objects']

        nt_obj = TestTransform.get_first_of_type(objects.values(),
                                                 'network-traffic')
        assert (nt_obj is not None), 'network-traffic object type not found'
        assert (nt_obj.keys() == {
            'type', 'src_port', 'dst_port', 'src_ref', 'dst_ref', 'protocols'
        })
        assert (nt_obj['src_port'] == 8080)
        assert (nt_obj['dst_port'] == 8090)
        assert (nt_obj['protocols'] == ['http'])

        ip_ref = nt_obj['dst_ref']
        assert (ip_ref
                in objects), f"dst_ref with key {nt_obj['dst_ref']} not found"
        ip_obj = objects[ip_ref]
        assert (ip_obj.keys() == {'type', 'value'})
        assert (ip_obj['type'] == 'ipv4-addr')
        assert (ip_obj['value'] == dest_ip)

        ip_ref = nt_obj['src_ref']
        assert (ip_ref
                in objects), f"src_ref with key {nt_obj['src_ref']} not found"
        ip_obj = objects[ip_ref]
        assert (ip_obj.keys() == {'type', 'value'})
        assert (ip_obj['type'] == 'ipv6-addr')
        assert (ip_obj['value'] == src_ip)
    def test_email_cim_to_stix(self):

        tag = "email"
        count = 3
        time = "2018-08-21T15:11:55.000+00:00"
        src_user = "******"
        subject = "Test Subject"
        multi = "False"

        data = {
            "tag": tag,
            "event_count": count,
            "_time": time,
            "src_user": src_user,
            "subject": subject,
            "is_multipart": multi
        }

        result_bundle = cim_to_stix_translator.convert_to_stix(
            data_source, map_data, [data], transformers.get_all_transformers(),
            options)

        assert (result_bundle['type'] == 'bundle')

        result_bundle_objects = result_bundle['objects']
        observed_data = result_bundle_objects[1]

        validated_result = validate_instance(observed_data)
        assert (validated_result.is_valid == True)

        assert ('objects' in observed_data)
        objects = observed_data['objects']

        msg_obj = TestTransform.get_first_of_type(objects.values(),
                                                  'email-message')
        assert (msg_obj is not None), 'email-message object type not found'
        assert (msg_obj.keys() == {
            'type', 'subject', 'sender_ref', 'from_ref', 'is_multipart'
        })
        assert (msg_obj['subject'] == "Test Subject")
        assert (msg_obj['is_multipart'] == False)

        sender_ref = msg_obj['sender_ref']
        assert (sender_ref in objects
                ), f"sender_ref with key {msg_obj['sender_ref']} not found"

        addr_obj = objects[sender_ref]
        assert (addr_obj.keys() == {'type', 'value'})
        assert (addr_obj['type'] == 'email-addr')
        assert (addr_obj['value'] == src_user)

        from_ref = msg_obj['from_ref']
        assert (
            sender_ref
            in objects), f"from_ref with key {msg_obj['from_ref']} not found"

        addr_obj = objects[from_ref]
        assert (addr_obj.keys() == {'type', 'value'})
        assert (addr_obj['type'] == 'email-addr')
        assert (addr_obj['value'] == src_user)
Example #10
0
    def create_results_connection(self, search_id, offset, length):
        # search_id is the pattern
        observations = []
        return_obj = dict()

        response = self.client.call_api(self.bundle_url,
                                        'get',
                                        timeout=self.timeout)

        if response.code != 200:
            response_txt = response.raise_for_status()
            if ErrorResponder.is_plain_string(response_txt):
                ErrorResponder.fill_error(return_obj, message=response_txt)
            elif ErrorResponder.is_json_string(response_txt):
                response_json = json.loads(response_txt)
                ErrorResponder.fill_error(return_obj, response_json,
                                          ['reason'])
            else:
                raise UnexpectedResponseException
        else:
            try:
                response_txt = response.read().decode('utf-8')
                bundle = json.loads(response_txt)

                if "stix_validator" in self.connection[
                        'options'] and self.connection['options'].get(
                            "stix_validator") is True:
                    results = validate_instance(bundle)

                    if results.is_valid is not True:
                        ErrorResponder.fill_error(
                            return_obj,
                            message='Invalid Objects in STIX Bundle.')
                        return return_obj

                for obj in bundle["objects"]:
                    if obj["type"] == "observed-data":
                        observations.append(obj)

                # Pattern match
                try:
                    results = self.match(search_id, observations, False)

                    if len(results) != 0:
                        return_obj['success'] = True
                        return_obj['data'] = results[int(offset):int(offset +
                                                                     length)]
                    else:
                        return_obj['success'] = True
                        return_obj['data'] = []
                except Exception as ex:
                    ErrorResponder.fill_error(
                        return_obj,
                        message='Object matching error: ' + str(ex))
            except Exception as ex:
                ErrorResponder.fill_error(
                    return_obj,
                    message='Invalid STIX bundle. Malformed JSON: ' + str(ex))
        return return_obj
Example #11
0
    def create_results_connection(self, search_id, offset, length):
        # search_id is the pattern
        observations = []
        return_obj = dict()

        bundle_url = self.connection.get('host')
        auth = self.configuration.get('auth')

        if auth is not None:
            response = requests.get(bundle_url,
                                    auth=(auth.get('username'),
                                          auth.get('password')))
        else:
            response = requests.get(bundle_url)

        response_code = response.status_code

        if response_code != 200:
            response_txt = response.raise_for_status()
            if ErrorResponder.is_plain_string(response_txt):
                ErrorResponder.fill_error(return_obj, message=response_txt)
            elif ErrorResponder.is_json_string(response_txt):
                response_json = json.loads(response_txt)
                ErrorResponder.fill_error(return_obj, response_json,
                                          ['reason'])
            else:
                raise UnexpectedResponseException
        else:
            bundle = response.json()

            if "validate" in self.configuration and self.configuration[
                    "validate"] is True:
                results = validate_instance(bundle)

                if results.is_valid is not True:
                    return {
                        "success": False,
                        "message":
                        "Invalid STIX received: " + json.dumps(results)
                    }

            for obj in bundle["objects"]:
                if obj["type"] == "observed-data":
                    observations.append(obj)

            # Pattern match
            results = self.match(search_id, observations, False)

            if len(results) != 0:
                return_obj['success'] = True
                return_obj['data'] = results[int(offset):int(offset + length)]
            else:
                return_obj['success'] = True
                return_obj['data'] = []

        return return_obj
Example #12
0
def bundle_analysis(db, bundle, collection_id):
    if bundle['type'] == 'bundle':
        status = {
            "id": str(uuid.uuid4()),
            "status": None,
            "request_timestamp": datetime.utcnow(),
            "total_count": len(bundle['objects']),
            "success_count": 0,
            "successes": [],
            "failure_count": 0,
            "failures": [],
            "pending_count": 0,
            "pendings": []
        }
        for object in bundle['objects']:
            results = validate_instance(object)
            if results.is_valid:
                date_added = datetime.utcnow()
                print(object["id"])
                manifest = {
                    "id": object["id"],
                    "date_added": date_added,
                    "versions": [],
                    "media_types":
                    ["application/vnd.oasis.stix+json; version=2.0"],
                    "_collection_id": collection_id
                }
                manifest['versions'].append(date_added)
                # TODO check if object exists and update manifest instead of adding new
                db.manifest.insert_one(manifest)
                status["success_count"] += 1
                status["successes"].append(object["id"])
                # insert collection_id field and insert object in database
                object['_collection_id'] = collection_id
                db.objects.insert_one(object)
            else:
                status["failure_count"] += 1
                status["failures"].append(object["id"])
        status["status"] = 'complete'
        db.status.insert_one(status)
        status.pop('_id')
        resp = jsonify(status)
        resp.status_code = 200
        return resp
    else:
        # Respond that submitted input is not a STIX2 bundle
        debugstr = "Submitted Input is not a valid STIX bundle"
        message = {
            'status': 422,
            'message': debugstr,
        }
        resp = jsonify(message)
        resp.status_code = 404
        return resp
    def create_results_connection(self, search_id, offset, length):
        observations = []
        return_obj = dict()

        response = None
        if self.connection['options'].get('error_type') == ERROR_TYPE_TIMEOUT:
            # httpstat.us/200?sleep=60000 for slow connection that is valid
            self.client.call_api('https://httpstat.us/200?sleep=60000', 'get', timeout=self.timeout)
        elif self.connection['options'].get('error_type') == ERROR_TYPE_BAD_CONNECTION:
            # www.google.com:81 for a bad connection that will timeout
            response = self.client.call_api('https://www.google.com:81', 'get', timeout=self.timeout)
        if not response:
            response = self.client.call_api(self.bundle_url, 'get', timeout=self.timeout)
        if response.code != 200:
            response_txt = response.raise_for_status()
            if ErrorResponder.is_plain_string(response_txt):
                ErrorResponder.fill_error(return_obj, message=response_txt, connector=self.connector)
            elif ErrorResponder.is_json_string(response_txt):
                response_json = json.loads(response_txt)
                ErrorResponder.fill_error(return_obj, response_json, ['reason'], connector=self.connector)
            else:
                raise UnexpectedResponseException
        else:
            try:
                response_txt = response.read().decode('utf-8')
                bundle = json.loads(response_txt)

                if "stix_validator" in self.connection['options'] and self.connection['options'].get("stix_validator") is True:
                    results = validate_instance(bundle)

                    if results.is_valid is not True:
                        ErrorResponder.fill_error(return_obj,  message='Invalid Objects in STIX Bundle.', connector=self.connector)
                        return return_obj

                for obj in bundle["objects"]:
                    if obj["type"] == "observed-data":
                        observations.append(obj)

                # Pattern match
                try:
                    results = self.match(search_id, observations, False)

                    if len(results) != 0:
                        return_obj['success'] = True
                        return_obj['data'] = results[int(offset):int(offset + length)]
                    else:
                        return_obj['success'] = True
                        return_obj['data'] = []
                except Exception as ex:
                    ErrorResponder.fill_error(return_obj,  message='Object matching error: ' + str(ex), connector=self.connector)
            except Exception as ex:
                ErrorResponder.fill_error(return_obj,  message='Invalid STIX bundle. Malformed JSON: ' + str(ex), connector=self.connector)
        return return_obj
Example #14
0
    def transform(self, obj):
        """
        Transforms the given object in to a STIX observation based on the mapping file and transform functions
        :param obj: the datasource object that is being converted to stix
        :return: the input object converted to stix valid json
        """
        NUMBER_OBSERVED_KEY = 'number_observed'
        object_map = {}
        stix_type = 'observed-data'
        ds_map = self.ds_to_stix_map

        observation = {
            'id':
            stix_type + '--' + str(uuid.uuid4()),
            'type':
            stix_type,
            'created_by_ref':
            self.identity_id,
            'created':
            "{}Z".format(
                datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%S.%f')[:-3]),
            'modified':
            "{}Z".format(
                datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%S.%f')[:-3]),
            'objects': {}
        }

        # create normal type objects
        if isinstance(obj, dict):
            for ds_key in obj.keys():
                self._transform(object_map, observation, ds_map, ds_key, obj)
        else:
            self.logger.debug("Not a dict: {}".format(obj))

        # Add required property to the observation if it wasn't added via the mapping
        if self.options.get('unmapped_fallback'):
            if "first_observed" not in observation and "last_observed" not in observation:
                observation['first_observed'] = "{}Z".format(
                    datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%S.%f')[:-3])
                observation['last_observed'] = "{}Z".format(
                    datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%S.%f')[:-3])

        # Add required property to the observation if it wasn't added via the mapping
        if NUMBER_OBSERVED_KEY not in observation:
            observation[NUMBER_OBSERVED_KEY] = 1

        # Validate each STIX object
        if self.stix_validator:
            validated_result = validate_instance(observation)
            print_results(validated_result)

        return observation
    def create_malware_sdo(self,malware_object, indicator_id, enriched_ioc):        
        try:        
            malware_array=[]  
            if isinstance(malware_object, list):                
                for data in malware_object:
                    #print(data)            
                    stix_type = 'malware'
                    DEFAULT_SPEC_VERSION = "2.1"
                    now = "{}Z".format(datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%S.%f')[:-3])            
                    malware = {                      
                            'type': stix_type,
                            'name': data.get('name') if data.get('name') is not None else 'Malware related to ' + enriched_ioc,                         
                            'spec_version': DEFAULT_SPEC_VERSION,
                            'id': stix_type + '--' + str(uuid.uuid4()),            
                            'created': now,
                            'modified': now,                                                                
                            'malware_types': data.get('malware_types') if data.get('malware_types') is not None else ['unknown'],
                            'is_family' : data.get('is_family') if data.get('is_family') is not None else False
                        }
                    # right now its iterates additional attributes of malware SDO and no null, empty list is not checked. Developer has to ensure not to send such data
                    for key,value in data.items():                       
                        if key is not malware:
                            malware[key] = value
                    # set the description same as malware type returns from threat feed if description property is not provided. 
                    if data.get('description'):
                        malware['description'] = data.get('description')
                    elif data.get('malware_types') and 'unknown' not in data.get('malware_types'):                    
                        malware['description'] =  ','.join(data.get('malware_types')) if isinstance(data.get('malware_types'),list) else data.get('malware_types')

                    malware_types = self.normalized_malware_type(malware['malware_types'])     
                    malware['malware_types'] = malware_types                    

                    # malware SDO properties validation        
                    if self.stix_validator:
                        options = ValidationOptions(version="2.1")
                        results = validate_instance(malware, options)                    
                        if results.is_valid is False:                        
                            print_results(results)
                            raise Exception(f'Invalid parameter set in malware SDO. Please follow STIX 2.1 spec for properties') 

                    # if name is not present then compare only malware_types to remove duplicate else check malware types and name.                    
                    if (len([i for i in malware_array if (i['malware_types'] == malware ['malware_types'] and i['name'] == malware ['name'])]) == 0):                        
                        malware_array.append(malware)                   

                relationship = self.createRelationship(malware_array, indicator_id)
                malware_array += relationship
                return malware_array
        except Exception as err:
            raise Exception(f'Exception occurred in create_malware_sdo in BaseNormalization : {err}')
    def create_indicator_sdo(self, indicator_object: dict, identity_id: str, extension_id:str=None, nested_properties:list=None, top_properties:list=None):
        try:

            # Param: Dictionary
            stix_type = 'indicator'
            pattern_type = 'stix'
            DEFAULT_SPEC_VERSION = "2.1"
            now = "{}Z".format(datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%S.%f')[:-3])

            # Exception handle required property
            if 'pattern' not in indicator_object:
                raise ValueError(f'Missing required indicator property: pattern')

            indicator = {
                'type': stix_type,
                'spec_version': DEFAULT_SPEC_VERSION,
                'id': stix_type + '--' + str(uuid.uuid4()),
                'pattern': indicator_object['pattern'],
                'pattern_type': pattern_type,
                'created_by_ref': identity_id,
                'created': now,
                'modified': now,
                'valid_from': now,
            }

            if indicator_object.get('name'): indicator['name'] = indicator_object['name']
            if indicator_object.get('description'): indicator['description'] = indicator_object['description']
            if indicator_object.get('pattern_version'): indicator['pattern_version'] = indicator_object['pattern_version']
            if indicator_object.get('valid_until'): indicator['valid_until'] = indicator_object['valid_until']
            if indicator_object.get('kill_chain_phases'): indicator['kill_chain_phases'] = indicator_object['kill_chain_phases']
            if indicator_object.get('indicator_types'): indicator['indicator_types'] = indicator_object['indicator_types']
            if indicator_object.get('external_references'): indicator['external_references'] = indicator_object['external_references']

            if (extension_id):
                indicator = self.add_extension(indicator, extension_id, nested_properties, top_properties)

            # indicator SDO properties validation        
            if self.stix_validator:
                options = ValidationOptions(version="2.1")
                results = validate_instance(indicator, options)                    
                if results.is_valid is False:
                    print_results(results)                
                    raise Exception(f'Invalid parameter set in indicator SDO. Please follow STIX 2.1 spec for properties')

            return [indicator]

        except ValueError as err:
            raise ValueError(err)
Example #17
0
def __main__():
    bundle_file = sys.argv[1]

    try:
        with open(bundle_file) as f:
            bundle = json.load(f)
        results = validate_instance(bundle)
        if results.is_valid is not True:
            print_results(results)
            raise Exception()

        print('*** STIX Bundle validated!!\n')
    except ValueError as ex:
        print("*** Malformed JSON in the STIX Bundle: " + str(ex))
    except Exception as ex:
        print(
            "\n *** Invalid STIX Objects found in the bundle. Please fix the error marked as Red[X]. Warnings marked as yellow [!] can be ingnored but recommended to fix ***\n"
        )
    def create_extension_sdo(self, identity_object, namespace, nested_properties=[], toplevel_properties=[], schema='https://www.ibm.com/cp4s'):
        try:
            # Create an extension-definition object to be used in conjunction with STIX Indicator object
            stix_type = 'extension-definition'
            DEFAULT_SPEC_VERSION = "2.1"
            EXTENSION_VERSION = '1.2.1'
            extension_object = {
                'id': stix_type + '--' + str(uuid.uuid5(uuid.UUID(namespace), 'extension-definition')),
                'type': stix_type,
                'spec_version': DEFAULT_SPEC_VERSION,
                'name': (identity_object.get('name') + ' extension') if identity_object.get('name') is not None else "extension definition object",
                'created': "{}Z".format(datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%S.%f')[:-3]),
                'modified': "{}Z".format(datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%S.%f')[:-3]),
                'created_by_ref': identity_object['id'],
                'schema': schema,
                'version': EXTENSION_VERSION,
            }

            if identity_object.get('description'): extension_object['description'] = 'Extension object for ' + identity_object.get('description')

            if (len(nested_properties) > 0 or len(toplevel_properties) > 0):
                extension_object['extension_types'] = []
                extension_object['extension_properties'] = []
                if (len(toplevel_properties) > 0):
                    extension_object['extension_types'].append('toplevel-property-extension') 
                    for prop in toplevel_properties:
                        extension_object['extension_properties'].append(prop)
                if (len(nested_properties) > 0):
                    extension_object['extension_types'].append('property-extension') 
                    if (not len(extension_object['extension_properties']) > 0):
                        del extension_object['extension_properties']

            if self.stix_validator:
                options = ValidationOptions(version="2.1")
                results = validate_instance(extension_object, options)            
                if results.is_valid is False:              
                    print_results(results)
                    raise Exception(f'Invalid parameter set in extension_object SDO. Please follow STIX 2.1 spec for properties') 

            stix_extension_sdo = [extension_object]
            return stix_extension_sdo

        except Exception as err:
            raise Exception(f'Exception occurred in create_extension_sdo in BaseNormalization : {err}')  
def __main__():
    bundle_file = sys.argv[1]

    try:
        with open(bundle_file) as f:
            bundle = json.load(f)
        results = validate_instance(bundle)
        if results.is_valid:
            print_results(results)
            print(
                "\n *** STIX bundle is valid but may contain warnings. Warnings marked as yellow [!] can be ignored but recommended to fix ***\n"
            )
        else:
            print_results(results)
            print(
                "\n *** Invalid STIX Objects found in the bundle. Please fix the error marked as Red[X]. Warnings marked as yellow [!] can be ignored but recommended to fix ***\n"
            )

    except ValueError as ex:
        print("*** Malformed JSON in the STIX Bundle: " + str(ex))
    def transform(self, obj):
        """
        Transforms the given object in to a STIX observation based on the mapping file and transform functions

        :param obj: the datasource object that is being converted to stix
        :return: the input object converted to stix valid json
        """
        object_map = {}
        stix_type = 'observed-data'
        ds_map = self.ds_to_stix_map
        transformers = self.transformers
        observation = {
            'id': stix_type + '--' + str(uuid.uuid4()),
            'type': stix_type,
            'created_by_ref': self.identity_id,
            'objects': {}
        }

        # create normal type objects
        for ds_key in obj:
            if ds_key not in ds_map:
                logging.debug(
                    '{} is not found in map, skipping'.format(ds_key))
                continue
            # get the stix keys that are mapped
            ds_key_def_obj = self.ds_to_stix_map[ds_key]
            ds_key_def_list = ds_key_def_obj if isinstance(
                ds_key_def_obj, list) else [ds_key_def_obj]
            for ds_key_def in ds_key_def_list:
                if ds_key_def is None or 'key' not in ds_key_def:
                    logging.debug(
                        '{} is not valid (None, or missing key)'.format(
                            ds_key_def))
                    continue

                key_to_add = ds_key_def['key']
                transformer = transformers[ds_key_def[
                    'transformer']] if 'transformer' in ds_key_def else None

                if ds_key_def.get('cybox', self.cybox_default):
                    object_name = ds_key_def.get('object')
                    if 'references' in ds_key_def:
                        stix_value = object_map[ds_key_def['references']]
                    else:
                        stix_value = DataSourceObjToStixObj._get_value(
                            obj, ds_key, transformer)
                        if not DataSourceObjToStixObj._valid_stix_value(
                                self.properties, key_to_add, stix_value):
                            continue
                    DataSourceObjToStixObj._handle_cybox_key_def(
                        key_to_add, observation, stix_value, object_map,
                        object_name)
                else:
                    stix_value = DataSourceObjToStixObj._get_value(
                        obj, ds_key, transformer)
                    if not DataSourceObjToStixObj._valid_stix_value(
                            self.properties, key_to_add, stix_value):
                        continue
                    DataSourceObjToStixObj._add_property(
                        observation, key_to_add, stix_value)

        # Validate each STIX object
        if self.stix_validator:
            validated_result = validate_instance(observation)
            print_results(validated_result)
        return observation
Example #21
0
    def test_change_cim_to_stix(self):
        count = 1
        time = "2018-08-21T15:11:55.000+00:00"
        file_bytes = "300"
        user = "******"
        objPath = "hkey_local_machine\\system\\bar\\foo"
        filePath = "C:\\Users\\someuser\\sample.dll"
        create_time = "2018-08-15T15:11:55.676+00:00"
        modify_time = "2018-08-15T18:10:30.456+00:00"
        file_hash = "41a26255d16d121dc525a6445144b895"
        file_name = "sample.dll"
        file_size = 25536

        data = {
            "event_count": count,
            "_time": time,
            "user": user,
            "bytes": file_bytes,
            "object_path": objPath,
            "file_path": filePath,
            "file_create_time": create_time,
            "file_modify_time": modify_time,
            "file_hash": file_hash,
            "file_size": file_size,
            "file_name": file_name
        }

        result_bundle = json_to_stix_translator.convert_to_stix(
            data_source,
            map_data, [data],
            get_module_transformers(MODULE),
            options,
            callback=hash_type_lookup)

        assert (result_bundle['type'] == 'bundle')
        result_bundle_objects = result_bundle['objects']
        observed_data = result_bundle_objects[1]
        validated_result = validate_instance(observed_data)
        assert (validated_result.is_valid == True)

        assert ('objects' in observed_data)
        objects = observed_data['objects']

        # Test objects in Stix observable data model after transform
        wrk_obj = TestTransform.get_first_of_type(objects.values(),
                                                  'windows-registry-key')
        assert (wrk_obj is not None)
        assert (wrk_obj.keys() == {'type', 'key'})
        assert (wrk_obj['key'] == "hkey_local_machine\\system\\bar\\foo")

        user_obj = TestTransform.get_first_of_type(objects.values(),
                                                   'user-account')

        assert (user_obj is not None), 'user-account object type not found'
        assert (user_obj.keys() == {'type', 'account_login', 'user_id'})
        assert (user_obj['account_login'] == "ibm_user")
        assert (user_obj['user_id'] == "ibm_user")

        file_obj = TestTransform.get_first_of_type(objects.values(), 'file')

        assert (file_obj is not None), 'file object type not found'
        assert (file_obj.keys() == {
            'type', 'parent_directory_ref', 'created', 'modified', 'hashes',
            'name', 'size'
        })

        assert (file_obj['created'] == "2018-08-15T15:11:55.676Z")
        assert (file_obj['modified'] == "2018-08-15T18:10:30.456Z")
        assert (file_obj['name'] == "sample.dll")
        assert (file_obj['size'] == 25536)
        assert (
            file_obj['hashes']['MD5'] == "41a26255d16d121dc525a6445144b895")

        dir_ref = file_obj['parent_directory_ref']
        assert (
            dir_ref in objects
        ), f"parent_directory_ref with key {file_obj['parent_directory_ref']} not found"
        dir_obj = objects[dir_ref]

        assert (dir_obj is not None), 'directory object type not found'
        assert (dir_obj.keys() == {'type', 'path', 'created', 'modified'})
        assert (dir_obj['path'] == "C:\\Users\\someuser\\sample.dll")
        assert (dir_obj['created'] == "2018-08-15T15:11:55.676Z")
        assert (dir_obj['modified'] == "2018-08-15T18:10:30.456Z")
        print(objects.keys())
        print(result_bundle_objects)
        assert (objects.keys() == set(map(str, range(0, 5))))
    def transform(self, obj):
        """
        Transforms the given object in to a STIX observation based on the mapping file and transform functions

        :param obj: the datasource object that is being converted to stix
        :return: the input object converted to stix valid json
        """

        index = 0
        ref_objs = {}
        linked_objs = {}
        stix_type = 'observed-data'
        uniq_id = str(uuid.uuid4())
        ds_map = self.dsToStixMap
        xformers = self.transformers
        observation = {
            'x_com_ibm_uds_datasource': {
                'id': self.datasource['id'],
                'name': self.datasource['name']
            },
            'id': stix_type + '--' + uniq_id,
            'type': stix_type,
            'objects': {},
        }
        # create normal type objects
        for ds_key in ds_map:
            # get the stix keys that are mapped
            ds_key_def_obj = self.dsToStixMap[ds_key]
            ds_key_def_list = ds_key_def_obj if isinstance(
                ds_key_def_obj, list) else [ds_key_def_obj]
            for ds_key_def in ds_key_def_list:
                if ds_key_def is None or 'key' not in ds_key_def or 'type' not in ds_key_def:
                    logging.debug(
                        '{} is not valid (None, or missing key and type)'.
                        format(ds_key_def))
                    continue
                if ds_key_def['type'] != 'value' or 'cybox' in ds_key_def:
                    continue

                key_to_add = ds_key_def['key']
                transformer = xformers[ds_key_def[
                    'transformer']] if 'transformer' in ds_key_def else None
                linked = ds_key_def[
                    'linked'] if 'linked' in ds_key_def else None
                stix_value = DataSourceObjToStixObj._get_value(
                    obj, ds_key, transformer)

                if stix_value is None:
                    continue

                prop_obj = DataSourceObjToStixObj._determine_prop_attr(
                    key_to_add, self.outer_props, self.simple_props)
                if prop_obj[0] is not None and 'valid_regex' in prop_obj[0]:
                    pattern = re.compile(prop_obj[0]['valid_regex'])
                    if not pattern.match(str(stix_value)):
                        continue
                # handle when object is linked
                if linked is not None:
                    observation = DataSourceObjToStixObj._handle_linked(
                        key_to_add, observation, stix_value)
                elif prop_obj[1] == 'OUTER':
                    observation.update({key_to_add: stix_value})
                else:
                    index = (self._add_to_objects(key_to_add, stix_value,
                                                  observation, index, ds_key,
                                                  ref_objs, linked,
                                                  linked_objs, True, None))
        # create complex type objects
        DataSourceObjToStixObj._create_complex_objects(ds_map, xformers, index,
                                                       observation, ref_objs,
                                                       linked_objs, obj)
        # Validate each STIX object
        if self.stix_validator:
            validated_result = validate_instance(observation)
            print_results(validated_result)
        return observation
Example #23
0
    def test_process_cim_to_stix(self):
        count = 1
        time = "2018-08-21T15:11:55.000+00:00"
        user = "******"
        pid = 0
        name = "test_process"
        filePath = "C:\\Users\\someuser\\sample.dll"
        create_time = "2018-08-15T15:11:55.676+00:00"
        modify_time = "2018-08-15T18:10:30.456+00:00"
        file_hash = "aec070645fe53ee3b3763059376134f058cc337247c978add178b6ccdfb0019f"
        file_name = "sample.dll"
        file_size = 25536

        data = {
            "event_count": count,
            "_time": time,
            "user": user,
            "process_name": name,
            "process_id": pid,
            "file_path": filePath,
            "file_create_time": create_time,
            "file_modify_time": modify_time,
            "file_hash": file_hash,
            "file_size": file_size,
            "file_name": file_name
        }

        result_bundle = json_to_stix_translator.convert_to_stix(
            data_source,
            map_data, [data],
            get_module_transformers(MODULE),
            options,
            callback=hash_type_lookup)

        assert (result_bundle['type'] == 'bundle')
        result_bundle_objects = result_bundle['objects']
        observed_data = result_bundle_objects[1]

        validated_result = validate_instance(observed_data)
        assert (validated_result.is_valid == True)

        assert ('objects' in observed_data)
        objects = observed_data['objects']

        # Test objects in Stix observable data model after transform
        proc_obj = TestTransform.get_first_of_type(objects.values(), 'process')
        assert (proc_obj is not None), 'process object type not found'
        assert (proc_obj.keys() == {'type', 'name', 'pid', 'binary_ref'})

        assert (proc_obj['name'] == "test_process")
        assert (proc_obj['pid'] == 0)

        user_obj = TestTransform.get_first_of_type(objects.values(),
                                                   'user-account')

        assert (user_obj is not None), 'user-account object type not found'
        assert (user_obj.keys() == {'type', 'account_login', 'user_id'})
        assert (user_obj['account_login'] == "test_user")
        assert (user_obj['user_id'] == "test_user")

        bin_ref = proc_obj['binary_ref']
        assert (bin_ref in objects
                ), f"binary_ref with key {proc_obj['binary_ref']} not found"
        file_obj = objects[bin_ref]

        assert (file_obj is not None), 'file object type not found'
        assert (file_obj.keys() == {
            'type', 'parent_directory_ref', 'created', 'modified', 'size',
            'name', 'hashes'
        })
        assert (file_obj['created'] == "2018-08-15T15:11:55.676Z")
        assert (file_obj['modified'] == "2018-08-15T18:10:30.456Z")
        assert (file_obj['name'] == "sample.dll")
        assert (file_obj['size'] == 25536)
        assert (
            file_obj['hashes']['SHA-256'] ==
            "aec070645fe53ee3b3763059376134f058cc337247c978add178b6ccdfb0019f")
        dir_ref = file_obj['parent_directory_ref']
        assert (
            dir_ref in objects
        ), f"parent_directory_ref with key {file_obj['parent_directory_ref']} not found"
        dir_obj = objects[dir_ref]

        assert (dir_obj is not None), 'directory object type not found'
        assert (dir_obj.keys() == {'type', 'path', 'created', 'modified'})
        assert (dir_obj['path'] == "C:\\Users\\someuser\\sample.dll")
        assert (dir_obj['created'] == "2018-08-15T15:11:55.676Z")
        assert (dir_obj['modified'] == "2018-08-15T18:10:30.456Z")

        assert (objects.keys() == set(map(str, range(0, 4))))
Example #24
0
    def test_cim_to_stix_no_tags(self):

        data = {"src_ip": "169.250.0.1", "src_port": "1220", "src_mac": "aa:bb:cc:dd:11:22",
                "dest_ip": "127.0.0.1", "dest_port": "1120", "dest_mac": "ee:dd:bb:aa:cc:11",
                "file_hash": "cf23df2207d99a74fbe169e3eba035e633b65d94",
                "user": "******", "url": "https://wally.fireeye.com/malware_analysis/analyses?maid=1",
                "protocol": "tcp", "_bkt": "main~44~6D3E49A0-31FE-44C3-8373-C3AC6B1ABF06", "_cd": "44:12606114",
                "_indextime": "1546960685",
                "_raw": "Jan 08 2019 15:18:04 192.168.33.131 fenotify-2.alert: CEF:0|FireEye|MAS|6.2.0.74298|MO|"
                        "malware-object|4|rt=Jan 08 2019 15:18:04 Z src=169.250.0.1 dpt=1120 dst=127.0.0.1"
                        " spt=1220 smac=AA:BB:CC:DD:11:22 dmac=EE:DD:BB:AA:CC:11 cn2Label=sid cn2=111"
                        " fileHash=41a26255d16d121dc525a6445144b895 proto=tcp "
                        "request=http://qa-server.eng.fireeye.com/QE/NotificationPcaps/"
                        "58.253.68.29_80-192.168.85.128_1165-2119283109_T.exe cs3Label=osinfo"
                        " cs3=Microsoft Windows7 Professional 6.1 sp1 dvchost=wally dvc=10.2.101.101 cn1Label=vlan"
                        " cn1=0 externalId=1 cs4Label=link "
                        "cs4=https://wally.fireeye.com/malware_analysis/analyses?maid=1 cs2Label=anomaly"
                        " cs2=misc-anomaly cs1Label=sname cs1=FE_UPX;Trojan.PWS.OnlineGames",
                "_serial": "0", "_si": ["splunk3-01.internal.resilientsystems.com", "main"],
                "_sourcetype": "fe_cef_syslog", "_time": "2019-01-08T15:18:04.000+00:00", "event_count": 1
                }

        result_bundle = json_to_stix_translator.convert_to_stix(
            data_source, map_data, [data], transformers.get_all_transformers(), options, callback=hash_type_lookup)

        assert(result_bundle['type'] == 'bundle')

        result_bundle_objects = result_bundle['objects']
        observed_data = result_bundle_objects[1]
        validated_result = validate_instance(observed_data)
        assert(validated_result.is_valid == True)
        assert('objects' in observed_data)
        objects = observed_data['objects']
        nt_obj = TestTransform.get_first_of_type(objects.values(), 'network-traffic')
        assert(nt_obj is not None), 'network-traffic object type not found'
        assert(nt_obj.keys() == {'type', 'src_ref', 'src_port', 'dst_ref', 'dst_port', 'protocols'})
        assert(nt_obj['src_port'] == 1220)
        assert(nt_obj['dst_port'] == 1120)
        assert(nt_obj['protocols'] == ['tcp'])

        nt_obj_2 = objects['2']
        assert (nt_obj_2 is not None), 'network-traffic object type not found'
        assert (nt_obj_2.keys() == {'type', 'src_ref', 'src_port', 'dst_ref', 'dst_port', 'protocols'})
        assert (nt_obj_2['src_port'] == 1220)
        assert (nt_obj_2['dst_port'] == 1120)
        assert (nt_obj_2['protocols'] == ['tcp'])

        mac_ref = nt_obj_2['dst_ref']
        assert(mac_ref in objects), "dst_ref with key {nt_obj['dst_ref']} not found"
        mac_obj = objects[mac_ref]
        assert(mac_obj.keys() == {'type', 'value'})
        assert(mac_obj['type'] == 'mac-addr')
        assert(mac_obj['value'] == 'ee:dd:bb:aa:cc:11')

        mac_ref = nt_obj_2['src_ref']
        assert(mac_ref in objects), "src_ref with key {nt_obj['dst_ref']} not found"
        mac_obj = objects[mac_ref]
        assert(mac_obj.keys() == {'type', 'value'})
        assert(mac_obj['type'] == 'mac-addr')
        assert(mac_obj['value'] == 'aa:bb:cc:dd:11:22')

        ip_ref = nt_obj['dst_ref']
        assert(ip_ref in objects), "dst_ref with key {nt_obj['dst_ref']} not found"
        ip_obj = objects[ip_ref]
        assert(ip_obj.keys() == {'type', 'value'})
        assert(ip_obj['type'] == 'ipv4-addr')
        assert(ip_obj['value'] == '127.0.0.1')

        ip_ref = nt_obj['src_ref']
        assert(ip_ref in objects), "src_ref with key {nt_obj['src_ref']} not found"
        ip_obj = objects[ip_ref]
        assert(ip_obj.keys() == {'type', 'value'})
        assert(ip_obj['type'] == 'ipv4-addr')
        assert(ip_obj['value'] == '169.250.0.1')

        file_obj = TestTransform.get_first_of_type(objects.values(), 'file')
        assert (file_obj is not None), 'file object type not found'
        assert (file_obj.keys() == {'type', 'hashes'})
        assert (file_obj['hashes']['SHA-1'] == "cf23df2207d99a74fbe169e3eba035e633b65d94")
        user_obj = TestTransform.get_first_of_type(objects.values(), 'user-account')
        assert (user_obj is not None), 'user object type not found'
        assert (user_obj.keys() == {'type', 'account_login', 'user_id'})
        assert (user_obj['account_login'] == "sname")
        assert (user_obj['user_id'] == "sname")

        url_obj = TestTransform.get_first_of_type(objects.values(), 'url')
        assert (url_obj is not None), 'url object type not found'
        assert (url_obj.keys() == {'type', 'value'})
        assert (url_obj['value'] == "https://wally.fireeye.com/malware_analysis/analyses?maid=1")

        domain_obj = TestTransform.get_first_of_type(objects.values(), 'domain-name')
        assert (domain_obj is not None), 'domain object type not found'
        assert (domain_obj.keys() == {'type', 'value'})
        assert (domain_obj['value'] == "wally.fireeye.com")

        payload_obj = TestTransform.get_first_of_type(objects.values(), 'artifact')
        assert (payload_obj is not None), 'payload object type not found'
        assert (payload_obj.keys() == {'type', 'payload_bin'})
        payload = 'SmFuIDA4IDIwMTkgMTU6MTg6MDQgMTkyLjE2OC4zMy4xMzEgZmVub3RpZnktMi5hbGVydDogQ0VGOjB8RmlyZUV5ZXxNQV' \
                  'N8Ni4yLjAuNzQyOTh8TU98bWFsd2FyZS1vYmplY3R8NHxydD1KYW4gMDggMjAxOSAxNToxODowNCBaIHNyYz0xNjkuMjUw' \
                  'LjAuMSBkcHQ9MTEyMCBkc3Q9MTI3LjAuMC4xIHNwdD0xMjIwIHNtYWM9QUE6QkI6Q0M6REQ6MTE6MjIgZG1hYz1FRTpERD' \
                  'pCQjpBQTpDQzoxMSBjbjJMYWJlbD1zaWQgY24yPTExMSBmaWxlSGFzaD00MWEyNjI1NWQxNmQxMjFkYzUyNWE2NDQ1MTQ0' \
                  'Yjg5NSBwcm90bz10Y3AgcmVxdWVzdD1odHRwOi8vcWEtc2VydmVyLmVuZy5maXJlZXllLmNvbS9RRS9Ob3RpZmljYXRpb2' \
                  '5QY2Fwcy81OC4yNTMuNjguMjlfODAtMTkyLjE2OC44NS4xMjhfMTE2NS0yMTE5MjgzMTA5X1QuZXhlIGNzM0xhYmVsPW9z' \
                  'aW5mbyBjczM9TWljcm9zb2Z0IFdpbmRvd3M3IFByb2Zlc3Npb25hbCA2LjEgc3AxIGR2Y2hvc3Q9d2FsbHkgZHZjPTEwLj' \
                  'IuMTAxLjEwMSBjbjFMYWJlbD12bGFuIGNuMT0wIGV4dGVybmFsSWQ9MSBjczRMYWJlbD1saW5rIGNzND1odHRwczovL3dh' \
                  'bGx5LmZpcmVleWUuY29tL21hbHdhcmVfYW5hbHlzaXMvYW5hbHlzZXM/bWFpZD0xIGNzMkxhYmVsPWFub21hbHkgY3MyPW' \
                  '1pc2MtYW5vbWFseSBjczFMYWJlbD1zbmFtZSBjczE9RkVfVVBYO1Ryb2phbi5QV1MuT25saW5lR2FtZXM='
        assert (payload_obj['payload_bin'] == payload)
    def test_change_cim_to_stix(self):
        tag = "change"
        count = 1
        time = "2018-08-21T15:11:55.000+00:00"
        file_bytes = "300"
        user = "******"
        objPath = "hkey_local_machine\\system\\bar\\foo"
        filePath = "C:\\Users\\someuser\\sample.dll"
        create_time = "2018-08-15T15:11:55.676+00:00"
        modify_time = "2018-08-15T18:10:30.456+00:00"
        file_hash = "aec070645fe53ee3b3763059376134f058cc337247c978add178b6ccdfb0019f"
        file_name = "sample.dll"
        file_size = 25536

        data = {
            "tag": tag,
            "event_count": count,
            "_time": time,
            "user": user,
            "bytes": file_bytes,
            "object_path": objPath,
            "file_path": filePath,
            "file_create_time": create_time,
            "file_modify_time": modify_time,
            "file_hash": file_hash,
            "file_size": file_size,
            "file_name": file_name
        }

        result_bundle = cim_to_stix_translator.convert_to_stix(
            data_source, map_data, [data], transformers.get_all_transformers(),
            options)

        assert (result_bundle['type'] == 'bundle')
        result_bundle_objects = result_bundle['objects']
        observed_data = result_bundle_objects[1]

        validated_result = validate_instance(observed_data)
        assert (validated_result.is_valid == True)

        assert ('objects' in observed_data)
        objects = observed_data['objects']

        # Test objects in Stix observable data model after transform
        wrk_obj = TestTransform.get_first_of_type(objects.values(),
                                                  'windows-registry-key')
        assert (wrk_obj
                is not None), 'windows-registry-key object type not found'
        assert (wrk_obj.keys() == {'type', 'creator_user_ref', 'key'})
        assert (wrk_obj['key'] == "hkey_local_machine\\system\\bar\\foo")

        user_ref = wrk_obj['creator_user_ref']
        assert (
            user_ref in objects
        ), f"creator_user_ref with key {wrk_obj['creator_user_ref']} not found"
        user_obj = objects[user_ref]

        assert (user_obj is not None), 'user-account object type not found'
        assert (user_obj.keys() == {'type', 'account_login', 'user_id'})
        assert (user_obj['account_login'] == "ibm_user")
        assert (user_obj['user_id'] == "ibm_user")

        file_obj = TestTransform.get_first_of_type(objects.values(), 'file')
        assert (file_obj is not None), 'file object type not found'
        assert (file_obj.keys() == {
            'type', 'parent_directory_ref', 'created', 'modified', 'hashes',
            'name', 'size'
        })

        assert (file_obj['created'] == "2018-08-15T15:11:55.676Z")
        assert (file_obj['modified'] == "2018-08-15T18:10:30.456Z")
        assert (file_obj['name'] == "sample.dll")
        assert (file_obj['size'] == 25536)
        assert (
            file_obj['hashes']['SHA-256'] ==
            "aec070645fe53ee3b3763059376134f058cc337247c978add178b6ccdfb0019f")

        dir_ref = file_obj['parent_directory_ref']
        assert (
            dir_ref in objects
        ), f"parent_directory_ref with key {file_obj['parent_directory_ref']} not found"
        dir_obj = objects[dir_ref]

        assert (dir_obj is not None), 'directory object type not found'
        assert (dir_obj.keys() == {'type', 'path', 'created', 'modified'})
        assert (dir_obj['path'] == "C:\\Users\\someuser\\sample.dll")
        assert (dir_obj['created'] == "2018-08-15T15:11:55.676Z")
        assert (dir_obj['modified'] == "2018-08-15T18:10:30.456Z")

        assert (objects.keys() == set(map(str, range(0, 4))))
    def transform(self, obj):
        """
        Transforms the given object in to a STIX observation based on the mapping file and transform functions
        :param obj: the datasource object that is being converted to stix
        :return: the input object converted to stix valid json
        """
        object_map = {}
        stix_type = 'observed-data'
        ds_map = self.ds_to_stix_map
        now = "{}Z".format(
            datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%S.%f')[:-3])
        object_id_map = {}

        observation = {
            'id': stix_type + '--' + str(uuid.uuid4()),
            'type': stix_type,
            'created_by_ref': self.identity_id,
            'created': now,
            'modified': now,
            'objects': {}
        }

        # create normal type objects
        if isinstance(obj, dict):
            for ds_key in obj.keys():
                self._transform(object_map, observation, ds_map, ds_key, obj)
        else:
            self.logger.debug("Not a dict: {}".format(obj))

        # special case:
        # remove object if:
        # a reference attribute object does not contain at least one property other than 'type'
        self._cleanup_references(object_map, observation, ds_map)

        # Add required properties to the observation if it wasn't added from the mapping
        if FIRST_OBSERVED_KEY not in observation:
            observation[FIRST_OBSERVED_KEY] = now
        if LAST_OBSERVED_KEY not in observation:
            observation[LAST_OBSERVED_KEY] = now
        if NUMBER_OBSERVED_KEY not in observation:
            observation[NUMBER_OBSERVED_KEY] = 1

        if self.spec_version == "2.1":
            cybox_objects = observation["objects"]
            self._generate_and_apply_deterministic_id(object_id_map,
                                                      cybox_objects)
            self._replace_references(object_id_map, cybox_objects)
            object_refs = []
            # add cybox references to observed-data object
            for key, value in object_id_map.items():
                object_refs.append(value)
            observation["object_refs"] = object_refs
            observation["spec_version"] = "2.1"
            self._collect_unique_cybox_objects(cybox_objects)

        # Validate each STIX object
        if self.stix_validator:
            validated_result = validate_instance(observation)
            print_results(validated_result)

        return observation