Commit 9a505c809823a462e0335415c368b1826d3b2246

Authored by Philippe Lagadec
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:
... ...