Beispiel #1
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():
             #     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)
                 log.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: this function should be provided by olevba
                 if attachment.startswith(olevba.olefile.MAGIC) \
                     or is_zipfile(StringIO.StringIO(attachment)) \
                     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:
                         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')
                         # TODO: handle case when CTE is absent
                         part.replace_header('Content-Transfer-Encoding', '7bit')
                         # for name, value in part.items():
                         #     log.debug(' - %s: %r' % (name, value))
                         # TODO: archive filtered e-mail to a file
                     else:
                         log.debug('The attachment %r is clean.'
                                         % filename)
     except Exception:
         log.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)
     log.info('[%d] Message relayed' % self.id)
     return result
Beispiel #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():
             #     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)
                 log.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: this function should be provided by olevba
                 if attachment.startswith(olevba.olefile.MAGIC) \
                     or is_zipfile(StringIO.StringIO(attachment)) \
                     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:
                         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')
                         # TODO: handle case when CTE is absent
                         part.replace_header('Content-Transfer-Encoding', '7bit')
                         # for name, value in part.items():
                         #     log.debug(' - %s: %r' % (name, value))
                         # TODO: archive filtered e-mail to a file
                     else:
                         log.debug('The attachment %r is clean.'
                                         % filename)
     except Exception:
         log.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)
     log.info('[%d] Message relayed' % self.id)
     return result
Beispiel #3
0
    def zipwalk(self, zfilename, count, tmpfiles):

        z = ZipFile(zfilename, 'r')
        # start walk
        for info in z.infolist():
            fname = info.filename
            data = z.read(fname)
            extn = (os.path.splitext(fname)[1]).lower()

            # create a random secure temp file
            tmp_fs, tmpfpath = tempfile.mkstemp(suffix=extn)
            # add tmp filename to list
            tmpfiles.append(tmpfpath)

            if extn == '.zip' or extn == '.7z':
                checkz = False
                # use a context manager to open the file
                with open(tmpfpath, 'w') as f:
                    f.write(data)

                if is_zipfile(tmpfpath):
                    checkz = True
                    count = count + 1
                    # check each round
                    if count > MAX_ZIP:
                        self.deleteFileRecursive(tmpfiles)
                        tmpfiles = []
                        raise ToManyZipException(
                            "[%d] Too many nested zips found - possible zipbomb!"
                            % self.id)
                if checkz and not olefile.isOleFile(data):
                    try:
                        # recursive call if nested
                        for x in self.zipwalk(tmpfpath, count, tmpfiles):
                            yield x
                    except Exception:
                        self.deleteFileRecursive(tmpfiles)
                        tmpfiles = []
                        raise
            else:
                # return the generator
                yield (info, data)

        # cleanup tmp
        self.deleteFileRecursive(tmpfiles)
        tmpfiles = []
Beispiel #4
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