コード例 #1
0
    def build_attachments(self, attachment_names, incident_id, resilient_client):
        """
        Get attachment data from Resilient and format it for Exchange Online.
        :param attachment_name: name of the attachment in Resilient
        :param incident_id: ID of Resilient incident
        :param resilient_client: Resilient REST API client
        :return: formatted attachment data
        """
        attachments = []
        failed_attached = []

        attachment_names = [item.strip() for item in attachment_names.split(",")]
        for attachment_name in attachment_names:
            base64content, attachment_id = get_incident_file_attachment(resilient_client, incident_id, attachment_name)
            if not attachment_id:
                failed_attached.append(attachment_name)
                LOG.info(u"Failed to attach %s. No matching incident attachment found.", attachment_name)
                continue
            contentType = get_file_attachment_metadata(resilient_client, incident_id, attachment_id=attachment_id)["content_type"]
            attachment = {
                    "@odata.type": "#microsoft.graph.fileAttachment",
                    "name": attachment_name,
                    "contentType": contentType,
                    "contentBytes": base64content
                }

            attachments.append(attachment)
            LOG.info(u"Successfully attached %s", attachment_name)

        return attachments , failed_attached
    def _attachment_to_base64_function(self, event, *args, **kwargs):
        """Function: Produce base64 content of a file attachment."""
        try:
            log = logging.getLogger(__name__)

            # Get the function parameters:
            incident_id = kwargs.get("incident_id")  # number
            task_id = kwargs.get("task_id")  # number
            attachment_id = kwargs.get("attachment_id")  # number
            artifact_id = kwargs.get("artifact_id")  # number

            log.info("incident_id: %s", incident_id)
            log.info("task_id: %s", task_id)
            log.info("attachment_id: %s", attachment_id)
            log.info("artifact_id: %s", artifact_id)

            if incident_id is None:
                raise FunctionError("Error: incident_id must be specified.")
            elif attachment_id is None and artifact_id is None:
                raise FunctionError(
                    "Error: attachment_id or artifact_id must be specified.")
            else:
                yield StatusMessage("> Function inputs OK")

            yield StatusMessage("> Reading attachment...")

            client = self.rest_client()
            data = get_file_attachment(client,
                                       incident_id,
                                       artifact_id=artifact_id,
                                       task_id=task_id,
                                       attachment_id=attachment_id)
            metadata = get_file_attachment_metadata(
                client,
                incident_id,
                artifact_id=artifact_id,
                task_id=task_id,
                attachment_id=attachment_id)

            results = {
                "filename": metadata["name"],
                "content_type": metadata["content_type"],
                "size": metadata["size"],
                "created": metadata["created"],
                "content": b_to_s(base64.b64encode(data)),
            }
            yield StatusMessage("> Complete...")
            # Produce a FunctionResult with the return value
            log.debug(json.dumps(results, indent=2))
            yield FunctionResult(results)
        except Exception:
            yield FunctionError()
コード例 #3
0
    def _email_parse_function(self, event, *args, **kwargs):
        """Function: Extract message headers and body parts from an email message (.eml or .msg).
        Any attachments found are added to the Incident as Artifacts if 'utilities_parse_email_attachments' is set to True"""

        try:
            log = logging.getLogger(__name__)

            # Set variables
            parsed_email = path_tmp_file = path_tmp_dir = reason = results = None

            # Get the function inputs:
            fn_inputs = validate_fields(["incident_id"], kwargs)

            # Instansiate ResultPayload
            rp = ResultPayload(CONFIG_DATA_SECTION, **kwargs)

            # If its just base64content as input, use parse_from_string
            if fn_inputs.get("base64content"):
                yield StatusMessage("Processing provided base64content")
                parsed_email = mailparser.parse_from_string(
                    b_to_s(base64.b64decode(fn_inputs.get("base64content"))))
                yield StatusMessage("Provided base64content processed")

            else:

                # Validate that either: (incident_id AND attachment_id OR artifact_id) OR (task_id AND attachment_id) is defined
                if not (fn_inputs.get("incident_id") and (fn_inputs.get("attachment_id") or fn_inputs.get("artifact_id"))) and \
                   not (fn_inputs.get("task_id") and fn_inputs.get("attachment_id")):
                    raise FunctionError(
                        "You must define either: (incident_id AND attachment_id OR artifact_id) OR (task_id AND attachment_id)"
                    )

                # Instansiate new Resilient API object
                res_client = self.rest_client()

                # Get attachment metadata
                attachment_metadata = get_file_attachment_metadata(
                    res_client=res_client,
                    incident_id=fn_inputs.get("incident_id"),
                    artifact_id=fn_inputs.get("artifact_id"),
                    task_id=fn_inputs.get("task_id"),
                    attachment_id=fn_inputs.get("attachment_id"))

                # Get attachment content
                attachment_contents = get_file_attachment(
                    res_client=res_client,
                    incident_id=fn_inputs.get("incident_id"),
                    artifact_id=fn_inputs.get("artifact_id"),
                    task_id=fn_inputs.get("task_id"),
                    attachment_id=fn_inputs.get("attachment_id"))

                # Write the attachment_contents to a temp file
                path_tmp_file, path_tmp_dir = write_to_tmp_file(
                    attachment_contents,
                    tmp_file_name=attachment_metadata.get("name"))

                # Get the file_extension
                file_extension = os.path.splitext(path_tmp_file)[1]

                if file_extension == ".msg":
                    yield StatusMessage("Processing MSG File")
                    try:
                        parsed_email = mailparser.parse_from_file_msg(
                            path_tmp_file)
                        yield StatusMessage("MSG File processed")
                    except Exception as err:
                        reason = u"Could not parse {0} MSG File".format(
                            attachment_metadata.get("name"))
                        yield StatusMessage(reason)
                        results = rp.done(success=False,
                                          content=None,
                                          reason=reason)
                        log.error(err)

                else:
                    yield StatusMessage("Processing Raw Email File")
                    try:
                        parsed_email = mailparser.parse_from_file(
                            path_tmp_file)
                        yield StatusMessage("Raw Email File processed")
                    except Exception as err:
                        reason = u"Could not parse {0} Email File".format(
                            attachment_metadata.get("name"))
                        yield StatusMessage(reason)
                        results = rp.done(success=False,
                                          content=None,
                                          reason=reason)
                        log.error(err)

            if parsed_email is not None:
                if not parsed_email.mail:
                    reason = u"Raw email in unsupported format. Failed to parse {0}".format(
                        u"provided base64content" if fn_inputs.
                        get("base64content"
                            ) else attachment_metadata.get("name"))
                    yield StatusMessage(reason)
                    results = rp.done(success=False,
                                      content=None,
                                      reason=reason)

                else:
                    # Load all parsed email attributes into a Python Dict
                    parsed_email_dict = json.loads(parsed_email.mail_json,
                                                   encoding="utf-8")
                    parsed_email_dict[
                        "plain_body"] = parsed_email.text_plain_json
                    parsed_email_dict[
                        "html_body"] = parsed_email.text_html_json
                    yield StatusMessage("Email parsed")

                    # If the input 'utilities_parse_email_attachments' is true and some attachments were found
                    if fn_inputs.get("utilities_parse_email_attachments"
                                     ) and parsed_email_dict.get(
                                         "attachments"):

                        yield StatusMessage(
                            "Attachments found in email message")
                        attachments_found = parsed_email_dict.get(
                            "attachments")

                        # Loop attachments found
                        for attachment in attachments_found:

                            yield StatusMessage(
                                u"Attempting to add {0} to Incident: {1}".
                                format(attachment.get("filename"),
                                       fn_inputs.get("incident_id")))

                            # Write the attachment.payload to a temp file
                            path_tmp_file, path_tmp_dir = write_to_tmp_file(
                                data=s_to_b(attachment.get("payload")),
                                tmp_file_name=attachment.get("filename"),
                                path_tmp_dir=path_tmp_dir)

                            artifact_description = u"This email attachment was found in the parsed email message from: '{0}'".format(
                                u"provided base64content" if fn_inputs.
                                get("base64content"
                                    ) else attachment_metadata.get("name"))

                            # POST the artifact to Resilient as an 'Email Attachment' Artifact
                            res_client.post_artifact_file(
                                uri=ARTIFACT_URI.format(
                                    fn_inputs.get("incident_id")),
                                artifact_type=EMAIL_ATTACHMENT_ARTIFACT_ID,
                                artifact_filepath=path_tmp_file,
                                description=artifact_description,
                                value=attachment.get("filename"),
                                mimetype=attachment.get("mail_content_type"))

                    results = rp.done(True, parsed_email_dict)

            else:
                reason = u"Raw email in unsupported format. Failed to parse {0}".format(
                    u"provided base64content" if fn_inputs.
                    get("base64content") else attachment_metadata.get("name"))
                yield StatusMessage(reason)
                results = rp.done(success=False, content=None, reason=reason)

            log.info("Done")

            yield FunctionResult(results)
        except Exception:
            yield FunctionError()

        finally:
            # Remove the tmp directory
            if path_tmp_dir and os.path.isdir(path_tmp_dir):
                shutil.rmtree(path_tmp_dir)
コード例 #4
0
    def _attachment_hash_function(self, event, *args, **kwargs):
        """Function: Calculate hashes for a file attachment."""
        try:
            log = logging.getLogger(__name__)

            # Get the function parameters:
            incident_id = kwargs.get("incident_id")  # number
            task_id = kwargs.get("task_id")  # number
            attachment_id = kwargs.get("attachment_id")  # number

            log.info("incident_id: %s", incident_id)
            log.info("task_id: %s", task_id)
            log.info("attachment_id: %s", attachment_id)
            if incident_id is None and task_id is None:
                raise FunctionError(
                    "Error: incident_id or task_id must be specified.")
            if attachment_id is None:
                raise FunctionError("Error: attachment_id must be specified.")

            yield StatusMessage("Reading attachment...")

            client = self.rest_client()
            data = get_file_attachment(client,
                                       incident_id,
                                       task_id=task_id,
                                       attachment_id=attachment_id)
            metadata = get_file_attachment_metadata(
                client,
                incident_id,
                task_id=task_id,
                attachment_id=attachment_id)

            results = {
                "filename": metadata["name"],
                "content_type": metadata["content_type"],
                "size": metadata["size"],
                "created": metadata["created"]
            }

            # Hashlib provides a list of all "algorithms_available", but there's duplication, so
            # use the standard list: ('md5', 'sha1', 'sha224', 'sha256', 'sha384', 'sha512')
            # Hashlib in 3.2 and above does not supports hashlib.algorithms_guaranteed this contains a longer
            # list but will be used instead of hashlib.algorithms
            if sys.version_info.major >= 3:
                algorithms = hashlib.algorithms_guaranteed
            else:
                algorithms = hashlib.algorithms

            for algo in algorithms:
                impl = hashlib.new(algo)
                impl.update(data)
                # shake algorithms require a 'length' parameter
                if algo.startswith("shake_"):
                    length_list = algo.split('_')
                    results[algo] = impl.hexdigest(int(length_list[-1]))
                else:
                    results[algo] = impl.hexdigest()

            log.info(u"{} sha1={}".format(metadata["name"], results["sha1"]))

            # Produce a FunctionResult with the return value
            log.debug(json.dumps(results))
            yield FunctionResult(results)
        except Exception:
            yield FunctionError()