Commit 3ac3fd00d6be623db64e6e3548a2feb5733fb060
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 | 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 | 1692 | Analyze the provided VBA code to detect suspicious keywords, |
| 1693 | 1693 | auto-executable macros, IOC patterns, obfuscation patterns |
| 1694 | 1694 | such as hex-encoded strings. |
| 1695 | 1695 | |
| 1696 | 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 | 1698 | :return: list of tuples (type, keyword, description) |
| 1698 | 1699 | (type = 'AutoExec', 'Suspicious', 'IOC', 'Hex String', 'Base64 String' or 'Dridex String') |
| 1699 | 1700 | """ |
| ... | ... | @@ -1722,7 +1723,10 @@ class VBA_Scanner(object): |
| 1722 | 1723 | for encoded, decoded in self.dridex_strings: |
| 1723 | 1724 | self.code_dridex += '\n' + decoded |
| 1724 | 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 | 1730 | for encoded, decoded in self.vba_strings: |
| 1727 | 1731 | self.code_vba += '\n' + decoded |
| 1728 | 1732 | results = [] |
| ... | ... | @@ -1806,7 +1810,7 @@ class VBA_Scanner(object): |
| 1806 | 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 | 1815 | Analyze the provided VBA code to detect suspicious keywords, |
| 1812 | 1816 | auto-executable macros, IOC patterns, obfuscation patterns |
| ... | ... | @@ -1815,10 +1819,11 @@ def scan_vba(vba_code, include_decoded_strings): |
| 1815 | 1819 | |
| 1816 | 1820 | :param vba_code: str, VBA source code to be analyzed |
| 1817 | 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 | 1823 | :return: list of tuples (type, keyword, description) |
| 1819 | 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 | 1829 | #=== CLASSES ================================================================= |
| ... | ... | @@ -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 | 2304 | runs extract_macros and analyze the source code of all VBA macros |
| 2300 | 2305 | found in the file. |
| ... | ... | @@ -2313,7 +2318,7 @@ class VBA_Parser(object): |
| 2313 | 2318 | self.vba_code_all_modules += form_string + '\n' |
| 2314 | 2319 | # Analyze the whole code at once: |
| 2315 | 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 | 2322 | autoexec, suspicious, iocs, hexstrings, base64strings, dridex, vbastrings = scanner.scan_summary() |
| 2318 | 2323 | self.nb_autoexec += autoexec |
| 2319 | 2324 | self.nb_suspicious += suspicious |
| ... | ... | @@ -2476,19 +2481,20 @@ class VBA_Parser_CLI(VBA_Parser): |
| 2476 | 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 | 2486 | Analyze the provided VBA code, and print the results in a table |
| 2482 | 2487 | |
| 2483 | 2488 | :param vba_code: str, VBA source code to be analyzed |
| 2484 | 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 | 2491 | :return: None |
| 2486 | 2492 | """ |
| 2487 | 2493 | # print a waiting message only if the output is not redirected to a file: |
| 2488 | 2494 | if sys.stdout.isatty(): |
| 2489 | 2495 | print 'Analysis...\r', |
| 2490 | 2496 | sys.stdout.flush() |
| 2491 | - results = self.analyze_macros(show_decoded_strings) | |
| 2497 | + results = self.analyze_macros(show_decoded_strings, skip_deobfuscate) | |
| 2492 | 2498 | if results: |
| 2493 | 2499 | t = prettytable.PrettyTable(('Type', 'Keyword', 'Description')) |
| 2494 | 2500 | t.align = 'l' |
| ... | ... | @@ -2509,7 +2515,8 @@ class VBA_Parser_CLI(VBA_Parser): |
| 2509 | 2515 | |
| 2510 | 2516 | def process_file(self, show_decoded_strings=False, |
| 2511 | 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 | 2521 | Process a single file |
| 2515 | 2522 | |
| ... | ... | @@ -2520,6 +2527,7 @@ class VBA_Parser_CLI(VBA_Parser): |
| 2520 | 2527 | :param global_analysis: bool, if True all modules are merged for a single analysis (default), |
| 2521 | 2528 | otherwise each module is analyzed separately (old behaviour) |
| 2522 | 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 | 2532 | #TODO: replace print by writing to a provided output file (sys.stdout by default) |
| 2525 | 2533 | # fix conflicting parameters: |
| ... | ... | @@ -2558,7 +2566,7 @@ class VBA_Parser_CLI(VBA_Parser): |
| 2558 | 2566 | print '- ' * 39 |
| 2559 | 2567 | print 'ANALYSIS:' |
| 2560 | 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 | 2570 | for (subfilename, stream_path, form_string) in self.extract_form_strings(): |
| 2563 | 2571 | print '-' * 79 |
| 2564 | 2572 | print 'VBA FORM STRING IN %r - OLE stream: %r' % (subfilename, stream_path) |
| ... | ... | @@ -2566,7 +2574,7 @@ class VBA_Parser_CLI(VBA_Parser): |
| 2566 | 2574 | print form_string |
| 2567 | 2575 | if global_analysis and not vba_code_only: |
| 2568 | 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 | 2578 | if show_deobfuscated_code: |
| 2571 | 2579 | print 'MACRO SOURCE CODE WITH DEOBFUSCATED VBA STRINGS (EXPERIMENTAL):\n\n' |
| 2572 | 2580 | print self.reveal() |
| ... | ... | @@ -2584,7 +2592,7 @@ class VBA_Parser_CLI(VBA_Parser): |
| 2584 | 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 | 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 | 2606 | if sys.stdout.isatty(): |
| 2599 | 2607 | print 'Analysis...\r', |
| 2600 | 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 | 2611 | flags = TYPE2TAG[self.type] |
| 2603 | 2612 | macros = autoexec = suspicious = iocs = hexstrings = base64obf = dridex = vba_obf = '-' |
| 2604 | 2613 | if self.contains_macros: macros = 'M' |
| ... | ... | @@ -2698,6 +2707,8 @@ def main(): |
| 2698 | 2707 | help='display the macro source code after replacing all the obfuscated strings by their decoded content.') |
| 2699 | 2708 | parser.add_option('-l', '--loglevel', dest="loglevel", action="store", default=DEFAULT_LOG_LEVEL, |
| 2700 | 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 | 2713 | # Disabled options: |
| 2703 | 2714 | # parser.add_option("--each", action="store_false", dest="global_analysis", default=True, |
| ... | ... | @@ -2729,12 +2740,16 @@ def main(): |
| 2729 | 2740 | # print 'Analysis of VBA source code from %s:' % options.input |
| 2730 | 2741 | # vba_code = open(options.input).read() |
| 2731 | 2742 | # print_analysis(vba_code, show_decoded_strings=options.show_decoded_strings) |
| 2743 | + # skip_deobfuscate=options.skip_deobfuscate) | |
| 2732 | 2744 | # sys.exit() |
| 2733 | 2745 | |
| 2734 | 2746 | # Old display with number of items detected: |
| 2735 | 2747 | # print '%-8s %-7s %-7s %-7s %-7s %-7s' % ('Type', 'Macros', 'AutoEx', 'Susp.', 'IOCs', 'HexStr') |
| 2736 | 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 | 2753 | # Column headers (except if detailed mode) |
| 2739 | 2754 | if not options.detailed_mode or options.triage_mode: |
| 2740 | 2755 | print '%-12s %-65s' % ('Flags', 'Filename') |
| ... | ... | @@ -2756,7 +2771,8 @@ def main(): |
| 2756 | 2771 | vba_parser.process_file(show_decoded_strings=options.show_decoded_strings, |
| 2757 | 2772 | display_code=options.display_code, global_analysis=True, #options.global_analysis, |
| 2758 | 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 | 2776 | else: |
| 2761 | 2777 | # print container name when it changes: |
| 2762 | 2778 | if container != previous_container: |
| ... | ... | @@ -2764,7 +2780,8 @@ def main(): |
| 2764 | 2780 | print '\nFiles in %s:' % container |
| 2765 | 2781 | previous_container = container |
| 2766 | 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 | 2785 | count += 1 |
| 2769 | 2786 | if not options.detailed_mode or options.triage_mode: |
| 2770 | 2787 | print '\n(Flags: OpX=OpenXML, XML=Word2003XML, MHT=MHTML, TXT=Text, M=Macros, ' \ |
| ... | ... | @@ -2776,7 +2793,8 @@ def main(): |
| 2776 | 2793 | vba_parser.process_file(show_decoded_strings=options.show_decoded_strings, |
| 2777 | 2794 | display_code=options.display_code, global_analysis=True, #options.global_analysis, |
| 2778 | 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 | 2800 | if __name__ == '__main__': | ... | ... |