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 | 1 | This license applies to the python-oletools package, apart from the thirdparty folder which contains third-party files |
| 2 | 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 | 6 | All rights reserved. |
| 7 | 7 | ... | ... |
README.md
| ... | ... | @@ -145,8 +145,10 @@ Projects using oletools: |
| 145 | 145 | oletools are used by a number of projects and online malware analysis services, |
| 146 | 146 | including |
| 147 | 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 | 149 | [Anlyz.io](https://sandbox.anlyz.io/), |
| 149 | 150 | [AssemblyLine](https://www.cse-cst.gc.ca/en/assemblyline), |
| 151 | +[Binary Refinery](https://github.com/binref/refinery), | |
| 150 | 152 | [CAPE](https://github.com/ctxis/CAPE), |
| 151 | 153 | [CinCan](https://cincan.io), |
| 152 | 154 | [Cortex XSOAR (Palo Alto)](https://cortex.marketplace.pan.dev/marketplace/details/Oletools/), |
| ... | ... | @@ -156,6 +158,7 @@ including |
| 156 | 158 | [DIARIO](https://diario.elevenpaths.com/), |
| 157 | 159 | [dridex.malwareconfig.com](https://dridex.malwareconfig.com), |
| 158 | 160 | [EML Analyzer](https://github.com/ninoseki/eml_analyzer), |
| 161 | +[EXPMON](https://pub.expmon.com/), | |
| 159 | 162 | [FAME](https://certsocietegenerale.github.io/fame/), |
| 160 | 163 | [FLARE-VM](https://github.com/fireeye/flare-vm), |
| 161 | 164 | [GLIMPS Malware](https://www.glimps.fr/en/glimps-malware-2/), |
| ... | ... | @@ -177,6 +180,7 @@ including |
| 177 | 180 | [PyCIRCLean](https://github.com/CIRCL/PyCIRCLean), |
| 178 | 181 | [QFlow](https://www.quarkslab.com/products-qflow/), |
| 179 | 182 | [Qu1cksc0pe](https://github.com/CYB3RMX/Qu1cksc0pe), |
| 183 | +[Tylabs QuickSand](https://github.com/tylabs/quicksand), | |
| 180 | 184 | [REMnux](https://remnux.org/), |
| 181 | 185 | [Snake](https://github.com/countercept/snake), |
| 182 | 186 | [SNDBOX](https://app.sndbox.com), |
| ... | ... | @@ -252,7 +256,7 @@ License |
| 252 | 256 | This license applies to the python-oletools package, apart from the thirdparty folder which contains third-party files |
| 253 | 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 | 261 | All rights reserved. |
| 258 | 262 | ... | ... |
oletools/olevba.py
| ... | ... | @@ -32,7 +32,7 @@ https://github.com/unixfreak0037/officeparser |
| 32 | 32 | |
| 33 | 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 | 36 | # All rights reserved. |
| 37 | 37 | # |
| 38 | 38 | # Redistribution and use in source and binary forms, with or without modification, |
| ... | ... | @@ -234,8 +234,9 @@ from __future__ import print_function |
| 234 | 234 | # 2020-09-28 PL: - added VBA_Parser.get_vba_code_all_modules (partial fix |
| 235 | 235 | # for issue #619) |
| 236 | 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 | 242 | # TODO: |
| ... | ... | @@ -1701,9 +1702,25 @@ class VBA_Project(object): |
| 1701 | 1702 | if self.syskind not in SYSKIND_NAME: |
| 1702 | 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 | 1723 | # Specifies the VBA project's LCID. |
| 1706 | - projectlcid_id = struct.unpack("<H", dir_stream.read(2))[0] | |
| 1707 | 1724 | self.check_value('PROJECTLCID_Id', 0x0002, projectlcid_id) |
| 1708 | 1725 | projectlcid_size = struct.unpack("<L", dir_stream.read(4))[0] |
| 1709 | 1726 | self.check_value('PROJECTLCID_Size', 0x0004, projectlcid_size) | ... | ... |
requirements.txt
setup.py
| ... | ... | @@ -55,7 +55,7 @@ import os, fnmatch |
| 55 | 55 | #--- METADATA ----------------------------------------------------------------- |
| 56 | 56 | |
| 57 | 57 | name = "oletools" |
| 58 | -version = '0.60.2dev4' | |
| 58 | +version = '0.60.2dev5' | |
| 59 | 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 | 60 | long_desc = open('oletools/README.rst').read() |
| 61 | 61 | author = "Philippe Lagadec" |
| ... | ... | @@ -320,7 +320,7 @@ def main(): |
| 320 | 320 | test_suite="tests", |
| 321 | 321 | # scripts=scripts, |
| 322 | 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 | 324 | "olefile>=0.46", |
| 325 | 325 | "easygui", |
| 326 | 326 | 'colorclass', | ... | ... |
tests/oleid/test_basic.py
| ... | ... | @@ -67,6 +67,11 @@ class TestOleIDBasic(unittest.TestCase): |
| 67 | 67 | '949: ANSI/OEM Korean (Unified Hangul Code)') |
| 68 | 68 | self.assertEqual(value_dict['author'], |
| 69 | 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 | 75 | else: |
| 71 | 76 | self.assertEqual(value_dict['codepage'], |
| 72 | 77 | '1252: ANSI Latin 1; Western European (Windows)') | ... | ... |
tests/olevba/test_basic.py
| ... | ... | @@ -151,6 +151,24 @@ class TestOlevbaBasic(unittest.TestCase): |
| 151 | 151 | self.assertIn('AutoExec', types) |
| 152 | 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 | 173 | # just in case somebody calls this file as a script |
| 156 | 174 | if __name__ == '__main__': | ... | ... |
tests/test-data/olevba/sample_with_vba.ppt
0 → 100644
No preview for this file type