Commit 9a505c809823a462e0335415c368b1826d3b2246
1 parent
5dfb7b56
olevba: triage now uses VBA_Scanner results, shows Base64 and Dridex strings, ex…
…ception handling in detect_base64_strings
Showing
1 changed file
with
44 additions
and
14 deletions
oletools/olevba.py
| ... | ... | @@ -114,8 +114,11 @@ https://github.com/unixfreak0037/officeparser |
| 114 | 114 | # 2015-02-01 v0.22 PL: - fixed issue #4: regex for URL, e-mail and exe filename |
| 115 | 115 | # - added Base64 obfuscation decoding (contribution from |
| 116 | 116 | # @JamesHabben) |
| 117 | +# 2015-02-03 v0.23 PL: - triage now uses VBA_Scanner results, shows Base64 and | |
| 118 | +# Dridex strings | |
| 119 | +# - exception handling in detect_base64_strings | |
| 117 | 120 | |
| 118 | -__version__ = '0.22' | |
| 121 | +__version__ = '0.23' | |
| 119 | 122 | |
| 120 | 123 | #------------------------------------------------------------------------------ |
| 121 | 124 | # TODO: |
| ... | ... | @@ -940,14 +943,19 @@ def detect_base64_strings(vba_code): |
| 940 | 943 | :param vba_code: str, VBA source code |
| 941 | 944 | :return: list of str tuples (encoded string, decoded string) |
| 942 | 945 | """ |
| 946 | + #TODO: avoid matching simple hex strings as base64? | |
| 943 | 947 | results = [] |
| 944 | 948 | found = set() |
| 945 | 949 | for match in re_base64_string.finditer(vba_code): |
| 946 | 950 | value = match.group() |
| 947 | 951 | if value not in found: |
| 948 | - decoded = base64.b64decode(value) | |
| 949 | - results.append((value, decoded)) | |
| 950 | - found.add(value) | |
| 952 | + try: | |
| 953 | + decoded = base64.b64decode(value) | |
| 954 | + results.append((value, decoded)) | |
| 955 | + found.add(value) | |
| 956 | + except: | |
| 957 | + # if an exception occurs, it is likely not a base64-encoded string | |
| 958 | + pass | |
| 951 | 959 | return results |
| 952 | 960 | |
| 953 | 961 | |
| ... | ... | @@ -1076,6 +1084,20 @@ class VBA_Scanner (object): |
| 1076 | 1084 | results.append(('Dridex string', repr(decoded), encoded)) |
| 1077 | 1085 | return results |
| 1078 | 1086 | |
| 1087 | + def scan_summary(self): | |
| 1088 | + """ | |
| 1089 | + Analyze the provided VBA code to detect suspicious keywords, | |
| 1090 | + auto-executable macros, IOC patterns, obfuscation patterns | |
| 1091 | + such as hex-encoded strings. | |
| 1092 | + | |
| 1093 | + :return: tuple with the number of items found for each category: | |
| 1094 | + (autoexec, suspicious, IOCs, hex, base64, dridex) | |
| 1095 | + """ | |
| 1096 | + self.scan() | |
| 1097 | + return (len(self.autoexec_keywords), len(self.suspicious_keywords), | |
| 1098 | + len(self.iocs), len(self.hex_strings), len(self.base64_strings), | |
| 1099 | + len(self.dridex_strings)) | |
| 1100 | + | |
| 1079 | 1101 | |
| 1080 | 1102 | |
| 1081 | 1103 | def scan_vba(vba_code, include_hex_strings): |
| ... | ... | @@ -1397,6 +1419,8 @@ def process_file_triage (container, filename, data): |
| 1397 | 1419 | nb_suspicious = 0 |
| 1398 | 1420 | nb_iocs = 0 |
| 1399 | 1421 | nb_hexstrings = 0 |
| 1422 | + nb_base64strings = 0 | |
| 1423 | + nb_dridexstrings = 0 | |
| 1400 | 1424 | # ftype = 'Other' |
| 1401 | 1425 | message = '' |
| 1402 | 1426 | try: |
| ... | ... | @@ -1406,22 +1430,28 @@ def process_file_triage (container, filename, data): |
| 1406 | 1430 | for (subfilename, stream_path, vba_filename, vba_code) in vba.extract_macros(): |
| 1407 | 1431 | nb_macros += 1 |
| 1408 | 1432 | if vba_code.strip() != '': |
| 1409 | - #TODO: same changes as scan_vba, or modify scan_vba to return these counts | |
| 1410 | - nb_autoexec += len(detect_autoexec(vba_code)) | |
| 1411 | - nb_suspicious += len(detect_suspicious(vba_code)) | |
| 1412 | - nb_iocs += len(detect_patterns(vba_code)) | |
| 1413 | - nb_hexstrings += len(detect_hex_strings(vba_code)) | |
| 1433 | + scanner = VBA_Scanner(vba_code) | |
| 1434 | + autoexec, suspicious, iocs, hexstrings, base64strings, dridex = scanner.scan_summary() | |
| 1435 | + nb_autoexec += autoexec | |
| 1436 | + nb_suspicious += suspicious | |
| 1437 | + nb_iocs += iocs | |
| 1438 | + nb_hexstrings += hexstrings | |
| 1439 | + nb_base64strings += base64strings | |
| 1440 | + nb_dridexstrings += dridex | |
| 1414 | 1441 | if vba.type == TYPE_OLE: |
| 1415 | - flags = 'O' | |
| 1442 | + flags = 'OLE:' | |
| 1416 | 1443 | else: |
| 1417 | - flags = 'X' | |
| 1418 | - macros = autoexec = suspicious = iocs = hexstrings = '-' | |
| 1444 | + flags = 'OpX:' | |
| 1445 | + macros = autoexec = suspicious = iocs = hexstrings = base64strings = dridex = '-' | |
| 1419 | 1446 | if nb_macros: macros = 'M' |
| 1420 | 1447 | if nb_autoexec: autoexec = 'A' |
| 1421 | 1448 | if nb_suspicious: suspicious = 'S' |
| 1422 | 1449 | if nb_iocs: iocs = 'I' |
| 1423 | 1450 | if nb_hexstrings: hexstrings = 'H' |
| 1424 | - flags += '%s%s%s%s%s' % (macros, autoexec, suspicious, iocs, hexstrings) | |
| 1451 | + if nb_base64strings: base64strings = 'B' | |
| 1452 | + if nb_dridexstrings: dridex = 'D' | |
| 1453 | + flags += '%s%s%s%s%s%s%s' % (macros, autoexec, suspicious, iocs, hexstrings, | |
| 1454 | + base64strings, dridex) | |
| 1425 | 1455 | |
| 1426 | 1456 | # macros = autoexec = suspicious = iocs = hexstrings = 'no' |
| 1427 | 1457 | # if nb_macros: macros = 'YES:%d' % nb_macros |
| ... | ... | @@ -1535,7 +1565,7 @@ def main(): |
| 1535 | 1565 | process_file_triage(container, filename, data) |
| 1536 | 1566 | count += 1 |
| 1537 | 1567 | if not options.detailed_mode or options.triage_mode: |
| 1538 | - print '\n(Flags: O=OLE, X=OpenXML, M=Macros, A=Auto-executable, S=Suspicious keywords, I=IOCs, H=Hex-encoded strings, ?=Unknown)\n' | |
| 1568 | + print '\n(Flags: OpX=OpenXML, M=Macros, A=Auto-executable, S=Suspicious keywords, I=IOCs, H=Hex strings, B=Base64 strings, D=Dridex strings, ?=Unknown)\n' | |
| 1539 | 1569 | |
| 1540 | 1570 | if count == 1 and not options.triage_mode and not options.detailed_mode: |
| 1541 | 1571 | # if options -t and -d were not specified and it's a single file, print details: | ... | ... |