Commit 3b7a4ea86c282ac3017240c2678b2c889c1abac3
Committed by
GitHub
Merge branch 'master' into unittest-automation
Showing
9 changed files
with
54 additions
and
9 deletions
.gitignore
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
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