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,8 +114,11 @@ https://github.com/unixfreak0037/officeparser
114 # 2015-02-01 v0.22 PL: - fixed issue #4: regex for URL, e-mail and exe filename 114 # 2015-02-01 v0.22 PL: - fixed issue #4: regex for URL, e-mail and exe filename
115 # - added Base64 obfuscation decoding (contribution from 115 # - added Base64 obfuscation decoding (contribution from
116 # @JamesHabben) 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 # TODO: 124 # TODO:
@@ -940,14 +943,19 @@ def detect_base64_strings(vba_code): @@ -940,14 +943,19 @@ def detect_base64_strings(vba_code):
940 :param vba_code: str, VBA source code 943 :param vba_code: str, VBA source code
941 :return: list of str tuples (encoded string, decoded string) 944 :return: list of str tuples (encoded string, decoded string)
942 """ 945 """
  946 + #TODO: avoid matching simple hex strings as base64?
943 results = [] 947 results = []
944 found = set() 948 found = set()
945 for match in re_base64_string.finditer(vba_code): 949 for match in re_base64_string.finditer(vba_code):
946 value = match.group() 950 value = match.group()
947 if value not in found: 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 return results 959 return results
952 960
953 961
@@ -1076,6 +1084,20 @@ class VBA_Scanner (object): @@ -1076,6 +1084,20 @@ class VBA_Scanner (object):
1076 results.append(('Dridex string', repr(decoded), encoded)) 1084 results.append(('Dridex string', repr(decoded), encoded))
1077 return results 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 def scan_vba(vba_code, include_hex_strings): 1103 def scan_vba(vba_code, include_hex_strings):
@@ -1397,6 +1419,8 @@ def process_file_triage (container, filename, data): @@ -1397,6 +1419,8 @@ def process_file_triage (container, filename, data):
1397 nb_suspicious = 0 1419 nb_suspicious = 0
1398 nb_iocs = 0 1420 nb_iocs = 0
1399 nb_hexstrings = 0 1421 nb_hexstrings = 0
  1422 + nb_base64strings = 0
  1423 + nb_dridexstrings = 0
1400 # ftype = 'Other' 1424 # ftype = 'Other'
1401 message = '' 1425 message = ''
1402 try: 1426 try:
@@ -1406,22 +1430,28 @@ def process_file_triage (container, filename, data): @@ -1406,22 +1430,28 @@ def process_file_triage (container, filename, data):
1406 for (subfilename, stream_path, vba_filename, vba_code) in vba.extract_macros(): 1430 for (subfilename, stream_path, vba_filename, vba_code) in vba.extract_macros():
1407 nb_macros += 1 1431 nb_macros += 1
1408 if vba_code.strip() != '': 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 if vba.type == TYPE_OLE: 1441 if vba.type == TYPE_OLE:
1415 - flags = 'O' 1442 + flags = 'OLE:'
1416 else: 1443 else:
1417 - flags = 'X'  
1418 - macros = autoexec = suspicious = iocs = hexstrings = '-' 1444 + flags = 'OpX:'
  1445 + macros = autoexec = suspicious = iocs = hexstrings = base64strings = dridex = '-'
1419 if nb_macros: macros = 'M' 1446 if nb_macros: macros = 'M'
1420 if nb_autoexec: autoexec = 'A' 1447 if nb_autoexec: autoexec = 'A'
1421 if nb_suspicious: suspicious = 'S' 1448 if nb_suspicious: suspicious = 'S'
1422 if nb_iocs: iocs = 'I' 1449 if nb_iocs: iocs = 'I'
1423 if nb_hexstrings: hexstrings = 'H' 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 # macros = autoexec = suspicious = iocs = hexstrings = 'no' 1456 # macros = autoexec = suspicious = iocs = hexstrings = 'no'
1427 # if nb_macros: macros = 'YES:%d' % nb_macros 1457 # if nb_macros: macros = 'YES:%d' % nb_macros
@@ -1535,7 +1565,7 @@ def main(): @@ -1535,7 +1565,7 @@ def main():
1535 process_file_triage(container, filename, data) 1565 process_file_triage(container, filename, data)
1536 count += 1 1566 count += 1
1537 if not options.detailed_mode or options.triage_mode: 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 if count == 1 and not options.triage_mode and not options.detailed_mode: 1570 if count == 1 and not options.triage_mode and not options.detailed_mode:
1541 # if options -t and -d were not specified and it's a single file, print details: 1571 # if options -t and -d were not specified and it's a single file, print details: