Commit 3b7a4ea86c282ac3017240c2678b2c889c1abac3

Authored by Philippe Lagadec
Committed by GitHub
2 parents f93f527e 78b2d459

Merge branch 'master' into unittest-automation

.gitignore
@@ -98,3 +98,4 @@ ENV/ @@ -98,3 +98,4 @@ ENV/
98 .ropeproject 98 .ropeproject
99 99
100 /temp/ 100 /temp/
  101 +/issues/
LICENSE.md
1 This license applies to the python-oletools package, apart from the thirdparty folder which contains third-party files 1 This license applies to the python-oletools package, apart from the thirdparty folder which contains third-party files
2 published with their own license. 2 published with their own license.
3 3
4 -The python-oletools package is copyright (c) 2012-2023 Philippe Lagadec (http://www.decalage.info) 4 +The python-oletools package is copyright (c) 2012-2024 Philippe Lagadec (http://www.decalage.info)
5 5
6 All rights reserved. 6 All rights reserved.
7 7
README.md
@@ -145,8 +145,10 @@ Projects using oletools: @@ -145,8 +145,10 @@ Projects using oletools:
145 oletools are used by a number of projects and online malware analysis services, 145 oletools are used by a number of projects and online malware analysis services,
146 including 146 including
147 [ACE](https://github.com/IntegralDefense/ACE), 147 [ACE](https://github.com/IntegralDefense/ACE),
  148 +[ADAPT](https://www.blackhat.com/eu-23/briefings/schedule/index.html#unmasking-apts-an-automated-approach-for-real-world-threat-attribution-35162),
148 [Anlyz.io](https://sandbox.anlyz.io/), 149 [Anlyz.io](https://sandbox.anlyz.io/),
149 [AssemblyLine](https://www.cse-cst.gc.ca/en/assemblyline), 150 [AssemblyLine](https://www.cse-cst.gc.ca/en/assemblyline),
  151 +[Binary Refinery](https://github.com/binref/refinery),
150 [CAPE](https://github.com/ctxis/CAPE), 152 [CAPE](https://github.com/ctxis/CAPE),
151 [CinCan](https://cincan.io), 153 [CinCan](https://cincan.io),
152 [Cortex XSOAR (Palo Alto)](https://cortex.marketplace.pan.dev/marketplace/details/Oletools/), 154 [Cortex XSOAR (Palo Alto)](https://cortex.marketplace.pan.dev/marketplace/details/Oletools/),
@@ -156,6 +158,7 @@ including @@ -156,6 +158,7 @@ including
156 [DIARIO](https://diario.elevenpaths.com/), 158 [DIARIO](https://diario.elevenpaths.com/),
157 [dridex.malwareconfig.com](https://dridex.malwareconfig.com), 159 [dridex.malwareconfig.com](https://dridex.malwareconfig.com),
158 [EML Analyzer](https://github.com/ninoseki/eml_analyzer), 160 [EML Analyzer](https://github.com/ninoseki/eml_analyzer),
  161 +[EXPMON](https://pub.expmon.com/),
159 [FAME](https://certsocietegenerale.github.io/fame/), 162 [FAME](https://certsocietegenerale.github.io/fame/),
160 [FLARE-VM](https://github.com/fireeye/flare-vm), 163 [FLARE-VM](https://github.com/fireeye/flare-vm),
161 [GLIMPS Malware](https://www.glimps.fr/en/glimps-malware-2/), 164 [GLIMPS Malware](https://www.glimps.fr/en/glimps-malware-2/),
@@ -177,6 +180,7 @@ including @@ -177,6 +180,7 @@ including
177 [PyCIRCLean](https://github.com/CIRCL/PyCIRCLean), 180 [PyCIRCLean](https://github.com/CIRCL/PyCIRCLean),
178 [QFlow](https://www.quarkslab.com/products-qflow/), 181 [QFlow](https://www.quarkslab.com/products-qflow/),
179 [Qu1cksc0pe](https://github.com/CYB3RMX/Qu1cksc0pe), 182 [Qu1cksc0pe](https://github.com/CYB3RMX/Qu1cksc0pe),
  183 +[Tylabs QuickSand](https://github.com/tylabs/quicksand),
180 [REMnux](https://remnux.org/), 184 [REMnux](https://remnux.org/),
181 [Snake](https://github.com/countercept/snake), 185 [Snake](https://github.com/countercept/snake),
182 [SNDBOX](https://app.sndbox.com), 186 [SNDBOX](https://app.sndbox.com),
@@ -252,7 +256,7 @@ License @@ -252,7 +256,7 @@ License
252 This license applies to the python-oletools package, apart from the thirdparty folder which contains third-party files 256 This license applies to the python-oletools package, apart from the thirdparty folder which contains third-party files
253 published with their own license. 257 published with their own license.
254 258
255 -The python-oletools package is copyright (c) 2012-2023 Philippe Lagadec (http://www.decalage.info) 259 +The python-oletools package is copyright (c) 2012-2024 Philippe Lagadec (http://www.decalage.info)
256 260
257 All rights reserved. 261 All rights reserved.
258 262
oletools/olevba.py
@@ -32,7 +32,7 @@ https://github.com/unixfreak0037/officeparser @@ -32,7 +32,7 @@ https://github.com/unixfreak0037/officeparser
32 32
33 # === LICENSE ================================================================== 33 # === LICENSE ==================================================================
34 34
35 -# olevba is copyright (c) 2014-2022 Philippe Lagadec (http://www.decalage.info) 35 +# olevba is copyright (c) 2014-2024 Philippe Lagadec (http://www.decalage.info)
36 # All rights reserved. 36 # All rights reserved.
37 # 37 #
38 # Redistribution and use in source and binary forms, with or without modification, 38 # Redistribution and use in source and binary forms, with or without modification,
@@ -234,8 +234,9 @@ from __future__ import print_function @@ -234,8 +234,9 @@ from __future__ import print_function
234 # 2020-09-28 PL: - added VBA_Parser.get_vba_code_all_modules (partial fix 234 # 2020-09-28 PL: - added VBA_Parser.get_vba_code_all_modules (partial fix
235 # for issue #619) 235 # for issue #619)
236 # 2021-04-14 PL: - added detection of Workbook_BeforeClose (issue #518) 236 # 2021-04-14 PL: - added detection of Workbook_BeforeClose (issue #518)
  237 +# 2021-11-09 KJ: - added PROJECTCOMPATVERSION Record on dir Stream
237 238
238 -__version__ = '0.60.2dev1' 239 +__version__ = '0.60.2dev5'
239 240
240 #------------------------------------------------------------------------------ 241 #------------------------------------------------------------------------------
241 # TODO: 242 # TODO:
@@ -1701,9 +1702,25 @@ class VBA_Project(object): @@ -1701,9 +1702,25 @@ class VBA_Project(object):
1701 if self.syskind not in SYSKIND_NAME: 1702 if self.syskind not in SYSKIND_NAME:
1702 log.error("invalid PROJECTSYSKIND_SysKind {0:04X}".format(self.syskind)) 1703 log.error("invalid PROJECTSYSKIND_SysKind {0:04X}".format(self.syskind))
1703 1704
1704 - # PROJECTLCID Record 1705 + # PROJECTLCID Record or PROJECTCOMPATVERSION Record
  1706 + project_id = struct.unpack("<H", dir_stream.read(2))[0]
  1707 + if project_id == 0x004A:
  1708 + # PROJECTCOMPATVERSION Record
  1709 + # Specifies the VBA project's compat version.
  1710 + projectcompatversion_id = project_id
  1711 + self.check_value('PROJETCOMPATVERSION_Id', 0x004A, projectcompatversion_id)
  1712 + projectcompatversion_size = struct.unpack("<L", dir_stream.read(4))[0]
  1713 + self.check_value('PROJECTCOMPATVERSION_Size', 0x0004, projectcompatversion_size)
  1714 + projectcompatversion_compatversion = struct.unpack("<L", dir_stream.read(4))[0]
  1715 + # compat version: A 32-bit number that identifies the Office Model version used by a VBA project.
  1716 + log.debug("compat version: {compat_version}".format(compat_version=projectcompatversion_compatversion))
  1717 +
  1718 + # PROJECTLCID Record
  1719 + project_id = struct.unpack("<H", dir_stream.read(2))[0]
  1720 +
  1721 + projectlcid_id = project_id
  1722 +
1705 # Specifies the VBA project's LCID. 1723 # Specifies the VBA project's LCID.
1706 - projectlcid_id = struct.unpack("<H", dir_stream.read(2))[0]  
1707 self.check_value('PROJECTLCID_Id', 0x0002, projectlcid_id) 1724 self.check_value('PROJECTLCID_Id', 0x0002, projectlcid_id)
1708 projectlcid_size = struct.unpack("<L", dir_stream.read(4))[0] 1725 projectlcid_size = struct.unpack("<L", dir_stream.read(4))[0]
1709 self.check_value('PROJECTLCID_Size', 0x0004, projectlcid_size) 1726 self.check_value('PROJECTLCID_Size', 0x0004, projectlcid_size)
requirements.txt
1 -pyparsing>=2.1.0,<3 1 +pyparsing>=2.1.0,<4
2 olefile>=0.46 2 olefile>=0.46
3 easygui 3 easygui
4 colorclass 4 colorclass
setup.py
@@ -55,7 +55,7 @@ import os, fnmatch @@ -55,7 +55,7 @@ import os, fnmatch
55 #--- METADATA ----------------------------------------------------------------- 55 #--- METADATA -----------------------------------------------------------------
56 56
57 name = "oletools" 57 name = "oletools"
58 -version = '0.60.2dev4' 58 +version = '0.60.2dev5'
59 desc = "Python tools to analyze security characteristics of MS Office and OLE files (also called Structured Storage, Compound File Binary Format or Compound Document File Format), for Malware Analysis and Incident Response #DFIR" 59 desc = "Python tools to analyze security characteristics of MS Office and OLE files (also called Structured Storage, Compound File Binary Format or Compound Document File Format), for Malware Analysis and Incident Response #DFIR"
60 long_desc = open('oletools/README.rst').read() 60 long_desc = open('oletools/README.rst').read()
61 author = "Philippe Lagadec" 61 author = "Philippe Lagadec"
@@ -320,7 +320,7 @@ def main(): @@ -320,7 +320,7 @@ def main():
320 test_suite="tests", 320 test_suite="tests",
321 # scripts=scripts, 321 # scripts=scripts,
322 install_requires=[ 322 install_requires=[
323 - "pyparsing>=2.1.0,<3", # changed from 2.2.0 to 2.1.0 for issue #481 323 + "pyparsing>=2.1.0,<4", # changed from 2.2.0 to 2.1.0 for issue #481
324 "olefile>=0.46", 324 "olefile>=0.46",
325 "easygui", 325 "easygui",
326 'colorclass', 326 'colorclass',
tests/oleid/test_basic.py
@@ -67,6 +67,11 @@ class TestOleIDBasic(unittest.TestCase): @@ -67,6 +67,11 @@ class TestOleIDBasic(unittest.TestCase):
67 '949: ANSI/OEM Korean (Unified Hangul Code)') 67 '949: ANSI/OEM Korean (Unified Hangul Code)')
68 self.assertEqual(value_dict['author'], 68 self.assertEqual(value_dict['author'],
69 b'\xb1\xe8\xb1\xe2\xc1\xa4;kijeong') 69 b'\xb1\xe8\xb1\xe2\xc1\xa4;kijeong')
  70 + elif 'olevba/sample_with_vba.ppt' in filename:
  71 + self.assertEqual(value_dict['codepage'],
  72 + '949: ANSI/OEM Korean (Unified Hangul Code)')
  73 + self.assertEqual(value_dict['author'],
  74 + b'\xb1\xe8 \xb1\xe2\xc1\xa4')
70 else: 75 else:
71 self.assertEqual(value_dict['codepage'], 76 self.assertEqual(value_dict['codepage'],
72 '1252: ANSI Latin 1; Western European (Windows)') 77 '1252: ANSI Latin 1; Western European (Windows)')
tests/olevba/test_basic.py
@@ -151,6 +151,24 @@ class TestOlevbaBasic(unittest.TestCase): @@ -151,6 +151,24 @@ class TestOlevbaBasic(unittest.TestCase):
151 self.assertIn('AutoExec', types) 151 self.assertIn('AutoExec', types)
152 self.assertIn('Suspicious', types) 152 self.assertIn('Suspicious', types)
153 153
  154 + def test_dir_stream_record_project_compat_version(self):
  155 + """Test PROJECTCOMPATVERSION record on dir stream with a ppt file."""
  156 + input_file = join(DATA_BASE_DIR, 'olevba', 'sample_with_vba.ppt')
  157 + output, ret_code = call_and_capture('olevba', args=(input_file, "--loglevel", "debug"))
  158 +
  159 + # check return code
  160 + self.assertEqual(ret_code, 0)
  161 +
  162 + # not expected string:
  163 + self.assertNotIn('invalid value for PROJECTLCID_Id expected 0002 got', output)
  164 + self.assertNotIn('Error in _extract_vba', output)
  165 +
  166 + # compat version in debug mode:
  167 + self.assertIn('compat version: 2', output)
  168 +
  169 + # vba contents:
  170 + self.assertIn('Sub Action_Click()\n MsgBox "The action button clicked!"\nEnd Sub', output)
  171 +
154 172
155 # just in case somebody calls this file as a script 173 # just in case somebody calls this file as a script
156 if __name__ == '__main__': 174 if __name__ == '__main__':
tests/test-data/olevba/sample_with_vba.ppt 0 → 100644
No preview for this file type