def do_test_md5(self, args, test_fun=None, only_run_every=1): """ helper for test_md5 and test_md5_args """ data_dir = join(DATA_BASE_DIR, 'oleobj') # name of sample, extension of embedded file, md5 hash of embedded file for sample_index, (sample_name, embedded_name, expect_hash) \ in enumerate(SAMPLES): if sample_index % only_run_every != 0: continue args_with_path = args + [join(data_dir, sample_name), ] if test_fun is None: output, ret_val = call_and_capture('oleobj', args_with_path, accept_nonzero_exit=True) else: ret_val = test_fun(args_with_path) output = '[output: see above]' self.assertEqual(ret_val, oleobj.RETURN_DID_DUMP, msg='Wrong return value {} for {}. Output:\n{}' .format(ret_val, sample_name, output)) expect_name = join(self.temp_dir, sample_name + '_' + embedded_name) if not isfile(expect_name): self.did_fail = True self.fail('{0} not created from {1}. Output:\n{2}' .format(expect_name, sample_name, output)) continue md5_hash = calc_md5(expect_name) if md5_hash != expect_hash: self.did_fail = True self.fail('Wrong md5 {0} of {1} from {2}. Output:\n{3}' .format(md5_hash, expect_name, sample_name, output)) continue
def test_rtf_behaviour(self): """Test behaviour of olevba when presented with an rtf file.""" input_file = join(DATA_BASE_DIR, 'msodde', 'RTF-Spec-1.7.rtf') output, ret_code = call_and_capture('olevba', args=(input_file, ), accept_nonzero_exit=True) # check that return code is olevba.RETURN_OPEN_ERROR self.assertEqual(ret_code, 5) # check output: self.assertIn('FileOpenError', output) self.assertIn('is RTF', output) self.assertIn('rtfobj', output) # TODO: I disabled this test because we do not log "not encrypted" as warning anymore # to avoid other issues. # If we really want to test this, then the test should be run with log level INFO: # self.assertIn('not encrypted', output) # check warnings for line in output.splitlines(): if line.startswith('WARNING ') and 'encrypted' in line: continue # encryption warnings are ok elif 'warn' in line.lower(): raise self.fail('Found "warn" in output line: "{}"'.format( line.rstrip()))
def test_autostart(self): """Check that autostart macro is found in xls[mb] sample file.""" for suffix in 'xlsm', 'xlsb': example_file = pjoin( DATA_BASE_DIR, 'encrypted', 'autostart-encrypt-standardpassword.' + suffix) output, _ = call_and_capture('olevba', args=('-j', example_file), exclude_stderr=True) data = json.loads(output, object_pairs_hook=OrderedDict) # debug: json.dump(data, sys.stdout, indent=4) self.assertEqual(len(data), 4) self.assertIn('script_name', data[0]) self.assertIn('version', data[0]) self.assertEqual(data[0]['type'], 'MetaInformation') self.assertIn('return_code', data[-1]) self.assertEqual(data[-1]['type'], 'MetaInformation') self.assertEqual(data[1]['container'], None) self.assertEqual(data[1]['file'], example_file) self.assertEqual(data[1]['analysis'], None) self.assertEqual(data[1]['macros'], []) self.assertEqual(data[1]['type'], 'OLE') self.assertEqual(data[2]['container'], example_file) self.assertNotEqual(data[2]['file'], example_file) self.assertEqual(data[2]['type'], "OpenXML") analysis = data[2]['analysis'] self.assertEqual(analysis[0]['type'], 'AutoExec') self.assertEqual(analysis[0]['keyword'], 'Auto_Open') macros = data[2]['macros'] self.assertEqual(macros[0]['vba_filename'], 'Modul1.bas') self.assertIn('Sub Auto_Open()', macros[0]['code'])
def test_standard_password(self): """Check dde-link is found in xls[mb] sample files.""" for suffix in 'xls', 'xlsx', 'xlsm', 'xlsb': example_file = pjoin(DATA_BASE_DIR, 'encrypted', 'dde-test-encrypt-standardpassword.' + suffix) output, _ = call_and_capture('msodde', [ example_file, ]) self.assertIn('\nDDE Links:\ncmd /c calc.exe\n', output, msg='Unexpected output {!r} for {}'.format( output, suffix))
def test_crypt_output(self): """Check for helpful error message when failing to decrypt.""" for suffix in 'doc', 'docm', 'docx', 'ppt', 'pptm', 'pptx', 'xls', \ 'xlsb', 'xlsm', 'xlsx': example_file = join(BASE_DIR, 'encrypted', 'encrypted.' + suffix) output, ret_code = call_and_capture('msodde', [ example_file, ], accept_nonzero_exit=True) self.assertEqual(ret_code, 1) self.assertIn('passwords could not decrypt office file', output, msg='Unexpected output: {}'.format(output.strip()))
def test_no_output(self): """ test that oleobj does not find data where it should not """ args = ['-d', self.temp_dir] for sample_name in ('sample_with_lnk_to_calc.doc', 'embedded-simple-2007.xml', 'embedded-simple-2007-as2003.xml'): full_name = join(DATA_BASE_DIR, 'oleobj', sample_name) output, ret_val = call_and_capture('oleobj', args + [full_name, ], accept_nonzero_exit=True) if glob(self.temp_dir + 'ole-object-*'): self.fail('found embedded data in {0}. Output:\n{1}' .format(sample_name, output)) self.assertEqual(ret_val, oleobj.RETURN_NO_DUMP, msg='Wrong return value {} for {}. Output:\n{}' .format(ret_val, sample_name, output))
def test_external_links(self): """ loop through sample files asserting that external links are found """ for dirpath, _, filenames in os.walk(BASE_DIR): for filename in filenames: file_path = path.join(dirpath, filename) output, ret_val = call_and_capture('oleobj', [ file_path, ], accept_nonzero_exit=True) self.assertEqual( ret_val, oleobj.RETURN_DID_DUMP, msg='Wrong return value {} for {}. Output:\n{}'.format( ret_val, filename, output))
def test_crypt_return(self): """ Test that encrypted files give a certain return code. Currently, only the encryption applied by Office 2010 (CryptoApi RC4 Encryption) is tested. """ CRYPT_DIR = join(DATA_BASE_DIR, 'encrypted') CRYPT_RETURN_CODE = 9 ADD_ARGS = [], [ '-d', ], [ '-a', ], [ '-j', ], [ '-t', ] # only 1st file EXCEPTIONS = [ 'autostart-encrypt-standardpassword.xls', # These ... 'autostart-encrypt-standardpassword.xlsm', # files ... 'autostart-encrypt-standardpassword.xlsb', # are ... 'dde-test-encrypt-standardpassword.xls', # automati... 'dde-test-encrypt-standardpassword.xlsx', # ...cally... 'dde-test-encrypt-standardpassword.xlsm', # decrypted. 'dde-test-encrypt-standardpassword.xlsb' ] for filename in os.listdir(CRYPT_DIR): if filename in EXCEPTIONS: continue full_name = join(CRYPT_DIR, filename) for args in ADD_ARGS: _, ret_code = call_and_capture('olevba', args=[ full_name, ] + args, accept_nonzero_exit=True) self.assertEqual(ret_code, CRYPT_RETURN_CODE, msg='Wrong return code {} for args {}'\ .format(ret_code, args + [filename, ])) # test only first file with all arg combinations, others just # without arg (test takes too long otherwise ADD_ARGS = ([], )
def test_xlm(self): """Test that xlm macros are found.""" XLM_DIR = join(DATA_BASE_DIR, 'excel4-macros') ADD_ARGS = ['-j'] for filename in os.listdir(XLM_DIR): full_name = join(XLM_DIR, filename) suffix = splitext(filename)[1] out_str, ret_code = call_and_capture('olevba', args=[ full_name, ] + ADD_ARGS, accept_nonzero_exit=True) output = json.loads(out_str) self.assertEqual(len(output), 3) self.assertEqual(output[0]['type'], 'MetaInformation') self.assertEqual(output[0]['script_name'], 'olevba') self.assertEqual(output[-1]['type'], 'MetaInformation') self.assertEqual(output[-1]['n_processed'], 1) self.assertEqual(output[-1]['return_code'], 0) result = output[1] self.assertTrue(result['json_conversion_successful']) if suffix in ('.xlsb', '.xltm', '.xlsm'): # TODO: cannot extract xlm macros for these types yet self.assertEqual(result['macros'], []) else: code = result['macros'][0]['code'] if suffix == '.slk': self.assertIn('Excel 4 macros extracted', code) else: self.assertIn('Excel 4.0 macro sheet', code) self.assertIn('Auto_Open', code) if 'excel5' not in filename: # TODO: is not found in excel5 self.assertIn('ALERT(', code) self.assertIn('HALT()', code) self.assertIn(len(result['analysis']), (2, 3)) types = [entry['type'] for entry in result['analysis']] keywords = [entry['keyword'] for entry in result['analysis']] self.assertIn('Auto_Open', keywords) self.assertIn('XLM macro', keywords) self.assertIn('AutoExec', types) self.assertIn('Suspicious', types)
def do_test_behaviour(self, filename): """Helper for test_{text,empty}_behaviour.""" input_file = join(DATA_BASE_DIR, 'basic', filename) output, _ = call_and_capture('olevba', args=(input_file, )) # check output self.assertTrue(re.search(r'^Type:\s+Text\s*$', output, re.MULTILINE), msg='"Type: Text" not found in output:\n' + output) self.assertTrue(re.search(r'^No suspicious .+ found.$', output, re.MULTILINE), msg='"No suspicous...found" not found in output:\n' + \ output) self.assertNotIn('error', output.lower()) # check warnings for line in output.splitlines(): if line.startswith('WARNING ') and 'encrypted' in line: continue # encryption warnings are ok elif 'warn' in line.lower(): raise self.fail('Found "warn" in output line: "{}"'.format( line.rstrip()))
def test_autostart(self): """Check that autostart macro is found in xls[mb] sample file.""" for suffix in 'xlsm', 'xlsb': example_file = pjoin( DATA_BASE_DIR, 'encrypted', 'autostart-encrypt-standardpassword.' + suffix) output, _ = call_and_capture('olevba', args=('-j', example_file), exclude_stderr=True) data = json.loads(output, object_pairs_hook=OrderedDict) # debug: json.dump(data, sys.stdout, indent=4) self.assertIn(len(data), (3, 4)) # first 2 parts: general info about script and file self.assertIn('script_name', data[0]) self.assertIn('version', data[0]) self.assertEqual(data[0]['type'], 'MetaInformation') self.assertEqual(data[1]['container'], None) self.assertEqual(data[1]['file'], example_file) self.assertEqual(data[1]['analysis'], None) self.assertEqual(data[1]['macros'], []) self.assertEqual(data[1]['type'], 'OLE') self.assertTrue(data[1]['json_conversion_successful']) # possible VBA stomping warning if len(data) == 4: self.assertEqual(data[2]['type'], 'msg') self.assertIn('VBA stomping', data[2]['msg']) # last part is the actual result self.assertEqual(data[-1]['container'], example_file) self.assertNotEqual(data[-1]['file'], example_file) self.assertEqual(data[-1]['type'], "OpenXML") analysis = data[-1]['analysis'] self.assertEqual(analysis[0]['type'], 'AutoExec') self.assertEqual(analysis[0]['keyword'], 'Auto_Open') macros = data[-1]['macros'] self.assertEqual(macros[0]['vba_filename'], 'Modul1.bas') self.assertIn('Sub Auto_Open()', macros[0]['code']) self.assertTrue(data[-1]['json_conversion_successful'])
def test_rtf_behaviour(self): """Test behaviour of olevba when presented with an rtf file.""" input_file = join(DATA_BASE_DIR, 'msodde', 'RTF-Spec-1.7.rtf') output, ret_code = call_and_capture('olevba', args=(input_file, ), accept_nonzero_exit=True) # check that return code is olevba.RETURN_OPEN_ERROR self.assertEqual(ret_code, 5) # check output: self.assertIn('FileOpenError', output) self.assertIn('is RTF', output) self.assertIn('rtfobj.py', output) self.assertIn('not encrypted', output) # check warnings for line in output.splitlines(): if line.startswith('WARNING ') and 'encrypted' in line: continue # encryption warnings are ok elif 'warn' in line.lower(): raise self.fail('Found "warn" in output line: "{}"'.format( line.rstrip()))