Commit 3ac3fd00d6be623db64e6e3548a2feb5733fb060

Authored by Christian Herdtweck
1 parent 989984d9

added option --no-deobfuscate and argument skip_deobufscate to many functions to…

… avoid call to slow detect_vba_strings
Showing 1 changed file with 34 additions and 16 deletions
oletools/olevba.py
@@ -1687,13 +1687,14 @@ class VBA_Scanner(object): @@ -1687,13 +1687,14 @@ class VBA_Scanner(object):
1687 self.vba_strings = None 1687 self.vba_strings = None
1688 1688
1689 1689
1690 - def scan(self, include_decoded_strings=False): 1690 + def scan(self, include_decoded_strings=False, skip_deobfuscate=False):
1691 """ 1691 """
1692 Analyze the provided VBA code to detect suspicious keywords, 1692 Analyze the provided VBA code to detect suspicious keywords,
1693 auto-executable macros, IOC patterns, obfuscation patterns 1693 auto-executable macros, IOC patterns, obfuscation patterns
1694 such as hex-encoded strings. 1694 such as hex-encoded strings.
1695 1695
1696 :param include_decoded_strings: bool, if True, all encoded strings will be included with their decoded content. 1696 :param include_decoded_strings: bool, if True, all encoded strings will be included with their decoded content.
  1697 + :param skip_deobfuscate: bool, if True do not try to deobfuscate code (faster but less secure)
1697 :return: list of tuples (type, keyword, description) 1698 :return: list of tuples (type, keyword, description)
1698 (type = 'AutoExec', 'Suspicious', 'IOC', 'Hex String', 'Base64 String' or 'Dridex String') 1699 (type = 'AutoExec', 'Suspicious', 'IOC', 'Hex String', 'Base64 String' or 'Dridex String')
1699 """ 1700 """
@@ -1722,7 +1723,10 @@ class VBA_Scanner(object): @@ -1722,7 +1723,10 @@ class VBA_Scanner(object):
1722 for encoded, decoded in self.dridex_strings: 1723 for encoded, decoded in self.dridex_strings:
1723 self.code_dridex += '\n' + decoded 1724 self.code_dridex += '\n' + decoded
1724 # Detect obfuscated strings in VBA expressions 1725 # Detect obfuscated strings in VBA expressions
1725 - self.vba_strings = detect_vba_strings(self.code) 1726 + if skip_deobfuscate:
  1727 + self.vba_strings = []
  1728 + else:
  1729 + self.vba_strings = detect_vba_strings(self.code)
1726 for encoded, decoded in self.vba_strings: 1730 for encoded, decoded in self.vba_strings:
1727 self.code_vba += '\n' + decoded 1731 self.code_vba += '\n' + decoded
1728 results = [] 1732 results = []
@@ -1806,7 +1810,7 @@ class VBA_Scanner(object): @@ -1806,7 +1810,7 @@ class VBA_Scanner(object):
1806 len(self.dridex_strings), len(self.vba_strings)) 1810 len(self.dridex_strings), len(self.vba_strings))
1807 1811
1808 1812
1809 -def scan_vba(vba_code, include_decoded_strings): 1813 +def scan_vba(vba_code, include_decoded_strings, skip_deobfuscate=False):
1810 """ 1814 """
1811 Analyze the provided VBA code to detect suspicious keywords, 1815 Analyze the provided VBA code to detect suspicious keywords,
1812 auto-executable macros, IOC patterns, obfuscation patterns 1816 auto-executable macros, IOC patterns, obfuscation patterns
@@ -1815,10 +1819,11 @@ def scan_vba(vba_code, include_decoded_strings): @@ -1815,10 +1819,11 @@ def scan_vba(vba_code, include_decoded_strings):
1815 1819
1816 :param vba_code: str, VBA source code to be analyzed 1820 :param vba_code: str, VBA source code to be analyzed
1817 :param include_decoded_strings: bool, if True all encoded strings will be included with their decoded content. 1821 :param include_decoded_strings: bool, if True all encoded strings will be included with their decoded content.
  1822 + :param skip_deobfuscate: do not deobfuscate code; much faster but less secure
1818 :return: list of tuples (type, keyword, description) 1823 :return: list of tuples (type, keyword, description)
1819 (type = 'AutoExec', 'Suspicious', 'IOC', 'Hex String', 'Base64 String' or 'Dridex String') 1824 (type = 'AutoExec', 'Suspicious', 'IOC', 'Hex String', 'Base64 String' or 'Dridex String')
1820 """ 1825 """
1821 - return VBA_Scanner(vba_code).scan(include_decoded_strings) 1826 + return VBA_Scanner(vba_code).scan(include_decoded_strings, skip_deobfuscate)
1822 1827
1823 1828
1824 #=== CLASSES ================================================================= 1829 #=== CLASSES =================================================================
@@ -2294,7 +2299,7 @@ class VBA_Parser(object): @@ -2294,7 +2299,7 @@ class VBA_Parser(object):
2294 2299
2295 2300
2296 2301
2297 - def analyze_macros(self, show_decoded_strings=False): 2302 + def analyze_macros(self, show_decoded_strings=False, skip_deobfuscate=False):
2298 """ 2303 """
2299 runs extract_macros and analyze the source code of all VBA macros 2304 runs extract_macros and analyze the source code of all VBA macros
2300 found in the file. 2305 found in the file.
@@ -2313,7 +2318,7 @@ class VBA_Parser(object): @@ -2313,7 +2318,7 @@ class VBA_Parser(object):
2313 self.vba_code_all_modules += form_string + '\n' 2318 self.vba_code_all_modules += form_string + '\n'
2314 # Analyze the whole code at once: 2319 # Analyze the whole code at once:
2315 scanner = VBA_Scanner(self.vba_code_all_modules) 2320 scanner = VBA_Scanner(self.vba_code_all_modules)
2316 - self.analysis_results = scanner.scan(show_decoded_strings) 2321 + self.analysis_results = scanner.scan(show_decoded_strings, skip_deobfuscate)
2317 autoexec, suspicious, iocs, hexstrings, base64strings, dridex, vbastrings = scanner.scan_summary() 2322 autoexec, suspicious, iocs, hexstrings, base64strings, dridex, vbastrings = scanner.scan_summary()
2318 self.nb_autoexec += autoexec 2323 self.nb_autoexec += autoexec
2319 self.nb_suspicious += suspicious 2324 self.nb_suspicious += suspicious
@@ -2476,19 +2481,20 @@ class VBA_Parser_CLI(VBA_Parser): @@ -2476,19 +2481,20 @@ class VBA_Parser_CLI(VBA_Parser):
2476 pass 2481 pass
2477 2482
2478 2483
2479 - def print_analysis(self, show_decoded_strings=False): 2484 + def print_analysis(self, show_decoded_strings=False, skip_deobfuscate=False):
2480 """ 2485 """
2481 Analyze the provided VBA code, and print the results in a table 2486 Analyze the provided VBA code, and print the results in a table
2482 2487
2483 :param vba_code: str, VBA source code to be analyzed 2488 :param vba_code: str, VBA source code to be analyzed
2484 :param show_decoded_strings: bool, if True hex-encoded strings will be displayed with their decoded content. 2489 :param show_decoded_strings: bool, if True hex-encoded strings will be displayed with their decoded content.
  2490 + :param skip_deobfuscate: bool, if True do not try to deobfuscate code (faster but less secure)
2485 :return: None 2491 :return: None
2486 """ 2492 """
2487 # print a waiting message only if the output is not redirected to a file: 2493 # print a waiting message only if the output is not redirected to a file:
2488 if sys.stdout.isatty(): 2494 if sys.stdout.isatty():
2489 print 'Analysis...\r', 2495 print 'Analysis...\r',
2490 sys.stdout.flush() 2496 sys.stdout.flush()
2491 - results = self.analyze_macros(show_decoded_strings) 2497 + results = self.analyze_macros(show_decoded_strings, skip_deobfuscate)
2492 if results: 2498 if results:
2493 t = prettytable.PrettyTable(('Type', 'Keyword', 'Description')) 2499 t = prettytable.PrettyTable(('Type', 'Keyword', 'Description'))
2494 t.align = 'l' 2500 t.align = 'l'
@@ -2509,7 +2515,8 @@ class VBA_Parser_CLI(VBA_Parser): @@ -2509,7 +2515,8 @@ class VBA_Parser_CLI(VBA_Parser):
2509 2515
2510 def process_file(self, show_decoded_strings=False, 2516 def process_file(self, show_decoded_strings=False,
2511 display_code=True, global_analysis=True, hide_attributes=True, 2517 display_code=True, global_analysis=True, hide_attributes=True,
2512 - vba_code_only=False, show_deobfuscated_code=False): 2518 + vba_code_only=False, show_deobfuscated_code=False,
  2519 + skip_deobfuscate=False):
2513 """ 2520 """
2514 Process a single file 2521 Process a single file
2515 2522
@@ -2520,6 +2527,7 @@ class VBA_Parser_CLI(VBA_Parser): @@ -2520,6 +2527,7 @@ class VBA_Parser_CLI(VBA_Parser):
2520 :param global_analysis: bool, if True all modules are merged for a single analysis (default), 2527 :param global_analysis: bool, if True all modules are merged for a single analysis (default),
2521 otherwise each module is analyzed separately (old behaviour) 2528 otherwise each module is analyzed separately (old behaviour)
2522 :param hide_attributes: bool, if True the first lines starting with "Attribute VB" are hidden (default) 2529 :param hide_attributes: bool, if True the first lines starting with "Attribute VB" are hidden (default)
  2530 + :param skip_deobfuscate: bool, if True do not try to deobfuscate code (faster but less secure)
2523 """ 2531 """
2524 #TODO: replace print by writing to a provided output file (sys.stdout by default) 2532 #TODO: replace print by writing to a provided output file (sys.stdout by default)
2525 # fix conflicting parameters: 2533 # fix conflicting parameters:
@@ -2558,7 +2566,7 @@ class VBA_Parser_CLI(VBA_Parser): @@ -2558,7 +2566,7 @@ class VBA_Parser_CLI(VBA_Parser):
2558 print '- ' * 39 2566 print '- ' * 39
2559 print 'ANALYSIS:' 2567 print 'ANALYSIS:'
2560 # analyse each module's code, filtered to avoid false positives: 2568 # analyse each module's code, filtered to avoid false positives:
2561 - self.print_analysis(show_decoded_strings) 2569 + self.print_analysis(show_decoded_strings, skip_deobfuscate)
2562 for (subfilename, stream_path, form_string) in self.extract_form_strings(): 2570 for (subfilename, stream_path, form_string) in self.extract_form_strings():
2563 print '-' * 79 2571 print '-' * 79
2564 print 'VBA FORM STRING IN %r - OLE stream: %r' % (subfilename, stream_path) 2572 print 'VBA FORM STRING IN %r - OLE stream: %r' % (subfilename, stream_path)
@@ -2566,7 +2574,7 @@ class VBA_Parser_CLI(VBA_Parser): @@ -2566,7 +2574,7 @@ class VBA_Parser_CLI(VBA_Parser):
2566 print form_string 2574 print form_string
2567 if global_analysis and not vba_code_only: 2575 if global_analysis and not vba_code_only:
2568 # analyse the code from all modules at once: 2576 # analyse the code from all modules at once:
2569 - self.print_analysis(show_decoded_strings) 2577 + self.print_analysis(show_decoded_strings, skip_deobfuscate)
2570 if show_deobfuscated_code: 2578 if show_deobfuscated_code:
2571 print 'MACRO SOURCE CODE WITH DEOBFUSCATED VBA STRINGS (EXPERIMENTAL):\n\n' 2579 print 'MACRO SOURCE CODE WITH DEOBFUSCATED VBA STRINGS (EXPERIMENTAL):\n\n'
2572 print self.reveal() 2580 print self.reveal()
@@ -2584,7 +2592,7 @@ class VBA_Parser_CLI(VBA_Parser): @@ -2584,7 +2592,7 @@ class VBA_Parser_CLI(VBA_Parser):
2584 print '' 2592 print ''
2585 2593
2586 2594
2587 - def process_file_triage(self, show_decoded_strings=False): 2595 + def process_file_triage(self, show_decoded_strings=False, skip_deobfuscate=False):
2588 """ 2596 """
2589 Process a file in triage mode, showing only summary results on one line. 2597 Process a file in triage mode, showing only summary results on one line.
2590 """ 2598 """
@@ -2598,7 +2606,8 @@ class VBA_Parser_CLI(VBA_Parser): @@ -2598,7 +2606,8 @@ class VBA_Parser_CLI(VBA_Parser):
2598 if sys.stdout.isatty(): 2606 if sys.stdout.isatty():
2599 print 'Analysis...\r', 2607 print 'Analysis...\r',
2600 sys.stdout.flush() 2608 sys.stdout.flush()
2601 - self.analyze_macros(show_decoded_strings=show_decoded_strings) 2609 + self.analyze_macros(show_decoded_strings=show_decoded_strings,
  2610 + skip_deobfuscate=skip_deobfuscate)
2602 flags = TYPE2TAG[self.type] 2611 flags = TYPE2TAG[self.type]
2603 macros = autoexec = suspicious = iocs = hexstrings = base64obf = dridex = vba_obf = '-' 2612 macros = autoexec = suspicious = iocs = hexstrings = base64obf = dridex = vba_obf = '-'
2604 if self.contains_macros: macros = 'M' 2613 if self.contains_macros: macros = 'M'
@@ -2698,6 +2707,8 @@ def main(): @@ -2698,6 +2707,8 @@ def main():
2698 help='display the macro source code after replacing all the obfuscated strings by their decoded content.') 2707 help='display the macro source code after replacing all the obfuscated strings by their decoded content.')
2699 parser.add_option('-l', '--loglevel', dest="loglevel", action="store", default=DEFAULT_LOG_LEVEL, 2708 parser.add_option('-l', '--loglevel', dest="loglevel", action="store", default=DEFAULT_LOG_LEVEL,
2700 help="logging level debug/info/warning/error/critical (default=%default)") 2709 help="logging level debug/info/warning/error/critical (default=%default)")
  2710 + parser.add_option('-n', '--no-deobfuscate', dest="skip_deobfuscate", action="store_true", default=False,
  2711 + help="skip deobfuscation (much faster but less secure)")
2701 2712
2702 # Disabled options: 2713 # Disabled options:
2703 # parser.add_option("--each", action="store_false", dest="global_analysis", default=True, 2714 # parser.add_option("--each", action="store_false", dest="global_analysis", default=True,
@@ -2729,12 +2740,16 @@ def main(): @@ -2729,12 +2740,16 @@ def main():
2729 # print 'Analysis of VBA source code from %s:' % options.input 2740 # print 'Analysis of VBA source code from %s:' % options.input
2730 # vba_code = open(options.input).read() 2741 # vba_code = open(options.input).read()
2731 # print_analysis(vba_code, show_decoded_strings=options.show_decoded_strings) 2742 # print_analysis(vba_code, show_decoded_strings=options.show_decoded_strings)
  2743 + # skip_deobfuscate=options.skip_deobfuscate)
2732 # sys.exit() 2744 # sys.exit()
2733 2745
2734 # Old display with number of items detected: 2746 # Old display with number of items detected:
2735 # print '%-8s %-7s %-7s %-7s %-7s %-7s' % ('Type', 'Macros', 'AutoEx', 'Susp.', 'IOCs', 'HexStr') 2747 # print '%-8s %-7s %-7s %-7s %-7s %-7s' % ('Type', 'Macros', 'AutoEx', 'Susp.', 'IOCs', 'HexStr')
2736 # print '%-8s %-7s %-7s %-7s %-7s %-7s' % ('-'*8, '-'*7, '-'*7, '-'*7, '-'*7, '-'*7) 2748 # print '%-8s %-7s %-7s %-7s %-7s %-7s' % ('-'*8, '-'*7, '-'*7, '-'*7, '-'*7, '-'*7)
2737 2749
  2750 + if options.skip_deobfuscate and options.show_deobfuscated_code:
  2751 + logging.warning('Ignoring option --reveal since option -n / --no-deobfuscate is present!')
  2752 +
2738 # Column headers (except if detailed mode) 2753 # Column headers (except if detailed mode)
2739 if not options.detailed_mode or options.triage_mode: 2754 if not options.detailed_mode or options.triage_mode:
2740 print '%-12s %-65s' % ('Flags', 'Filename') 2755 print '%-12s %-65s' % ('Flags', 'Filename')
@@ -2756,7 +2771,8 @@ def main(): @@ -2756,7 +2771,8 @@ def main():
2756 vba_parser.process_file(show_decoded_strings=options.show_decoded_strings, 2771 vba_parser.process_file(show_decoded_strings=options.show_decoded_strings,
2757 display_code=options.display_code, global_analysis=True, #options.global_analysis, 2772 display_code=options.display_code, global_analysis=True, #options.global_analysis,
2758 hide_attributes=options.hide_attributes, vba_code_only=options.vba_code_only, 2773 hide_attributes=options.hide_attributes, vba_code_only=options.vba_code_only,
2759 - show_deobfuscated_code=options.show_deobfuscated_code) 2774 + show_deobfuscated_code=options.show_deobfuscated_code,
  2775 + skip_deobfuscate=options.skip_deobfuscate)
2760 else: 2776 else:
2761 # print container name when it changes: 2777 # print container name when it changes:
2762 if container != previous_container: 2778 if container != previous_container:
@@ -2764,7 +2780,8 @@ def main(): @@ -2764,7 +2780,8 @@ def main():
2764 print '\nFiles in %s:' % container 2780 print '\nFiles in %s:' % container
2765 previous_container = container 2781 previous_container = container
2766 # summarized output for triage: 2782 # summarized output for triage:
2767 - vba_parser.process_file_triage(show_decoded_strings=options.show_decoded_strings) 2783 + vba_parser.process_file_triage(show_decoded_strings=options.show_decoded_strings,
  2784 + skip_deobfuscate=options.skip_deobfuscate)
2768 count += 1 2785 count += 1
2769 if not options.detailed_mode or options.triage_mode: 2786 if not options.detailed_mode or options.triage_mode:
2770 print '\n(Flags: OpX=OpenXML, XML=Word2003XML, MHT=MHTML, TXT=Text, M=Macros, ' \ 2787 print '\n(Flags: OpX=OpenXML, XML=Word2003XML, MHT=MHTML, TXT=Text, M=Macros, ' \
@@ -2776,7 +2793,8 @@ def main(): @@ -2776,7 +2793,8 @@ def main():
2776 vba_parser.process_file(show_decoded_strings=options.show_decoded_strings, 2793 vba_parser.process_file(show_decoded_strings=options.show_decoded_strings,
2777 display_code=options.display_code, global_analysis=True, #options.global_analysis, 2794 display_code=options.display_code, global_analysis=True, #options.global_analysis,
2778 hide_attributes=options.hide_attributes, vba_code_only=options.vba_code_only, 2795 hide_attributes=options.hide_attributes, vba_code_only=options.vba_code_only,
2779 - show_deobfuscated_code=options.show_deobfuscated_code) 2796 + show_deobfuscated_code=options.show_deobfuscated_code,
  2797 + skip_deobfuscate=options.skip_deobfuscate)
2780 2798
2781 2799
2782 if __name__ == '__main__': 2800 if __name__ == '__main__':