def olevba_streams(self, args, file, opts):
        output = []
        try:
            vbaparser = olevba3.VBA_Parser(file.file_path)
        except Exception:
            raise error.CommandWarning('file ' + str(file.file_path) + ' is not a valid ole file')

        try:
            vbaparser.detect_vba_macros()
        except:
            vbaparser.close()
            raise error.CommandWarning('no macro was detected on this file')

        try:
            macros = vbaparser.extract_all_macros()
        except:
            raise error.CommandWarning('vbaparser.extract_all_macros() failed to extract macros')
        i = 1
        for m in macros:  # pylint: disable=invalid-name
            try:
                output += [{
                    'stream': str(i),
                    'stream_path': str(m[1]),
                    'vba_filename': str(m[2]),
                    'code': str(m[3].decode('utf-8'))
                }]
            except:
                output += [{
                    'stream': str(i),
                    'stream_path': str(m[1]),
                    'vba_filename': str(m[2]),
                    'code': str(m[3])
                }]
            i += 1
        return output
Exemple #2
0
    def scan(self, data, file, options, expire_at):
        analyze_macros = options.get('analyze_macros', True)

        self.event['total'] = {'files': 0, 'extracted': 0}

        try:
            vba = olevba3.VBA_Parser(filename=file.name, data=data)
            if vba.detect_vba_macros():
                extract_macros = list(vba.extract_macros())
                self.event['total']['files'] = len(extract_macros)
                for (filename, stream_path, vba_filename,
                     vba_code) in extract_macros:
                    extract_file = strelka.File(
                        name=f'{vba_filename}',
                        source=self.name,
                    )

                    for c in strelka.chunk_string(vba_code):
                        self.upload_to_coordinator(
                            extract_file.pointer,
                            c,
                            expire_at,
                        )

                    self.files.append(extract_file)
                    self.event['total']['extracted'] += 1

                if analyze_macros:
                    self.event.setdefault('auto_exec', [])
                    self.event.setdefault('base64', [])
                    self.event.setdefault('dridex', [])
                    self.event.setdefault('hex', [])
                    self.event.setdefault('ioc', [])
                    self.event.setdefault('suspicious', [])
                    macros = vba.analyze_macros()
                    for (macro_type, keyword, description) in macros:
                        if macro_type == 'AutoExec':
                            self.event['auto_exec'].append(keyword)
                        elif macro_type == 'Base64 String':
                            self.event['base64'].append(keyword)
                        elif macro_type == 'Dridex String':
                            self.event['dridex'].append(keyword)
                        elif macro_type == 'Hex String':
                            self.event['hex'].append(keyword)
                        elif macro_type == 'IOC':
                            self.event['ioc'].append(keyword)
                        elif macro_type == 'Suspicious':
                            self.event['suspicious'].append(keyword)

        except olevba3.FileOpenError:
            self.flags.append('file_open_error')
        finally:
            # TODO referenced before potential assignment as vba is opened in a try / catch block
            vba.close()
Exemple #3
0
    def scan(self, file_object, options):
        analyze_macros = options.get("analyze_macros", True)

        self.metadata["total"] = {"files": 0, "extracted": 0}

        try:
            vba_parser = olevba3.VBA_Parser(filename=file_object.filename,
                                            data=file_object.data)
            if vba_parser.detect_vba_macros():
                extract_macros = list(vba_parser.extract_macros())
                self.metadata["total"]["files"] = len(extract_macros)
                for (filename, stream_path, vba_filename,
                     vba_code) in extract_macros:
                    child_filename = f"{self.scanner_name}::{vba_filename}"
                    child_fo = objects.StrelkaFile(
                        data=vba_code,
                        filename=child_filename,
                        depth=file_object.depth + 1,
                        parent_uid=file_object.uid,
                        root_uid=file_object.root_uid,
                        parent_hash=file_object.hash,
                        root_hash=file_object.root_hash,
                        source=self.scanner_name)
                    self.children.append(child_fo)
                    self.metadata["total"]["extracted"] += 1

                if analyze_macros:
                    self.metadata.setdefault("autoExec", [])
                    self.metadata.setdefault("base64", [])
                    self.metadata.setdefault("dridex", [])
                    self.metadata.setdefault("hex", [])
                    self.metadata.setdefault("ioc", [])
                    self.metadata.setdefault("suspicious", [])
                    macros = vba_parser.analyze_macros()
                    for (type, keyword, description) in macros:
                        if type == "AutoExec":
                            self.metadata["autoExec"].append(keyword)
                        elif type == "Base64 String":
                            self.metadata["base64"].append(keyword)
                        elif type == "Dridex String":
                            self.metadata["dridex"].append(keyword)
                        elif type == "Hex String":
                            self.metadata["hex"].append(keyword)
                        elif type == "IOC":
                            self.metadata["ioc"].append(keyword)
                        elif type == "Suspicious":
                            self.metadata["suspicious"].append(keyword)
            vba_parser.close()

        except olevba3.FileOpenError:
            file_object.flags.append(f"{self.scanner_name}::file_open_error")
Exemple #4
0
    def mraptor(self, args, file, opts):

        # Monkeypatch 1 - This is to force the script argument to the appropriate file locaiton for analysis
        def temp_args(_a, _b, _c, _d):
            return [file.file_path]

        # Deploy Monkeypatch 1
        import optparse
        get_args = optparse.OptionParser._get_args
        optparse.OptionParser._get_args = temp_args

        # Monkeypatch - This rediverts stdout to a stream that can be collected later for results
        sys.stdout = io.StringIO()
        result = []

        try:
            vbaparser = olevba3.VBA_Parser(file.file_path)
        except Exception:
            raise error.CommandWarning('file ' + str(file.file_path) + ' is not a valid ole file')
        
        filetype = olevba3.TYPE2TAG[vbaparser.type]

        if not vbaparser.detect_vba_macros():
            vbaparser.close()
            raise error.CommandWarning('file does not have macros')

        try:
            vba_code_all_modules = ''
            for (subfilename, stream_path, vba_filename, vba_code) in vbaparser.extract_all_macros():
                vba_code_all_modules += vba_code + '\n'
            m = mraptor3.MacroRaptor(vba_code_all_modules)
            m.scan()
            if m.suspicious:
                result += [{
                    'Result': 'SUSPICIOUS',
                    'Flags': str(m.get_flags()),
                    'Match_on': str(m.matches)
                }]
            else:
                result += [{ 'Result': 'Macro seems fine' }]
        except Exception:
            # Revert patched functions to originals
            optparse.OptionParser._get_args = get_args
            sys.stdout = sys.__stdout__
            raise error.CommandWarning('failed to parse macros')

        # Revert patched function to originals
        optparse.OptionParser._get_args = get_args
        sys.stdout = sys.__stdout__
        reload(oledir)
        return result
Exemple #5
0
def html_vba_dump(args, office_filename, outfile):
    vba_parser = vba3.VBA_Parser(office_filename)
    for (filename, stream_path, vba_filename, vba_code) in vba_parser.extract_macros():
        # The stream_path should be the module name with a "VBA/" prefix; e.g., "VBA/Exercises"
        stream_path = html.escape(stream_path)
        if args.modules is None or stream_path[stream_path.find("/") + 1:] in args.modules:
            if args.sort:
                # If the user specified the sort option, the summary tag will only
                # show Lastname, Firstname, show the full file name in the output.
                print("""<h3 style="margin-left: 20px">{}: {}</h3>""".format(html.escape(office_filename),
                                                                             html.escape(stream_path)), file=outfile)

            # Pre-format and pretty-print the code.
            print("""<pre style="margin-left: 20px" class="prettyprint lang-vb">""", file=outfile)
            print(vba_code.decode("utf-8").replace("\r", ""), file=outfile)
            print("""</pre>""", file=outfile)

            # Show progress if the user has asked for it.
            if args.verbose:
                print("""{}: {}""".format(html.escape(office_filename), html.escape(stream_path)))
    def olevba_keywords(self, args, file, opts):
        try:
            vbaparser = olevba3.VBA_Parser(file.file_path)
        except Exception:
            raise error.CommandWarning('file ' + str(file.file_path) + ' is not a valid ole file')

        output = []
        if not vbaparser.detect_vba_macros():
            vbaparser.close()
            return output

        results = vbaparser.analyze_macros()
        for kw_type, keyword, description in results:
            output += [{
                'type': kw_type,
                'keyword': str(str(keyword).encode('utf-8'))[2:-1],
                'description': description
            }]
        vbaparser.close()
        return output
    def scan(self, payload: Payload, request_meta: RequestMeta) -> WorkerResponse:
        results = {}
        filename = payload.payload_meta.extra_data.get('filename', payload.payload_id)
        vba_parser = olevba.VBA_Parser(filename=filename, data=payload.content)

        if vba_parser.detect_vba_macros():
            vba_modules = [
                vba_code[3].decode('utf-8', 'replace')
                for vba_code in vba_parser.extract_all_macros()
            ]
            vba_modules = '\n'.join(vba_modules)
            mraptor = MacroRaptor(vba_modules)
            mraptor.scan()
            flags = [
                self.FLAGS[flag] for flag in mraptor.get_flags() if flag in self.FLAGS
            ]
            results = {
                'suspicous': mraptor.suspicious,
                'flags': flags,
                'filetype': vba_parser.type,
                'matches': mraptor.matches,
            }

        return WorkerResponse(results)
Exemple #8
0
def main():
    """
    Main function, called when olevba is run from the command line
    """
    global log
    DEFAULT_LOG_LEVEL = "warning"  # Default log level
    LOG_LEVELS = {
        'debug': logging.DEBUG,
        'info': logging.INFO,
        'warning': logging.WARNING,
        'error': logging.ERROR,
        'critical': logging.CRITICAL
    }

    usage = 'usage: %prog [options] <filename> [filename2 ...]'
    parser = optparse.OptionParser(usage=usage)
    parser.add_option("-r",
                      action="store_true",
                      dest="recursive",
                      help='find files recursively in subdirectories.')
    parser.add_option(
        "-z",
        "--zip",
        dest='zip_password',
        type='str',
        default=None,
        help=
        'if the file is a zip archive, open all files from it, using the provided password (requires Python 2.6+)'
    )
    parser.add_option(
        "-f",
        "--zipfname",
        dest='zip_fname',
        type='str',
        default='*',
        help=
        'if the file is a zip archive, file(s) to be opened within the zip. Wildcards * and ? are supported. (default:*)'
    )
    parser.add_option(
        '-l',
        '--loglevel',
        dest="loglevel",
        action="store",
        default=DEFAULT_LOG_LEVEL,
        help=
        "logging level debug/info/warning/error/critical (default=%default)")
    parser.add_option("-m",
                      '--matches',
                      action="store_true",
                      dest="show_matches",
                      help='Show matched strings.')

    # TODO: add logfile option

    (options, args) = parser.parse_args()

    # Print help if no arguments are passed
    if len(args) == 0:
        print('MacroRaptor %s - http://decalage.info/python/oletools' %
              __version__)
        print('This is work in progress, please report issues at %s' %
              URL_ISSUES)
        print(__doc__)
        parser.print_help()
        print('\nAn exit code is returned based on the analysis result:')
        for result in (Result_NoMacro, Result_NotMSOffice, Result_MacroOK,
                       Result_Error, Result_Suspicious):
            print(' - %d: %s' % (result.exit_code, result.name))
        sys.exit()

    # print banner with version
    print('MacroRaptor %s - http://decalage.info/python/oletools' %
          __version__)
    print('This is work in progress, please report issues at %s' % URL_ISSUES)

    logging.basicConfig(level=LOG_LEVELS[options.loglevel],
                        format='%(levelname)-8s %(message)s')
    # enable logging in the modules:
    log.setLevel(logging.NOTSET)

    t = tablestream.TableStream(style=tablestream.TableStyleSlim,
                                header_row=['Result', 'Flags', 'Type', 'File'],
                                column_width=[10, 5, 4, 56])

    exitcode = -1
    global_result = None
    # TODO: handle errors in xglob, to continue processing the next files
    for container, filename, data in xglob.iter_files(
            args,
            recursive=options.recursive,
            zip_password=options.zip_password,
            zip_fname=options.zip_fname):
        # ignore directory names stored in zip files:
        if container and filename.endswith('/'):
            continue
        full_name = '%s in %s' % (filename,
                                  container) if container else filename
        # try:
        #     # Open the file
        #     if data is None:
        #         data = open(filename, 'rb').read()
        # except:
        #     log.exception('Error when opening file %r' % full_name)
        #     continue
        if isinstance(data, Exception):
            result = Result_Error
            t.write_row([result.name, '', '', full_name],
                        colors=[result.color, None, None, None])
            t.write_row(['', '', '', str(data)],
                        colors=[None, None, None, result.color])
        else:
            filetype = '???'
            try:
                vba_parser = olevba.VBA_Parser(filename=filename,
                                               data=data,
                                               container=container)
                filetype = TYPE2TAG[vba_parser.type]
            except Exception as e:
                # log.error('Error when parsing VBA macros from file %r' % full_name)
                # TODO: distinguish actual errors from non-MSOffice files
                result = Result_Error
                t.write_row([result.name, '', filetype, full_name],
                            colors=[result.color, None, None, None])
                t.write_row(['', '', '', str(e)],
                            colors=[None, None, None, result.color])
                continue
            if vba_parser.detect_vba_macros():
                vba_code_all_modules = ''
                try:
                    for (subfilename, stream_path, vba_filename,
                         vba_code) in vba_parser.extract_all_macros():
                        vba_code_all_modules += vba_code.decode(
                            'utf-8', 'replace') + '\n'
                except Exception as e:
                    # log.error('Error when parsing VBA macros from file %r' % full_name)
                    result = Result_Error
                    t.write_row([
                        result.name, '', TYPE2TAG[vba_parser.type], full_name
                    ],
                                colors=[result.color, None, None, None])
                    t.write_row(['', '', '', str(e)],
                                colors=[None, None, None, result.color])
                    continue
                mraptor = MacroRaptor(vba_code_all_modules)
                mraptor.scan()
                if mraptor.suspicious:
                    result = Result_Suspicious
                else:
                    result = Result_MacroOK
                t.write_row(
                    [result.name,
                     mraptor.get_flags(), filetype, full_name],
                    colors=[result.color, None, None, None])
                if mraptor.matches and options.show_matches:
                    t.write_row(['', '', '', 'Matches: %r' % mraptor.matches])
            else:
                result = Result_NoMacro
                t.write_row([result.name, '', filetype, full_name],
                            colors=[result.color, None, None, None])
        if result.exit_code > exitcode:
            global_result = result
            exitcode = result.exit_code

    print('')
    print('Flags: A=AutoExec, W=Write, X=Execute')
    print('Exit code: %d - %s' % (exitcode, global_result.name))
    sys.exit(exitcode)