Пример #1
0
    def check_macros(self):
        """
        Check whether this file contains macros (VBA and XLM/Excel 4).

        :returns: :py:class:`Indicator`
        """
        vba_indicator = Indicator(_id='vba', value='No', _type=str, name='VBA Macros',
                                  description='This file does not contain VBA macros.',
                                  risk=RISK.NONE)
        try:
            vba_parser = olevba.VBA_Parser(filename=self.filename, data=self.data)
            if vba_parser.detect_vba_macros():
                vba_indicator.value = 'Yes'
                vba_indicator.risk = RISK.MEDIUM
                vba_indicator.description = 'This file contains VBA macros. No suspicious keyword was found. Use olevba and mraptor for more info.'
                # check code with mraptor
                vba_code = vba_parser.get_vba_code_all_modules()
                m = mraptor.MacroRaptor(vba_code)
                m.scan()
                if m.suspicious:
                    vba_indicator.value = 'Yes, suspicious'
                    vba_indicator.risk = RISK.HIGH
                    vba_indicator.description = 'This file contains VBA macros. Suspicious keywords were found. Use olevba and mraptor for more info.'
        except Exception as e:
            vba_indicator.risk = RISK.ERROR
            vba_indicator.value = 'Error'
            vba_indicator.description = 'Error while checking VBA macros: %s' % str(e)
        self.indicators.append(vba_indicator)
        return vba_indicator
Пример #2
0
 def check_mraptor(self):
     '''
     Check the attachments of a message using mraptor.
     If an attachment is identified as suspicious, it is replaced by a simple text file.
     :return: Milter.ACCEPT or Milter.DISCARD if processing error
     '''
     msg = email.message_from_string(self.message.getvalue())
     result = Milter.ACCEPT
     try:
         for part in msg.walk():
             # for name, value in part.items():
             #     logging.debug(' - %s: %r' % (name, value))
             content_type = part.get_content_type()
             logging.debug('[%d] Content-type: %r' % (self.id, content_type))
             # TODO: handle any content-type, but check the file magic?
             if not content_type.startswith('multipart'):
                 filename = part.get_filename(None)
                 logging.debug('[%d] Analyzing attachment %r' % (self.id, filename))
                 attachment = part.get_payload(decode=True)
                 attachment_lowercase = attachment.lower()
                 # check if this is a supported file type (if not, just skip it)
                 # TODO: use is_zipfile instead of 'PK'
                 # TODO: this function should be provided by olevba
                 if attachment.startswith(olevba.olefile.MAGIC) \
                     or attachment.startswith('PK') \
                     or 'http://schemas.microsoft.com/office/word/2003/wordml' in attachment \
                     or ('mime' in attachment_lowercase and 'version' in attachment_lowercase
                         and 'multipart' in attachment_lowercase):
                     vba_parser = olevba.VBA_Parser(filename='message', data=attachment)
                     vba_code_all_modules = ''
                     for (subfilename, stream_path, vba_filename, vba_code) in vba_parser.extract_all_macros():
                         vba_code_all_modules += vba_code + '\n'
                     m = mraptor.MacroRaptor(vba_code_all_modules)
                     m.scan()
                     if m.suspicious:
                         logging.warning('[%d] The attachment %r contains a suspicious macro: replace it with a text file'
                                         % (self.id, filename))
                         part.set_payload('This attachment has been removed because it contains a suspicious macro.')
                         part.set_type('text/plain')
                         # TODO: handle case when CTE is absent
                         part.replace_header('Content-Transfer-Encoding', '7bit')
                         # for name, value in part.items():
                         #     logging.debug(' - %s: %r' % (name, value))
                         # TODO: archive filtered e-mail to a file
                     else:
                         logging.debug('The attachment %r is clean.'
                                         % filename)
     except Exception:
         logging.exception('[%d] Error while processing the message' % self.id)
         # TODO: depending on error, decide to forward the e-mail as-is or not
         result = Milter.DISCARD
     # TODO: only do this if the body has actually changed
     body = str(msg)
     self.message = io.BytesIO(body)
     self.replacebody(body)
     logging.info('[%d] Message relayed' % self.id)
     return result
Пример #3
0
    def check_macros(self):
        """
        Check whether this file contains macros (VBA and XLM/Excel 4).

        :returns: :py:class:`Indicator`
        """
        vba_indicator = Indicator(
            _id='vba',
            value='No',
            _type=str,
            name='VBA Macros',
            description='This file does not contain VBA macros.',
            risk=RISK.NONE,
            hide_if_false=False)
        self.indicators.append(vba_indicator)
        xlm_indicator = Indicator(
            _id='xlm',
            value='No',
            _type=str,
            name='XLM Macros',
            description='This file does not contain Excel 4/XLM macros.',
            risk=RISK.NONE,
            hide_if_false=False)
        self.indicators.append(xlm_indicator)
        if self.ftg.filetype == ftguess.FTYPE.RTF:
            # For RTF we don't call olevba otherwise it triggers an error
            vba_indicator.description = 'RTF files cannot contain VBA macros'
            xlm_indicator.description = 'RTF files cannot contain XLM macros'
            return vba_indicator, xlm_indicator
        vba_parser = None  # flag in case olevba fails
        try:
            vba_parser = olevba.VBA_Parser(filename=self.filename,
                                           data=self.data)
            if vba_parser.detect_vba_macros():
                vba_indicator.value = 'Yes'
                vba_indicator.risk = RISK.MEDIUM
                vba_indicator.description = 'This file contains VBA macros. No suspicious keyword was found. Use olevba and mraptor for more info.'
                # check code with mraptor
                vba_code = vba_parser.get_vba_code_all_modules()
                m = mraptor.MacroRaptor(vba_code)
                m.scan()
                if m.suspicious:
                    vba_indicator.value = 'Yes, suspicious'
                    vba_indicator.risk = RISK.HIGH
                    vba_indicator.description = 'This file contains VBA macros. Suspicious keywords were found. Use olevba and mraptor for more info.'
        except Exception as e:
            vba_indicator.risk = RISK.ERROR
            vba_indicator.value = 'Error'
            vba_indicator.description = 'Error while checking VBA macros: %s' % str(
                e)
        # Check XLM macros only for Excel file types:
        if self.ftg.is_excel():
            # TODO: for now XLM detection only works for files on disk... So we need to reload VBA_Parser from the filename
            #       To be improved once XLMMacroDeobfuscator can work on files in memory
            if self.file_on_disk:
                try:
                    vba_parser = olevba.VBA_Parser(filename=self.filename)
                    if vba_parser.detect_xlm_macros():
                        xlm_indicator.value = 'Yes'
                        xlm_indicator.risk = RISK.MEDIUM
                        xlm_indicator.description = 'This file contains XLM macros. Use olevba to analyse them.'
                except Exception as e:
                    xlm_indicator.risk = RISK.ERROR
                    xlm_indicator.value = 'Error'
                    xlm_indicator.description = 'Error while checking XLM macros: %s' % str(
                        e)
            else:
                xlm_indicator.risk = RISK.UNKNOWN
                xlm_indicator.value = 'Unknown'
                xlm_indicator.description = 'For now, XLM macros can only be detected for files on disk, not in memory'

        return vba_indicator, xlm_indicator
Пример #4
0
def run(analyzer_name, job_id, filepath, filename, md5,
        additional_config_params):
    logger.info("started analyzer {} job_id {}"
                "".format(analyzer_name, job_id))
    report = general.get_basic_report_template(analyzer_name)
    try:
        results = {}

        # olevba
        olevba_results = {}
        try:

            vbaparser = VBA_Parser(filepath)

            olevba_results[
                'macro_found'] = True if vbaparser.detect_vba_macros(
                ) else False

            if olevba_results['macro_found']:
                macro_data = []
                for (v_filename, stream_path, vba_filename,
                     vba_code) in vbaparser.extract_macros():
                    extracted_macro = {
                        "filename": v_filename,
                        "ole_stream": stream_path,
                        "vba_filename": vba_filename,
                        "vba_code": vba_code
                    }
                    macro_data.append(extracted_macro)
                olevba_results['macro_data'] = macro_data

                # example output
                '''
                {'description': 'Runs when the Word document is opened',
                 'keyword': 'AutoOpen',
                 'type': 'AutoExec'},
                {'description': 'May run an executable file or a system command',
                 'keyword': 'Shell',
                 'type': 'Suspicious'},
                {'description': 'May run an executable file or a system command',
                 'keyword': 'WScript.Shell',
                 'type': 'Suspicious'},
                {'description': 'May run an executable file or a system command',
                 'keyword': 'Run',
                 'type': 'Suspicious'},
                {'description': 'May run PowerShell commands',
                 'keyword': 'powershell',
                 'type': 'Suspicious'},
                {'description': '9BA55BE5', 'keyword': 'xxx', 'type': 'Hex String'},
                 '''
                analyzer_results = vbaparser.analyze_macros(
                    show_decoded_strings=True)
                # it gives None if it does not find anything
                if analyzer_results:
                    analyze_macro_results = []
                    for kw_type, keyword, description in analyzer_results:
                        if kw_type != 'Hex String':
                            analyze_macro_result = {
                                "type": kw_type,
                                "keyword": keyword,
                                "description": description
                            }
                            analyze_macro_results.append(analyze_macro_result)
                    olevba_results['analyze_macro'] = analyze_macro_results

                olevba_results['reveal'] = vbaparser.reveal()

            vbaparser.close()

        except Exception as e:
            traceback.print_exc()
            error_message = "job_id {} vba parser failed. Error: {}".format(
                job_id, e)
            logger.exception(error_message)
            report['errors'].append(error_message)

        results['olevba'] = olevba_results

        # mraptor
        macro_raptor = mraptor.MacroRaptor(olevba_results.get('reveal', ''))
        if macro_raptor:
            macro_raptor.scan()
            results[
                'mraptor'] = "suspicious" if macro_raptor.suspicious else 'ok'

        # pprint.pprint(results)
        report['report'] = results
    except AnalyzerRunException as e:
        error_message = "job_id:{} analyzer:{} md5:{} filename: {} Analyzer Error {}" \
                        "".format(job_id, analyzer_name, md5, filename, e)
        logger.error(error_message)
        report['errors'].append(error_message)
        report['success'] = False
    except Exception as e:
        traceback.print_exc()
        error_message = "job_id:{} analyzer:{} md5:{} filename: {} Unexpected Error {}" \
                        "".format(job_id, analyzer_name, md5, filename, e)
        logger.exception(error_message)
        report['errors'].append(str(e))
        report['success'] = False
    else:
        report['success'] = True

    general.set_report_and_cleanup(job_id, report, logger)

    logger.info("ended analyzer {} job_id {}" "".format(analyzer_name, job_id))

    return report
Пример #5
0
    def run(self):
        results = {}

        # olevba
        try:
            self.vbaparser = VBA_Parser(self.filepath)

            self.manage_encrypted_doc()

            if self.experimental:
                self.experimental_analysis()

            # go on with the normal oletools execution
            self.olevba_results["macro_found"] = self.vbaparser.detect_vba_macros()

            if self.olevba_results["macro_found"]:
                vba_code_all_modules = ""
                macro_data = []
                for (
                    v_filename,
                    stream_path,
                    vba_filename,
                    vba_code,
                ) in self.vbaparser.extract_macros():
                    extracted_macro = {
                        "filename": v_filename,
                        "ole_stream": stream_path,
                        "vba_filename": vba_filename,
                        "vba_code": vba_code,
                    }
                    macro_data.append(extracted_macro)
                    vba_code_all_modules += vba_code + "\n"
                self.olevba_results["macro_data"] = macro_data

                # example output
                #
                # {'description': 'Runs when the Word document is opened',
                #  'keyword': 'AutoOpen',
                #  'type': 'AutoExec'},
                # {'description': 'May run an executable file or a system command',
                #  'keyword': 'Shell',
                #  'type': 'Suspicious'},
                # {'description': 'May run an executable file or a system command',
                #  'keyword': 'WScript.Shell',
                #  'type': 'Suspicious'},
                # {'description': 'May run an executable file or a system command',
                #  'keyword': 'Run',
                #  'type': 'Suspicious'},
                # {'description': 'May run PowerShell commands',
                #  'keyword': 'powershell',
                #  'type': 'Suspicious'},
                # {'description': '9BA55BE5', 'keyword': 'xxx', 'type': 'Hex String'},

                # mraptor
                macro_raptor = mraptor.MacroRaptor(vba_code_all_modules)
                if macro_raptor:
                    macro_raptor.scan()
                    results["mraptor"] = (
                        "suspicious" if macro_raptor.suspicious else "ok"
                    )

                # analyze macros
                analyzer_results = self.vbaparser.analyze_macros()
                # it gives None if it does not find anything
                if analyzer_results:
                    analyze_macro_results = []
                    for kw_type, keyword, description in analyzer_results:
                        if kw_type != "Hex String":
                            analyze_macro_result = {
                                "type": kw_type,
                                "keyword": keyword,
                                "description": description,
                            }
                            analyze_macro_results.append(analyze_macro_result)
                    self.olevba_results["analyze_macro"] = analyze_macro_results

        except CannotDecryptException as e:
            logger.info(e)
        except Exception as e:
            error_message = f"job_id {self.job_id} vba parser failed. Error: {e}"
            logger.exception(error_message)
            self.report["errors"].append(error_message)
        finally:
            if self.vbaparser:
                self.vbaparser.close()

        results["olevba"] = self.olevba_results

        return results
Пример #6
0
    def run(self):
        results = {}
        # olevba
        olevba_results = {}
        try:
            vbaparser = VBA_Parser(self.filepath)

            olevba_results["macro_found"] = (
                True if vbaparser.detect_vba_macros() else False
            )

            if olevba_results["macro_found"]:
                macro_data = []
                for (
                    v_filename,
                    stream_path,
                    vba_filename,
                    vba_code,
                ) in vbaparser.extract_macros():
                    extracted_macro = {
                        "filename": v_filename,
                        "ole_stream": stream_path,
                        "vba_filename": vba_filename,
                        "vba_code": vba_code,
                    }
                    macro_data.append(extracted_macro)
                olevba_results["macro_data"] = macro_data

                # example output
                """
                {'description': 'Runs when the Word document is opened',
                 'keyword': 'AutoOpen',
                 'type': 'AutoExec'},
                {'description': 'May run an executable file or a system command',
                 'keyword': 'Shell',
                 'type': 'Suspicious'},
                {'description': 'May run an executable file or a system command',
                 'keyword': 'WScript.Shell',
                 'type': 'Suspicious'},
                {'description': 'May run an executable file or a system command',
                 'keyword': 'Run',
                 'type': 'Suspicious'},
                {'description': 'May run PowerShell commands',
                 'keyword': 'powershell',
                 'type': 'Suspicious'},
                {'description': '9BA55BE5', 'keyword': 'xxx', 'type': 'Hex String'},
                 """
                analyzer_results = vbaparser.analyze_macros(show_decoded_strings=True)
                # it gives None if it does not find anything
                if analyzer_results:
                    analyze_macro_results = []
                    for kw_type, keyword, description in analyzer_results:
                        if kw_type != "Hex String":
                            analyze_macro_result = {
                                "type": kw_type,
                                "keyword": keyword,
                                "description": description,
                            }
                            analyze_macro_results.append(analyze_macro_result)
                    olevba_results["analyze_macro"] = analyze_macro_results

                olevba_results["reveal"] = vbaparser.reveal()

            vbaparser.close()

        except Exception as e:
            traceback.print_exc()
            error_message = f"job_id {self.job_id} vba parser failed. Error: {e}"
            logger.exception(error_message)
            self.report["errors"].append(error_message)

        results["olevba"] = olevba_results

        # mraptor
        macro_raptor = mraptor.MacroRaptor(olevba_results.get("reveal", None))
        if macro_raptor:
            macro_raptor.scan()
            results["mraptor"] = "suspicious" if macro_raptor.suspicious else "ok"

        return results
Пример #7
0
    def checkforVBA(self, msg):
        '''
			Checks if it contains a vba macro and checks if user is whitelisted or file already parsed
		'''
        # Accept all messages with no attachment
        result = Milter.ACCEPT
        try:
            for part in msg.walk():
                # for name, value in part.items():
                #     log.debug(' - %s: %r' % (name, value))
                content_type = part.get_content_type()
                log.debug('[%d] Content-Type: %r' % (self.id, content_type))
                # TODO: handle any content-type, but check the file magic?
                if not content_type.startswith('multipart'):
                    filename = part.get_filename(None)
                    attachment = part.get_payload(decode=True)
                    if attachment is None:
                        return Milter.CONTINUE
                    log.debug('[%d] Analyzing attachment: %r' %
                              (self.id, filename))
                    attachment_lowercase = attachment.lower()
                    attachment_fileobj = StringIO.StringIO(attachment)
                    # check if file was already parsed
                    if self.fileHasAlreadyBeenParsed(attachment):
                        return Milter.REJECT
                    # check if this is a supported file type (if not, just skip it)
                    # TODO: this function should be provided by olevba
                    if olefile.isOleFile(attachment_fileobj) or is_zipfile(attachment_fileobj) or 'http://schemas.microsoft.com/office/word/2003/wordml' in attachment \
                     or ('mime' in attachment_lowercase and 'version' in attachment_lowercase \
                     and 'multipart' in attachment_lowercase):
                        vba_code_all_modules = ''
                        # check if the attachment is a zip
                        if not olefile.isOleFile(attachment_fileobj):
                            extn = (os.path.splitext(filename)[1]).lower()
                            # skip non archives
                            if is_zipfile(attachment_fileobj) and not (
                                    ".docx" in extn or ".xlsx" in extn
                                    or ".pptx" in extn):
                                # extract all file in zip and add
                                try:
                                    zipvba = self.getZipFiles(
                                        attachment, filename)
                                    vba_code_all_modules += zipvba + '\n'
                                except ToManyZipException:
                                    log.warning(
                                        "[%d] Attachment %s is reached the max. nested zip count! ZipBomb?: REJECT"
                                        % (self.id, filename))
                                    # rewrite the reject message
                                    self.setreply(
                                        '550', '5.7.2',
                                        "The message contains a suspicious archive and was rejected!"
                                    )
                                    return Milter.REJECT
                        # check the rest of the message
                        vba_parser = olevba.VBA_Parser(filename='message',
                                                       data=attachment)
                        for (subfilename, stream_path, vba_filename,
                             vba_code) in vba_parser.extract_all_macros():
                            vba_code_all_modules += vba_code + '\n'
                        # run the mraptor
                        m = mraptor.MacroRaptor(vba_code_all_modules)
                        m.scan()
                        if m.suspicious:
                            # Add MD5 to the database
                            self.addHashtoDB(attachment)
                            # Replace the attachment or reject it
                            if REJECT_MESSAGE:
                                log.warning(
                                    '[%d] The attachment %r contains a suspicious macro: REJECT'
                                    % (self.id, filename))
                                result = Milter.REJECT
                            else:
                                log.warning(
                                    '[%d] The attachment %r contains a suspicious macro: replace it with a text file'
                                    % (self.id, filename))
                                part.set_payload(
                                    'This attachment has been removed because it contains a suspicious macro.'
                                )
                                part.set_type('text/plain')
                                part.replace_header(
                                    'Content-Transfer-Encoding', '7bit')
                        else:
                            log.debug('[%d] The attachment %r is clean.' %
                                      (self.id, filename))

        except Exception:
            log.error('[%d] Error while processing the message' % self.id)
            exc_type, exc_value, exc_traceback = sys.exc_info()
            lines = traceback.format_exception(exc_type, exc_value,
                                               exc_traceback)
            exep = ''.join('!! ' + line for line in lines)
            log.debug("[%d] Exeption code: [%s]" % (self.id, exep))

        if REJECT_MESSAGE is False:
            body = str(msg)
            self.message = io.BytesIO(body)
            self.replacebody(body)
            log.info('[%d] Message relayed' % self.id)
        return result