Commit 9afc59abd38d23a56d8d6f3ec12a2ac84374a3fb

Authored by decalage2
1 parent ce3c92cf

rtfobj: detect executable filenames in OLE Package

Showing 1 changed file with 24 additions and 9 deletions
oletools/rtfobj.py
... ... @@ -60,6 +60,7 @@ http://www.decalage.info/python/oletools
60 60 # 2016-07-30 PL: - new API with class RtfObject
61 61 # - backward-compatible API rtf_iter_objects (fixed issue #70)
62 62 # 2016-07-31 PL: - table output with tablestream
  63 +# 2016-08-01 PL: - detect executable filenames in OLE Package
63 64  
64 65 __version__ = '0.50'
65 66  
... ... @@ -72,6 +73,7 @@ __version__ = '0.50'
72 73 # === IMPORTS =================================================================
73 74  
74 75 import re, os, sys, binascii, logging, optparse
  76 +import os.path
75 77  
76 78 from thirdparty.xglob import xglob
77 79 from oleobj import OleObject, OleNativeStream
... ... @@ -219,6 +221,10 @@ re_delims_bin_decimal = re.compile(DELIMITERS_ZeroOrMore + BACKSLASH_BIN
219 221 + DECIMAL_GROUP + DELIMITER)
220 222 re_delim_hexblock = re.compile(DELIMITER + PATTERN)
221 223  
  224 +# TODO: use a frozenset instead of a regex?
  225 +re_executable_extensions = re.compile(
  226 + r"(?i)\.(EXE|COM|PIF|GADGET|MSI|MSP|MSC|VBS|VBE|VB|JSE|JS|WSF|WSC|WSH|WS|BAT|CMD|DLL|SCR|HTA|CPL|CLASS|JAR|PS1XML|PS1|PS2XML|PS2|PSC1|PSC2|SCF|LNK|INF|REG)\b")
  227 +
222 228 # Destination Control Words, according to MS RTF Specifications v1.9.1:
223 229 DESTINATION_CONTROL_WORDS = frozenset((
224 230 b"aftncn", b"aftnsep", b"aftnsepc", b"annotation", b"atnauthor", b"atndate", b"atnicn", b"atnid", b"atnparent", b"atnref",
... ... @@ -604,12 +610,6 @@ def sanitize_filename(filename, replacement='_', max_length=200):
604 610  
605 611  
606 612 def process_file(container, filename, data, output_dir=None):
607   - tstream = tablestream.TableStream(
608   - column_width=(3, 10, 31, 31),
609   - header_row=('id', 'index', 'OLE Object', 'OLE Package'),
610   - style=tablestream.TableStyleSlim
611   - )
612   -
613 613 if output_dir:
614 614 if not os.path.isdir(output_dir):
615 615 log.info('creating output directory %s' % output_dir)
... ... @@ -625,8 +625,13 @@ def process_file(container, filename, data, output_dir=None):
625 625 # TODO: option to extract objects to files (false by default)
626 626 if data is None:
627 627 data = open(filename, 'rb').read()
628   - # print('='*79)
629   - # print('File: %r - %d bytes' % (filename, len(data)))
  628 + print('='*79)
  629 + print('File: %r - size: %d bytes' % (filename, len(data)))
  630 + tstream = tablestream.TableStream(
  631 + column_width=(3, 10, 31, 31),
  632 + header_row=('id', 'index', 'OLE Object', 'OLE Package'),
  633 + style=tablestream.TableStyleSlim
  634 + )
630 635 rtfp = RtfObjParser(data)
631 636 rtfp.parse()
632 637 for rtfobj in rtfp.objects:
... ... @@ -636,6 +641,7 @@ def process_file(container, filename, data, output_dir=None):
636 641 # fname = '%s_object_%08X.raw' % (fname_prefix, rtfobj.start)
637 642 # print('saving object to file %s' % fname)
638 643 # open(fname, 'wb').write(rtfobj.rawdata)
  644 + pkg_color = None
639 645 if rtfobj.is_ole:
640 646 ole_column = 'format_id: %d\n' % rtfobj.format_id
641 647 ole_column += 'class name: %r\n' % rtfobj.class_name
... ... @@ -670,9 +676,17 @@ def process_file(container, filename, data, output_dir=None):
670 676 # fname = '%s_object_%08X.noname' % (fname_prefix, rtfobj.start)
671 677 # print('saving to file %s' % fname)
672 678 # open(fname, 'wb').write(rtfobj.olepkgdata)
  679 + pkg_color = 'yellow'
  680 + # check if the file extension is executable:
  681 + _, ext = os.path.splitext(rtfobj.filename)
  682 + log.debug('File extension: %r' % ext)
  683 + if re_executable_extensions.match(ext):
  684 + pkg_color = 'red'
  685 + pkg_column += '\nEXECUTABLE FILE'
673 686 else:
674 687 pkg_column = 'Not an OLE Package'
675 688 else:
  689 + pkg_column = ''
676 690 ole_column = 'Not a well-formed OLE object'
677 691 tstream.write_row((
678 692 rtfp.objects.index(rtfobj),
... ... @@ -680,7 +694,8 @@ def process_file(container, filename, data, output_dir=None):
680 694 '%08Xh' % rtfobj.start,
681 695 ole_column,
682 696 pkg_column
683   - ))
  697 + ), colors=(None, None, None, pkg_color)
  698 + )
684 699  
685 700  
686 701 #=== MAIN =================================================================
... ...