Beispiel #1
0
def EXTRACT_VBA_MACRO(s, buff):

    EXTRACT_MACRO = {}
    counter = 0

    ### TODO: REMOVE THIS WORKAROUND ONCE MODULE AUTHOR FIXES CODE ###
    ### Reference: http://stackoverflow.com/questions/32261679/strange-issue-using-logging-module-in-python/32264445#32264445
    ### Reference: https://bitbucket.org/decalage/oletools/issues/26/use-of-logger
    ### /dev/null used instead of NullHandler for 2.6 compatibility
    logging.getLogger('workaround').root.addHandler(
        logging.FileHandler('/dev/null'))
    ###

    vba = VBA_Parser('None', data=buff)

    if not vba.detect_vba_macros():
        return EXTRACT_MACRO

    for (filename, stream_path, vba_filename,
         vba_code) in vba.extract_macros():

        CHILD_MACRO = OrderedDict([('OLE Stream', stream_path),
                                   ('VBA Filename',
                                    vba_filename.decode('ascii', 'ignore')),
                                   ('Scan', scan_macro(vba_code)),
                                   ('Buffer', vba_code)])

        EXTRACT_MACRO['Object_%s' % counter] = CHILD_MACRO
        counter += 1

    return EXTRACT_MACRO
Beispiel #2
0
def parseOLEDocument(f):
	"""Parse an OLE document for VBA macros"""
	if not f or not useOLETools:
		return

	writeLog('DEBUG: Analyzing with oletools')
	try:
		v = VBA_Parser(f)
	except:
		writeLog("Not a supported file format: %s" % f)
		return
	writeLog('DEBUG: Detected file type: %s' % v.type)
	if v.detect_vba_macros():
		writeLog('DEBUG: VBA Macros found')
		try:
			t = open("%s.analysis" % f, 'w')
		except IOError as e:
			writeLog("Cannot create analysis file %s.analysis: %s" % (f,e.strerror))
			return
		for kw_type, keyword, description in v.analyze_macros():
			t.write("%-12s | %-25s | %s\n" % (kw_type, keyword, description))
		t.close()
		writeLog("DEBUG: Analysis dumped to %s.analysis" % f)
	else:
		writeLog('DEBUG: No VBA Macros found')
	return
def EXTRACT_VBA_MACRO(s, buff):

   EXTRACT_MACRO = {}
   counter = 0

   ### TODO: REMOVE THIS WORKAROUND ONCE MODULE AUTHOR FIXES CODE ###
   ### Reference: http://stackoverflow.com/questions/32261679/strange-issue-using-logging-module-in-python/32264445#32264445
   ### Reference: https://bitbucket.org/decalage/oletools/issues/26/use-of-logger
   ### /dev/null used instead of NullHandler for 2.6 compatibility 
   logging.getLogger('workaround').root.addHandler(logging.FileHandler('/dev/null'))
   ###

   vba = VBA_Parser('None', data=buff)

   if not vba.detect_vba_macros():
      return EXTRACT_MACRO

   for (filename, stream_path, vba_filename, vba_code) in vba.extract_macros():

      CHILD_MACRO = OrderedDict([('OLE Stream', stream_path),
                                 ('VBA Filename', vba_filename.decode('ascii', 'ignore')),
                                 ('Scan', scan_macro(vba_code)),
                                 ('Buffer', vba_code)])

      EXTRACT_MACRO['Object_%s' % counter] = CHILD_MACRO
      counter += 1

   return EXTRACT_MACRO
Beispiel #4
0
def MacroHunter(targetFile):
    answerTable = PrettyTable()
    answerTable.field_names = [f"{green}Threat Levels{white}", f"{green}Macros{white}", f"{green}Descriptions{white}"]

    print(f"\n{infoS} Looking for VBA Macros...")
    try:
        fileData = open(targetFile, "rb").read()
        vbaparser = VBA_Parser(targetFile, fileData)
        macroList = list(vbaparser.analyze_macros())
        if vbaparser.contains_macros == True:
            for fi in range(0, len(macroList)):
                if macroList[fi][0] == 'Suspicious':
                    if "(use option --deobf to deobfuscate)" in macroList[fi][2]:
                        sanitized = f"{macroList[fi][2]}".replace("(use option --deobf to deobfuscate)", "")
                        answerTable.add_row([f"{yellow}{macroList[fi][0]}{white}", f"{macroList[fi][1]}", f"{sanitized}"])
                    elif "(option --decode to see all)" in macroList[fi][2]:
                        sanitized = f"{macroList[fi][2]}".replace("(option --decode to see all)", "")
                        answerTable.add_row([f"{yellow}{macroList[fi][0]}{white}", f"{macroList[fi][1]}", f"{sanitized}"])
                    else:
                        answerTable.add_row([f"{yellow}{macroList[fi][0]}{white}", f"{macroList[fi][1]}", f"{macroList[fi][2]}"])
                elif macroList[fi][0] == 'IOC':
                    answerTable.add_row([f"{magenta}{macroList[fi][0]}{white}", f"{macroList[fi][1]}", f"{macroList[fi][2]}"])
                elif macroList[fi][0] == 'AutoExec':
                    answerTable.add_row([f"{red}{macroList[fi][0]}{white}", f"{macroList[fi][1]}", f"{macroList[fi][2]}"])
                else:
                    answerTable.add_row([f"{macroList[fi][0]}", f"{macroList[fi][1]}", f"{macroList[fi][2]}"])
            print(f"{answerTable}\n")
        else:
            print(f"{errorS} Not any VBA macros found.")
    except:
        print(f"{errorS} An error occured while parsing that file for macro scan.")
Beispiel #5
0
def olevba_trig(file):
    try:
        vbaparser = VBA_Parser(file)
        if vbaparser.detect_vba_macros():
            for (filename, stream_path, vba_filename,
                 vba_code) in vbaparser.extract_macros():
                print('Filename    :', filename)
                print('OLE stream  :', stream_path)
                print('VBA filename:', vba_filename)
                print('- ' * 39)
                print(vba_code)
                print('- ' * 39)

                results = vbaparser.analyze_macros()
                for kw_type, keyword, description in results:
                    print('type=%s - keyword=%s - description=%s' %
                          (kw_type, keyword, description))

                print('AutoExec keywords: %d' % vbaparser.nb_autoexec)
                print('Suspicious keywords: %d' % vbaparser.nb_suspicious)
                print('IOCs: %d' % vbaparser.nb_iocs)
                print('Hex obfuscated strings: %d' % vbaparser.nb_hexstrings)
                print('Base64 obfuscated strings: %d' %
                      vbaparser.nb_base64strings)
                print('Dridex obfuscated strings: %d' %
                      vbaparser.nb_dridexstrings)
                print('VBA obfuscated strings: %d' % vbaparser.nb_vbastrings)
                print("\n")

    except:
        e = sys.exc_info()[0]
        print(f'{e} from {file}')
Beispiel #6
0
def _get_vba_parser(data):
    """Get an olevba VBA_Parser object for reading an Office file. This
    handles regular Office files and HTA files with VBScript script
    elements.

    @param data (str) The file contents for which to generate a
    VBA_Parser.

    @return (VBA_Parser object) On success, the olevba VBA_Parser
    object for the given file contents. On error, None.

    """
    
    # First just try the most common case where olevba can directly get the VBA.
    vba = None
    try:
        vba = VBA_Parser('', data, relaxed=True)
    except Exception as e:

        if (log.getEffectiveLevel() == logging.DEBUG):
            log.debug("Creating VBA_PArser() Failed. Trying as HTA. " + safe_str_convert(e))
        
        # If that did not work see if we can pull HTA wrapped VB from the data.
        extracted_data = get_vb_contents_from_hta(data)

        # If this throws an exception it will get passed up.
        vba = VBA_Parser('', extracted_data, relaxed=True)

    # Return the vba parser.
    return vba
Beispiel #7
0
def vba2graph_from_vba_object(filepath):
    """ vba2graph as library
    Args:
        filepath (string): path to file
    """
    logger.info("Extracting macros from file")
    if HAVE_OLETOOLS:
        try:
            vba = VBA_Parser(filepath)
        except Exception as e:
            return False
    full_vba_code = ""
    for (subfilename, stream_path, vba_filename,
         vba_code) in vba.extract_macros():
        full_vba_code += 'VBA MACRO %s \n' % vba_filename
        full_vba_code += '- ' * 39 + '\n'
        # Temporary workaround. Change when oletools 0.56 will be released.
        if isinstance(vba_code, bytes):
            vba_code = vba_code.decode('utf8', errors='replace')
        full_vba_code += vba_code
    vba.close()
    if full_vba_code:
        input_vba_content = handle_olevba_input(full_vba_code)
        return input_vba_content
    return False
Beispiel #8
0
 def manage_encrypted_doc(self):
     self.olevba_results["is_encrypted"] = False
     # checks if it is an OLE file. That could be encrypted
     if self.vbaparser.ole_file:
         # check if the ole file is encrypted
         is_encrypted = self.vbaparser.detect_is_encrypted()
         self.olevba_results["is_encrypted"] = is_encrypted
         # in the case the file is encrypted I try to decrypt it
         # with the default password and the most common ones
         if is_encrypted:
             # by default oletools contains some basic passwords
             # we just add some more guesses
             common_pwd_to_check = []
             for num in range(10):
                 common_pwd_to_check.append(f"{num}{num}{num}{num}")
             # https://twitter.com/JohnLaTwC/status/1265377724522131457
             filename_without_spaces_and_numbers = sub("[-_\d\s]", "", self.filename)
             filename_without_extension = sub(
                 "(\..+)", "", filename_without_spaces_and_numbers
             )
             common_pwd_to_check.append(filename_without_extension)
             self.passwords_to_check.extend(common_pwd_to_check)
             decrypted_file_name = self.vbaparser.decrypt_file(
                 self.passwords_to_check
             )
             self.olevba_results[
                 "additional_passwords_tried"
             ] = self.passwords_to_check
             if decrypted_file_name:
                 self.vbaparser = VBA_Parser(decrypted_file_name)
             else:
                 self.olevba_results["cannot_decrypt"] = True
                 raise CannotDecryptException(
                     "cannot decrypt the file with the default password"
                 )
Beispiel #9
0
def compile_input_file(input_file: str) -> Program:
    path = Path(input_file)

    if path.is_dir():
        # A directory must be a project containing VBA source files
        return Compiler.compile_project(input_file)

    with open(input_file, "rb") as f:
        content = f.read()

    # Try to deserialize an already compiled program
    try:
        serializer = Serializer()
        obj = serializer.deserialize(content)

        if type(obj) == Program:
            return obj
    except SerializationError:
        pass

    # Compile an Office document or VBA source files
    units = []
    if magic_from_buffer(content, mime=True) == "text/plain":
        if not input_file.endswith(".vbs"):
            input_file += ".vbs"
        units.append(Unit.from_content(content.decode("utf-8"), input_file))
    else:
        vba_parser = VBA_Parser(input_file, data=content)

        for _, _, vba_filename, vba_code in vba_parser.extract_all_macros():
            units.append(Unit.from_content(vba_code, vba_filename))

    return Compiler.compile_units(units)
Beispiel #10
0
def parseOLEDocument(f):

	"""Parse an OLE document for VBA macros"""

	if not f or not useOLETools:
		return

	writeLog('DEBUG: Analyzing with oletools')
	try:
		v = VBA_Parser(f)
	except:
		writeLog("Not a supported file format: %s" % f)
		return
	writeLog('DEBUG: Detected file type: %s' % v.type)

	# Hack: Search for a .js extension
	fname, fextension = os.path.splitext(f)

	if v.detect_vba_macros() or fextension == ".js":
		writeLog('DEBUG: VBA Macros/JScript found')
		try:
			t = open("%s.analysis" % f, 'w')
		except IOError as e:
			writeLog("Cannot create analysis file %s.analysis: %s" % (f,e.strerror))
			return
		for kw_type, keyword, description in v.analyze_macros():
			t.write("%-12s | %-25s | %s\n" % (kw_type, keyword, description))
		t.close()
		writeLog("DEBUG: Analysis dumped to %s.analysis" % f)
	else:
		writeLog('DEBUG: No VBA Macros found')
	return
Beispiel #11
0
 def parse_vba(self, save_path):
     save = False
     vbaparser = VBA_Parser(__sessions__.current.file.path)
     # Check for Macros
     if not vbaparser.detect_vba_macros():
         self.log('error', "No Macro's Detected")
         return
     self.log('info', "Macro's Detected")
     #try:
     if True:
         an_results = {'AutoExec':[], 'Suspicious':[], 'IOC':[], 'Hex String':[], 'Base64 String':[], 'Dridex String':[], 'VBA string':[]}
         for (filename, stream_path, vba_filename, vba_code) in vbaparser.extract_macros():
             self.log('info', "Stream Details")
             self.log('item', "OLE Stream: {0}".format(string_clean(stream_path)))
             self.log('item', "VBA Filename: {0}".format(string_clean(vba_filename)))
             # Analyse the VBA Code
             vba_scanner = VBA_Scanner(vba_code)
             analysis = vba_scanner.scan(include_decoded_strings=True)
             for kw_type, keyword, description in analysis:
                 an_results[kw_type].append([string_clean_hex(keyword), description])
                 
             # Save the code to external File
             if save_path:
                 try:
                     with open(save_path, 'a') as out:
                         out.write(vba_code)
                     save = True
                 except:
                     self.log('error', "Unable to write to {0}".format(save_path))
                     return
         # Print all Tables together
         self.log('info', "AutoRun Macros Found")
         self.log('table', dict(header=['Method', 'Description'], rows=an_results['AutoExec']))
         
         self.log('info', "Suspicious Keywords Found")
         self.log('table', dict(header=['KeyWord', 'Description'], rows=an_results['Suspicious']))
         
         self.log('info', "Possible IOC's")
         self.log('table', dict(header=['IOC', 'Type'], rows=an_results['IOC']))
         
         self.log('info', "Hex Strings")
         self.log('table', dict(header=['Decoded', 'Raw'], rows=an_results['Hex String']))
         
         self.log('info', "Base64 Strings")
         self.log('table', dict(header=['Decoded', 'Raw'], rows=an_results['Base64 String']))
         
         self.log('info', "Dridex String")
         self.log('table', dict(header=['Decoded', 'Raw'], rows=an_results['Dridex String']))
         
         self.log('info', "VBA string")
         self.log('table', dict(header=['Decoded', 'Raw'], rows=an_results['VBA string']))
         
         
         
         if save:
             self.log('success', "Writing VBA Code to {0}".format(save_path))
     #except:
         #self.log('error', "Unable to Process File")
     # Close the file
     vbaparser.close()
Beispiel #12
0
def process_file_scanexpr (container, filename, data):
    """
    Process a single file

    :param container: str, path and filename of container if the file is within
    a zip archive, None otherwise.
    :param filename: str, path and filename of file on disk, or within the container.
    :param data: bytes, content of the file if it is in a container, None if it is a file on disk.
    """
    #TODO: replace print by writing to a provided output file (sys.stdout by default)
    if container:
        display_filename = '%s in %s' % (filename, container)
    else:
        display_filename = filename
    print '='*79
    print 'FILE:', display_filename
    all_code = ''
    try:
        #TODO: handle olefile errors, when an OLE file is malformed
        vba = VBA_Parser(filename, data)
        print 'Type:', vba.type
        if vba.detect_vba_macros():
            #print 'Contains VBA Macros:'
            for (subfilename, stream_path, vba_filename, vba_code) in vba.extract_macros():
                # hide attribute lines:
                #TODO: option to disable attribute filtering
                vba_code_filtered = filter_vba(vba_code)
                print '-'*79
                print 'VBA MACRO %s ' % vba_filename
                print 'in file: %s - OLE stream: %s' % (subfilename, repr(stream_path))
                print '- '*39
                # detect empty macros:
                if vba_code_filtered.strip() == '':
                    print '(empty macro)'
                else:
                    # TODO: option to display code
                    print vba_code_filtered
                    vba_code = vba_collapse_long_lines(vba_code)
                    all_code += '\n' + vba_code
            print '-'*79
            print 'EVALUATED VBA EXPRESSIONS:'
            t = prettytable.PrettyTable(('Obfuscated expression', 'Evaluated value'))
            t.align = 'l'
            t.max_width['Obfuscated expression'] = 36
            t.max_width['Evaluated value'] = 36
            for expression, expr_eval in scan_expressions(all_code):
                t.add_row((repr(expression), repr(expr_eval)))
            print t


        else:
            print 'No VBA macros found.'
    except: #TypeError:
        #raise
        #TODO: print more info if debug mode
        #print sys.exc_value
        # display the exception with full stack trace for debugging, but do not stop:
        traceback.print_exc()
    print ''
Beispiel #13
0
 def is_file_has_VBA_macros(self):
     """Check if file has VBA macros."""
     file_path = self.file_path
     vbaparser = VBA_Parser(file_path)
     print('The file type is "%s"' % (vbaparser.type))
     report = vbaparser.detect_vba_macros()
     vbaparser.close()
     return report
Beispiel #14
0
 def test_samples(self):
     if sys.version_info[0] > 2:
          # Unfortunately, olevba3 doesn't have extract_form_strings_extended
          return
     for sample, expected_result in SAMPLES:
         full_name = join(DATA_BASE_DIR, 'oleform', sample)
         parser = VBA_Parser(full_name)
         variables = list(parser.extract_form_strings_extended())
         self.assertEqual(variables, expected_result)
Beispiel #15
0
    def getVBA(self, myfile, source='filepath'):
        '''
        Given a file, parses out the stream paths, vba code, and vba filenames for each.
        :param myfile: filename
        :param source: type of data being passed in.  Either "filepath" to indicate we need to read from disk or
        "filecontents" meaning that the file contents are being passed as a parameter.
        :return: pandas Series that can be used in concert with the pandas DataFrame apply method
        '''
        if source == 'filepath':
            filedata = open(myfile, 'rb').read()
        else:
            filedata = myfile

        entry = {}
        try:
            vbaparser = VBA_Parser('mmbot', data=filedata)
            allcode = ''
            pathnames = ''
            filenames = ''
            if vbaparser.detect_vba_macros():
                filenameslist = []
                pathnameslist = []
                vbacodelist = []
                for (filename, stream_path, filename_vba,
                     extracted_vba) in vbaparser.extract_macros():
                    vbacodelist.append(extracted_vba.decode("ascii", "ignore"))
                    #vbacodelist.append(extracted_vba.decode('utf8', 'ignore'))

                    if pathnames is None:
                        pathnameslist.append(
                            stream_path.decode("ascii", "ignore"))
                        filenameslist.append(
                            filename_vba.decode("ascii", "ignore"))
                    else:
                        pathnameslist.append(
                            stream_path.decode("ascii", "ignore"))
                        filenameslist.append(
                            filename_vba.decode("ascii", "ignore"))
                allcode = "\n\n\n\n".join(vbacodelist)
                filenames = ", ".join(filenameslist)
                pathnames = ", ".join(pathnameslist)

            else:
                pathnames = 'No VBA Macros found'
                filenames = 'No VBA Macros found'
                allcode = 'No VBA Macros found'

        except Exception as e:
            pathnames = 'Error:' + str(e)
            filenames = 'Error:' + str(e)
            allcode = 'Error:' + str(e)

        return pd.Series({
            'extracted_vba': allcode,
            'stream_path': pathnames,
            'filename_vba': filenames
        })
Beispiel #16
0
 def test_samples(self):
     if sys.version_info[0] > 2:
          # Unfortunately, olevba3 doesn't have extract_form_strings_extended
          return
     for sample, expected_result in SAMPLES:
         full_name = join(DATA_BASE_DIR, 'oleform', sample)
         parser = VBA_Parser(full_name)
         variables = list(parser.extract_form_strings_extended())
         self.assertEqual(variables, expected_result)
Beispiel #17
0
    def get_report(self):
        """ Return oletools report or create if not already cached. """
        if self.sample.oletools_report is not None:
            return self.sample.oletools_report

        report = {
            'autoexec': [],
            'suspicious': [],
        }

        file_path = self.sample.file_path
        try:
            vbaparser = VBA_Parser(file_path)

            # VBA_Parser reports macros for office documents
            report['has_macros'] = vbaparser.detect_vba_macros(
            ) or vbaparser.detect_xlm_macros()
            try:
                report['vba'] = vbaparser.reveal()
            except TypeError:
                # office document with no macros
                pass

            all_macros = vbaparser.extract_all_macros()
            if (report['has_macros'] and len(all_macros) == 1
                    and isinstance(all_macros[0], tuple)
                    and len(all_macros[0]) >= 3
                    and all_macros[0][2] == file_path):
                logger.warning(
                    "Buggy oletools version detected, result overridden. May "
                    "lead to false negatives, please update to fixed version")
                report['has_macros'] = False

            if vbaparser.detect_vba_macros():
                vb_code = vbaparser.extract_all_macros()
                for (_, _, _, c) in vb_code:
                    autoexec = detect_autoexec(c)
                    if len(autoexec) >= 1:
                        report['autoexec'].append(autoexec[0])

                    suspicious = detect_suspicious(c)
                    if len(suspicious) >= 1:
                        report['suspicious'].append(suspicious[0])

            vbaparser.close()
        except IOError:
            raise
        except (TypeError, FileOpenError):
            # The given file is not an office document.
            pass
        except Exception as error:
            logger.exception(error)

        report = OletoolsReport(report)
        self.sample.register_oletools_report(report)
        return report
Beispiel #18
0
 def parse_vba(self, save_path):
     save = False
     vba = VBA_Parser(__sessions__.current.file.path)
     # Check for Macros
     if not vba.detect_vba_macros():
         self.log('error', "No Macro's Detected")
         return
     self.log('info', "Macro's Detected")
     try:
         run_rows = []
         word_rows = []
         pattern_rows = []
         for (filename, stream_path, vba_filename, vba_code) in vba.extract_macros():
             self.log('info', "Stream Details")
             self.log('item', "OLE Stream: {0}".format(string_clean(stream_path)))
             self.log('item', "VBA Filename: {0}".format(string_clean(vba_filename)))
             autoexec_keywords = detect_autoexec(vba_code)
             if autoexec_keywords:
                 for keyword, description in autoexec_keywords:
                     run_rows.append([keyword, description])
                 
             # Match Keyword Types
             suspicious_keywords = detect_suspicious(vba_code)
             if suspicious_keywords:
                 for keyword, description in suspicious_keywords:
                     word_rows.append([keyword, description])
                 
             # Match IOCs
             patterns = detect_patterns(vba_code)
             if patterns:
                 for pattern_type, value in patterns:
                     pattern_rows.append([pattern_type, value])
                 
             # Save the code to external File
             if save_path:
                 try:
                     with open(save_path, 'a') as out:
                         out.write(vba_code)
                     save = True
                 except:
                     self.log('Error', "Unable to write to {0}".format(save_path))
                     return
         # Print all Tables together
         self.log('info', "AutoRun Macros Found")
         self.log('table', dict(header=['KeyWord', 'Description'], rows=run_rows))
         self.log('info', "Suspicious Keywords Found")
         self.log('table', dict(header=['KeyWord', 'Description'], rows=word_rows))
         self.log('info', "Suspicious Patterns Found")
         self.log('table', dict(header=['Pattern', 'Value'], rows=pattern_rows))
         if save:
             self.log('success', "Writing VBA Code to {0}".format(save_path))
     except:
         self.log('Error', "Unable to Process File")
     # Close the file
     vba.close()
Beispiel #19
0
def vbaparsing(filename):
    vbafile = VBA_Parser(filename)
    results = ""
    for (filename, stream_path, vba_filename,
         vba_code) in vbafile.extract_macros():
        results = results + vba_code
    result = vbafile.analyze_macros()
    for kw_type, keyword, description in result:
        results = results + 'type=%s - keyword=%s - description=%s' % (
            kw_type, keyword, description)
    return results
def extract_src_codes(file):
    dic = {}
    parser = VBA_Parser(file)
    for (_, sname, _, code) in parser.extract_macros():
        sname = sname.split("/")[-1]
        if sname not in dic:
            lines = code.split("\n")[1: ]
            dic[sname] = "\n".join(lines) # remove the first line
        else:
            printError(f"Error in src_codes: repeat {sname}")
    return dic
Beispiel #21
0
def vba2graph_from_vba_object(filepath):
    logging.info("Extracting macros from file")
    full_vba_code = ""
    vba_parser = VBA_Parser(filepath)
    for (subfilename, stream_path, vba_filename, vba_code) in vba_parser.extract_macros():
        # workaround till oletools version 0.56
        if isinstance(vba_code, bytes):
            vba_code = vba_code.decode('latin1', errors='replace')
        full_vba_code += vba_code
    vba_parser.close()
    return full_vba_code
Beispiel #22
0
def parse(file_path):
    vba_folder = 'src'
    vba_parser = VBA_Parser(file_path)
    for _, _, vba_filename, vba_code in vba_parser.extract_macros():
        if not os.path.isdir(vba_folder):
            os.makedirs(vba_folder)
        with open(os.path.join(vba_folder, vba_filename),
                  'w',
                  encoding='utf-8',
                  newline='\n') as file:
            file.write(vba_code)
    vba_parser.close()
Beispiel #23
0
def POE(logdir, targetfile, logging, debug):

    if (logging == True): 
        LOG = logger()             
    newlogentry = ''
    macro_dump_data = ''

    FI = fileio()

    try:        
        filedata = open(targetfile.filename, 'rb').read()
        vbaparser = VBA_Parser(targetfile.filename, data=filedata)
        if vbaparser.detect_vba_macros():
            print '[*] VBA macros found - Extracting...\n'
            if (logging == True):
                newlogentry = 'VBA macros found - Extracting...'
                LOG.WriteLog(logdir, targetfile.filename, newlogentry)
            for (filename, stream_path, vba_filename, vba_code) in vbaparser.extract_macros():
                macro_dump_data += '-' * 79 + '\n'
                try:
                    macro_dump_data += 'Filename    :' + filename.encode("ascii", "replace") + '\n'
                    macro_dump_data += 'OLE stream  :' + stream_path.encode("ascii", "replace") + '\n'                
                    macro_dump_data += 'VBA filename:' + vba_filename.encode("ascii", "replace") + '\n'
                except Exception, e:
                    print '[x] Current macro - unable to print Filename, OLE stream or VBA filename due to encoding issue (Unicode?): ', e
                macro_dump_data += '-' * 79 + '\n'
                macro_dump_data += vba_code
                FI.WriteLogFile(logdir + vba_filename, macro_dump_data)

                try:                     
                    print '[*] Macro ' + vba_filename.encode("ascii", "replace") + ' extracted to: ' + logdir + vba_filename.encode("ascii", "replace")
                    targetfile.macros.append(logdir + vba_filename.encode("ascii", "replace"))

                    if (logging == True):
                        newlogentry = 'Macro ' + vba_filename.encode("ascii", "replace") + ' extracted to: <a href=\"' + logdir + vba_filename.encode("ascii", "replace") + '\">' + vba_filename.encode("ascii", "replace") + '</a>'
                    LOG.WriteLog(logdir, targetfile.filename, newlogentry)
                    if (debug == True):
                        print '-'*79
                        print 'Filename    :', filename.encode("ascii", "replace")
                        print 'OLE stream  :', stream_path.encode("ascii", "replace")
                        print 'VBA filename:', vba_filename.encode("utf-8", "ignore")
                        print '-'*79
                except Exception, e:
                    print '[x] Current macro - unable print Filename, OLE stream or VBA filename due to encoding issue: (Unicode?)', e

                if (debug == True):
                    print vba_code

                macro_dump_data = ''
            print 'Macro List'
            for mlist in targetfile.macros:
                print mlist
Beispiel #24
0
def writeVBA(workbookName):
    vbaparser = VBA_Parser('./'+workbookName)

    VBA_path = './src/VBA'
    if os.path.exists(VBA_path):
        shutil.rmtree(VBA_path)
    os.mkdir(VBA_path)

    for (filename, stream_path, vba_filename, vba_code) in vbaparser.extract_macros():
        f = open('./src/VBA/' + vba_filename, "w")
        f.write(vba_code)
        f.close()

    vbaparser.close()
Beispiel #25
0
 def get_macro(self):
     """
     Get Macros from an Office file and write them to a text file
     """
     try:
         self.mk_tmp_dir()
         print "Getting Macros from {}".format(self.file)
         vb = VBA_Parser(self.file, relaxed=True)
         if vb.detect_vba_macros():
             with open("{}{}macros.txt".format(self.tmp_dir, os.sep), "w") as macro_file:
                 for (subfilename, stream_path, vba_filename, vba_code) in vb.extract_all_macros():
                     macro_file.write(vba_code)
     except Exception as e:
         print "get_macro Exception: {}".format(e)
Beispiel #26
0
def check_macros(doc_path, filename) -> dict:
    try:

        vbaparser = VBA_Parser(doc_path)

        if not vbaparser.detect_vba_macros():
            return ""

        logging.warning("VBA macros in \"%s\"" % filename)

        details = "Macro Results\n"
        suspicious_count = 0
        suspicious_list = []
        ioc_list = []
        ioc_count = 0
        mal_score = 0

        logging.disable(level=logging.CRITICAL)
        results = vbaparser.analyze_macros()
        logging.disable(level=logging.NOTSET)

        for kw_type, keyword, description in results:

            details += "\t - " + kw_type + " - (" + keyword + ") - " + description + "\n"

            if kw_type.lower() == "suspicious":
                suspicious_count += 1
                suspicious_list.append(keyword)

            elif kw_type.lower() == "ioc":
                ioc_count += 1
                ioc_list.append(keyword)

        if suspicious_count != 0:
            logging.warning("Found %d suspicious items in \"%s\"" %
                            (suspicious_count, filename))
            mal_score += suspicious_count

        if ioc_count != 0:
            logging.warning("Found %d IOCs: %s, in \"%s\"" %
                            (ioc_count, ioc_list, filename))
            mal_score += ioc_count * 2

        return {'details': details, 'mal_score': mal_score}

    except Exception as e:
        logging.error("Failed to parse macros for: {} - {}".format(
            filename, e))
        return
def get_vba_source_from_excel(filename):
    vbaparser = VBA_Parser(filename)

    if vbaparser.detect_vba_macros():
        print('VBA Macros found')
    else:
        print('No VBA Macros found')

    for (filename, stream_path, vba_filename, vba_code) in vbaparser.extract_macros():
        print('-' * 79)
        print('Filename    :', filename)
        print('OLE stream  :', stream_path)
        print('VBA filename:', vba_filename)
        print('- ' * 39)
        print(vba_code)
def get_all_macros(filename_wchar):
    """
    Open a file and extract the source code of all VBA modules
    by calling oletools.VBA_Parser.get_vba_code_all_modules()
    :param filename_wchar: file to be opened (unicode wchar_t string)
    :return: source code of all VBA modules (unicode wchar_t string)
    """
    # print(wchar_string)
    filename_str = ffi.string(filename_wchar)
    print("filename: {}".format(filename_str))
    ovba = VBA_Parser(filename_str)
    # TODO: check if there are macros
    vba_code_str = ovba.get_vba_code_all_modules()
    # convert python str to C string
    return ffi.new("wchar_t[]", vba_code_str)
Beispiel #29
0
    def process(self, sample, tag):
        try:
            hashbot      = Hasher(sample)
            mymagic      = magic.Magic(mime=True)
            sample_info  = self.identify_sample(oletools.oleid.OleID(sample))
            vba          = VBA_Parser(sample)
    
            # Extend the results of sample_info with extra data and add to Splunk
            sample_info['submit_date']   = int(time())
            sample_info['filetype']      = mymagic.from_file(sample)
            sample_info['sample_type']   = 'office'
            sample_info['id_tag']        = tag
            sample_info['sha1']          = hashbot.get_sha1()
            sample_info['md5']           = hashbot.get_md5()
            indicators                   = dict()

            if self.doc_has_macros(vba):
                macro = self.macro_extraction(vba)
                data  = VBA_Scanner(macro['vba_code']).scan(include_decoded_strings=False)
    
                i = 1
                for kw_type, keyword, description in data:
                    indicators[str(i)] = {'type':kw_type, 
                                          'keyword':keyword, 
                                          'description':description
                                         } 
                    i+=1

            sample_info['indicators'] = indicators
            self.share_data(sample_info)
            self.store_sample(sample)
        except Exception, e:
            print "Error on",sample,e
	def doc_parsing(self, filename, filecontent):
		'''
			Function to parse the given data in mail content
		'''
		mil_attach = '' # reset var
		# send data to vba parser
		vbaparser = VBA_Parser(filename, data=filecontent)
		# if a macro is detected
		if vbaparser.detect_vba_macros():
			results = vbaparser.analyze_macros()
			nr = 1
			self.log("VBA Macros found")
			# generate report for log file
			for kw_type, keyword, description in results:
				if kw_type == 'Suspicious':
					mil_attach += 'Macro Number %i:\n Type: %s\n Keyword: %s\n Description: %s\n' % (nr, kw_type, keyword, description)
				nr += 1
			mil_attach += '\nSummery:\nAutoExec keywords: %d\n' % vbaparser.nb_autoexec
			mil_attach += 'Suspicious keywords: %d\n' % vbaparser.nb_suspicious
			mil_attach += 'IOCs: %d\n' % vbaparser.nb_iocs
			mil_attach += 'Hex obfuscated strings: %d\n' % vbaparser.nb_hexstrings
			mil_attach += 'Base64 obfuscated strings: %d\n' % vbaparser.nb_base64strings
			mil_attach += 'Dridex obfuscated strings: %d\n' % vbaparser.nb_dridexstrings
			mil_attach += 'VBA obfuscated strings: %d' % vbaparser.nb_vbastrings
			
			r_level = vbaparser.nb_autoexec + vbaparser.nb_suspicious + vbaparser.nb_iocs + vbaparser.nb_hexstrings + vbaparser.nb_base64strings + vbaparser.nb_dridexstrings + vbaparser.nb_vbastrings
			
			# set reject level to global
			self.level = r_level
			vbaparser.close()
			return mil_attach # return the log to caller
		else:
			self.log("VBA no Macros found in file")
			vbaparser.close()
			return None # nothing found
Beispiel #31
0
    def get_report(self, sample):
        """ Return oletools report or create if not already cached. """
        if sample.oletools_report != None:
            return sample.oletools_report

        report = {}

        try:
            vbaparser = VBA_Parser(sample.file_path)

            # VBA_Parser reports macros for office documents
            report['has_macros'] = vbaparser.detect_vba_macros(
            ) or vbaparser.detect_xlm_macros()
            try:
                report['vba'] = vbaparser.reveal()
            except TypeError:
                # no macros
                pass
            vbaparser.close()
        except IOError:
            raise
        except (TypeError, FileOpenError):
            # The given file is not an office document.
            pass
        except Exception as error:
            logger.exception(error)
        sample.register_oletools_report(OletoolsReport(report))
        return report
	def _run(self, scanObject, result, depth, args):
		moduleResult = []
		vbap_buffer = VBA_Parser(scanObject.buffer)

		try:
			if vbap_buffer.detect_vba_macros():
				vbap_result = vbap_buffer.analyze_macros()
				for kw_type, keyword, description in vbap_result:
					kw = '%s - %s' % ( keyword,description )
					scanObject.addMetadata(self.module_name,kw_type,kw)
		except (QuitScanException, GlobalScanTimeoutError, GlobalModuleTimeoutError):
			raise
		except:
			logging.debug("Failed to parse OLEVBA")

		vbap_buffer.close()

		return moduleResult
Beispiel #33
0
 def run(self, obj, config):
     username = self.current_task.username
     filename = obj.filename
     filedata = obj.filedata.read()
     try:
         vbaparser = VBA_Parser(filename, data=filedata)
     except Exception, e:
         self._error("Cannot parse file: %s" % str(e))
         return
Beispiel #34
0
    def manage_xlm_macros(self):
        self.olevba_results["xlm_macro"] = False
        # check if the file contains an XLM macro
        # and try an experimental parsing
        # credits to https://twitter.com/gabriele_pippi for the idea
        if self.vbaparser.detect_xlm_macros():
            self.olevba_results["xlm_macro"] = True
            logger.debug("experimental XLM macro analysis start")
            parsed_file = b""
            try:
                excel_doc = XLSWrapper2(self.filepath)
                ae_list = [
                    "auto_open",
                    "auto_close",
                    "auto_activate",
                    "auto_deactivate",
                ]
                self.olevba_results["xlm_macro_autoexec"] = []
                for ae in ae_list:
                    auto_exec_labels = excel_doc.get_defined_name(
                        ae, full_match=False)
                    for label in auto_exec_labels:
                        self.olevba_results["xlm_macro_autoexec"].append(
                            label[0])

                for i in show_cells(excel_doc):
                    rec_str = ""
                    if len(i) == 5:
                        # rec_str = 'CELL:{:10}, {:20}, {}'
                        # .format(i[0].get_local_address(), i[2], i[4])
                        if i[2] != "None":
                            rec_str = "{:20}".format(i[2])
                    if rec_str:
                        parsed_file += rec_str.encode()
                        parsed_file += b"\n"
            except Exception as e:
                logger.info(
                    f"experimental XLM macro analysis failed. Exception: {e}")
            else:
                logger.debug(f"experimental XLM macro analysis succeded. "
                             f"Binary to analyze: {parsed_file}")
                if parsed_file:
                    self.vbaparser = VBA_Parser(self.filename,
                                                data=parsed_file)
Beispiel #35
0
def vba2graph_from_vba_object(filepath):
    """ vba2graph as library
    Args:
        filepath (string): path to file
    """
    if HAVE_OLETOOLS:
        try:
            vba = VBA_Parser(filepath)
        except Exception as e:
            return False
    full_vba_code = ""
    for (subfilename, stream_path, vba_filename, vba_code) in vba.extract_macros():
        full_vba_code += 'VBA MACRO %s \n' % vba_filename
        full_vba_code += '- '*39 + '\n'
        full_vba_code += vba_code
    vba.close()
    if full_vba_code:
        input_vba_content = handle_olevba_input(full_vba_code)
        return input_vba_content

    return False
Beispiel #36
0
def MacroHunter(targetFile):
    answerTable = PrettyTable()
    answerTable.field_names = [f"{green}Threat Levels{white}", f"{green}Macros{white}", f"{green}Descriptions{white}"]

    print(f"\n{infoS} Looking for VBA Macros...")
    fileData = open(targetFile, "rb").read()
    vbaparser = VBA_Parser(targetFile, data=fileData)
    if vbaparser.contains_macros == True:
        macroList = list(vbaparser.analyze_macros())
        for fi in range(0, len(macroList)):
            if macroList[fi][0] == 'Suspicious':
                answerTable.add_row([f"{yellow}{macroList[fi][0]}{white}", f"{macroList[fi][1]}", f"{macroList[fi][2]}"])
            elif macroList[fi][0] == 'IOC':
                answerTable.add_row([f"{magenta}{macroList[fi][0]}{white}", f"{macroList[fi][1]}", f"{macroList[fi][2]}"])
            elif macroList[fi][0] == 'AutoExec':
                answerTable.add_row([f"{red}{macroList[fi][0]}{white}", f"{macroList[fi][1]}", f"{macroList[fi][2]}"])
            else:
                answerTable.add_row([f"{macroList[fi][0]}", f"{macroList[fi][1]}", f"{macroList[fi][2]}"])
        print(f"{answerTable}\n")
    else:
        print(f"{errorS} Not any macros found.")
Beispiel #37
0
def has_office_macros(office_file):
    """
    Detects macros in Microsoft Office documents.

    :param office_file: The MS Office document to check for macros.
    :return: True if macros where found, otherwise False.
             If VBA_Parser crashes it returns False too.
    """
    file_extension = office_file.split('.')[-1]
    if file_extension not in ms_office_extensions:
        return False
    try:
        # VBA_Parser reports macros for office documents
        vbaparser = VBA_Parser(office_file)
        return vbaparser.detect_vba_macros()
    except TypeError:
        # The given file is not an office document.
        return False
    except Exception as e:
        logger.exception(e)
        return False
Beispiel #38
0
 def parse_vba(self, save_path):
     save = False
     vbaparser = VBA_Parser(__sessions__.current.file.path)
     # Check for Macros
     if not vbaparser.detect_vba_macros():
         self.log('error', "No Macro's Detected")
         return
     self.log('info', "Macro's Detected")
     #try:
     if True:
         an_results = {'AutoExec':[], 'Suspicious':[], 'IOC':[], 'Hex String':[], 'Base64 String':[], 'Dridex String':[], 'VBA string':[]}
         for (filename, stream_path, vba_filename, vba_code) in vbaparser.extract_macros():
             self.log('info', "Stream Details")
             self.log('item', "OLE Stream: {0}".format(string_clean(stream_path)))
             self.log('item', "VBA Filename: {0}".format(string_clean(vba_filename)))
             # Analyse the VBA Code
             vba_scanner = VBA_Scanner(vba_code)
             analysis = vba_scanner.scan(include_decoded_strings=True)
             for kw_type, keyword, description in analysis:
                 an_results[kw_type].append([string_clean_hex(keyword), description])
                 
             # Save the code to external File
             if save_path:
                 try:
                     with open(save_path, 'a') as out:
                         out.write(vba_code)
                     save = True
                 except:
                     self.log('error', "Unable to write to {0}".format(save_path))
                     return
         # Print all Tables together
         self.log('info', "AutoRun Macros Found")
         self.log('table', dict(header=['Method', 'Description'], rows=an_results['AutoExec']))
         
         self.log('info', "Suspicious Keywords Found")
         self.log('table', dict(header=['KeyWord', 'Description'], rows=an_results['Suspicious']))
         
         self.log('info', "Possible IOC's")
         self.log('table', dict(header=['IOC', 'Type'], rows=an_results['IOC']))
         
         self.log('info', "Hex Strings")
         self.log('table', dict(header=['Decoded', 'Raw'], rows=an_results['Hex String']))
         
         self.log('info', "Base64 Strings")
         self.log('table', dict(header=['Decoded', 'Raw'], rows=an_results['Base64 String']))
         
         self.log('info', "Dridex String")
         self.log('table', dict(header=['Decoded', 'Raw'], rows=an_results['Dridex String']))
         
         self.log('info', "VBA string")
         self.log('table', dict(header=['Decoded', 'Raw'], rows=an_results['VBA string']))
         
         
         
         if save:
             self.log('success', "Writing VBA Code to {0}".format(save_path))
     #except:
         #self.log('error', "Unable to Process File")
     # Close the file
     vbaparser.close()
Beispiel #39
0
def _ole_analysis(full_targ_path):
    # This function calls a number of tools / scripts to run against document samples containing OLE data and extracts data and/or performs analysis as needed.
    try:
        vba_parse_Obj = VBA_Parser(full_targ_path)
    except AttributeError:
        return("ERROR_PARSING", "ERROR_PARSING", "ERROR_PARSING", "ERROR_PARSING")
    macro_analysis_over = []
    macro_analysis_info = []
    if vba_parse_Obj.detect_vba_macros():
        vba_macro = "Present"
        # Utilizing oletools to perform analysis.

        # Grabbing info from each macro.
        MA_CNT = 1
        for (file_name, ole_stream, vba_filename, vba_code) in vba_parse_Obj.extract_macros():
            macro_analysis_over.append(str(MA_CNT)+':'+str(full_targ_path))
            macro_analysis_over.append(str(MA_CNT)+":Filename      :"+file_name)
            macro_analysis_over.append(str(MA_CNT)+":OLE Stream    :"+ole_stream)
            macro_analysis_over.append(str(MA_CNT)+":VBA Filename  :"+vba_filename)
            macro_analysis_over.append(str(MA_CNT)+':'+vba_code)
            MA_CNT+=1

        # Grabbing some overall VBA analysis info.
        macro_flag_types = []
        macro_analysis_res = vba_parse_Obj.analyze_macros()
        if isinstance(macro_analysis_res, list):
            for iocType in macro_analysis_res:
                if str(iocType) not in macro_flag_types:
                    macro_flag_types.append(str(iocType[0]))


        if len(macro_flag_types) > 0:
            iocs = ':'.join(list(set(macro_flag_types)))
        else:
            iocs = "None"

    else:
        vba_macro = "None"
        iocs = "None"
        macro_analysis_res = "None"

    vba_parse_Obj.close()

    return(vba_macro, macro_analysis_over, str(macro_analysis_res), iocs)
Beispiel #40
0
	def inspect_vba_data(self, filename, filecontent):
		'''
			Function to parse the given data in mail content
		'''
		vbaparser_report_log = ''  # reset var
		# send data to vba parser
		vbaparser = VBA_Parser(filename, data=filecontent)
		# if a macro is detected
		if not vbaparser.detect_vba_macros():
			self.log("VBA no Macros found in file")
			vbaparser.close()
			return None  # nothing found
		else:
			results = vbaparser.analyze_macros()
			nr = 1
			self.log("VBA Macros found")
			# generate report for log file
			for kw_type, keyword, description in results:
				if kw_type == 'Suspicious':
					vbaparser_report_log += 'Macro Number %i:\n Type: %s\n Keyword: %s\n Description: %s\n' % (
					nr, kw_type, keyword, description)
				nr += 1
			vbaparser_report_log += '\nSummery:\nAutoExec keywords: %d\n' % vbaparser.nb_autoexec
			vbaparser_report_log += 'Suspicious keywords: %d\n' % vbaparser.nb_suspicious
			vbaparser_report_log += 'IOCs: %d\n' % vbaparser.nb_iocs
			vbaparser_report_log += 'Hex obfuscated strings: %d\n' % vbaparser.nb_hexstrings
			vbaparser_report_log += 'Base64 obfuscated strings: %d\n' % vbaparser.nb_base64strings
			vbaparser_report_log += 'Dridex obfuscated strings: %d\n' % vbaparser.nb_dridexstrings
			vbaparser_report_log += 'VBA obfuscated strings: %d' % vbaparser.nb_vbastrings
			
			# TBD: calculate a better level - add additional values to Base64 and Hex. No human writes such strings into code.
			r_level = vbaparser.nb_autoexec + vbaparser.nb_suspicious + vbaparser.nb_iocs + vbaparser.nb_hexstrings + vbaparser.nb_base64strings + vbaparser.nb_dridexstrings + vbaparser.nb_vbastrings

			# set reject level to global
			#self.level = r_level
			vbaparser.close()
			return [r_level, vbaparser_report_log]  # return the log to caller
	return suspicious

# Window to select a file for analysis
errmsg = 'Error!'
file_win = Tkinter.Tk()
D = Tkinter.Button(	text = 'Choose a file',
			fg = 'darkgreen',
			command = sus_call)
D.pack()
Tkinter.Button(		text = 'Start Analysis',
			fg = 'darkorange',
			command = file_win.quit).pack()
file_win.mainloop()

#vbaparser = VBA_Parser(sys.argv[1])
vbaparser = VBA_Parser(suspicious)
#sus_file = sys.argv[1] # sus_file suspicious_file
sus_file = suspicious
## Other way to do the same
sus_filedata = open(sus_file, 'rb').read()
vbaparser = VBA_Parser(sus_file, data=sus_filedata)

## Manual Macro extraction details
##for (sys.argv[1], stream_path, vba_filename, vba_code) in vbaparser.extract_macros():
##	print '-' * 79
##	print 'Filename        :', sys.argv[1]
##	print 'OLE stream      :', stream_path
##	print 'VBA filename    :', vba_filename
##	print '- ' * 39
##	print vba_code
Beispiel #42
0
def ProcessFile(path):
    if not(os.path.isfile(path)):
        print '{0} not a file!'.format(path)
        return 2

    try:
        data = {}
        data['valid'] = True  
        oledata = {}
        
        vbaparser = VBA_Parser(path)
        oledata['has_macros'] = vbaparser.detect_vba_macros()
        
        # dump macros content
        macros = []
        for (filename, stream_path, vba_filename, vba_code) in vbaparser.extract_macros():
            macro = {}
            macro['filename']   = filename
            macro['stream']     = stream_path
            macro['vba']        = vba_filename
            macro['content']    = convert_to_printable_null_terminated(vba_code)
            macros.append(macro)
        oledata['macros'] = macros
            
        # macro analysis
        macros_warnings = []
        results = vbaparser.analyze_macros()
        for kw_type, keyword, description in results:
            warning = {}
            warning['type']         = kw_type
            warning['keyword']      = keyword
            warning['description']  = description
            macros_warnings.append(warning)
        oledata['macros_warnings'] = macros_warnings
            
        # counters
        counters = {}
        counters['autoexec']           = vbaparser.nb_autoexec
        counters['suspicious']         = vbaparser.nb_suspicious
        counters['iocs']               = vbaparser.nb_iocs
        counters['hexstrings']         = vbaparser.nb_hexstrings
        counters['base64strings']      = vbaparser.nb_base64strings
        counters['dridexstrings']      = vbaparser.nb_dridexstrings
        counters['vbastrings']         = vbaparser.nb_vbastrings
        oledata['counters']            = counters    
        
        # deobfuscation    
        oledata['deobfuscated'] = convert_to_printable_null_terminated(vbaparser.reveal())
        
        # close
        vbaparser.close()
        
        data['data'] = oledata
        encoded = json.dumps(data)
        print encoded
    except Exception as ex:
        data = {}
        data['valid'] = False
        data['error'] = str(ex)
        print json.dumps(data)
        return 1
        
    return 0