From 6381b67125cd18da4f764d68a4ba392b68be2e69 Mon Sep 17 00:00:00 2001 From: Ryan O'Horo <10855297+ryanohoro@users.noreply.github.com> Date: Wed, 6 Mar 2024 23:23:05 -0600 Subject: [PATCH 1/8] Port ScanZip improvements to ScanRar, fixes #441, port password caching to ScanZip --- configs/python/backend/backend.yaml | 5 + docs/README.md | 6 +- src/python/strelka/scanners/scan_rar.py | 179 ++++-- src/python/strelka/scanners/scan_zip.py | 34 +- .../strelka/tests/fixtures/test_big.rar | Bin 0 -> 20848 bytes .../strelka/tests/fixtures/test_password.rar | Bin 0 -> 4832 bytes src/python/strelka/tests/test_scan_rar.py | 518 ++++++++++++++++++ 7 files changed, 681 insertions(+), 61 deletions(-) create mode 100755 src/python/strelka/tests/fixtures/test_big.rar create mode 100755 src/python/strelka/tests/fixtures/test_password.rar diff --git a/configs/python/backend/backend.yaml b/configs/python/backend/backend.yaml index cf474bfa..714eb288 100644 --- a/configs/python/backend/backend.yaml +++ b/configs/python/backend/backend.yaml @@ -503,6 +503,11 @@ scanners: priority: 5 options: limit: 1000 + limit_metadata: True + size_limit: 250000000 + crack_pws: False + log_pws: True + password_file: '/etc/strelka/passwords.dat' 'ScanRpm': - positive: flavors: diff --git a/docs/README.md b/docs/README.md index ae440b6b..3a3c3395 100644 --- a/docs/README.md +++ b/docs/README.md @@ -789,7 +789,7 @@ The table below describes each scanner and its options. Each scanner has the hid | ScanFalconSandbox | Sends files to an instance of Falcon Sandbox | `server` -- URL of the Falcon Sandbox API inteface
`priority` -- Falcon Sandbox priority assigned to the task (defaults to `3`)
`timeout` -- amount of time (in seconds) to wait for the task to upload (defaults to `60`)
`envID` -- list of numeric envrionment IDs that tells Falcon Sandbox which sandbox to submit a sample to (defaults to `[100]`)
`api_key` -- API key used for authenticating to Falcon Sandbox (defaults to None, optionally read from environment variable "FS_API_KEY")
`api_secret` -- API secret key used for authenticating to Falcon Sandbox (defaults to None, optionally read from environment variable "FS_API_SECKEY") | | ScanFooter | Collects file footer | `length` -- number of footer characters to log as metadata (defaults to `50`)
`encodings` -- list of output encodings, any of `classic`, `raw`, `hex`, `backslash` | | ScanGif | Extracts data embedded in GIF files | N/A | -| ScanGzip | Decompresses gzip files | N/A +| ScanGzip | Decompresses gzip files | N/A | ScanHash | Calculates file hash values | N/A | | ScanHeader | Collects file header | `length` -- number of header characters to log as metadata (defaults to `50`)
`encodings` -- list of output encodings, any of `classic`, `raw`, `hex`, `backslash` | | ScanHtml | Collects metadata and extracts embedded files from HTML files | `parser` -- sets the HTML parser used during scanning (defaults to `html.parser`)
`max_links` -- Maximum amount of links to output in hyperlinks field (defaults to `50`) | @@ -816,7 +816,7 @@ The table below describes each scanner and its options. Each scanner has the hid | ScanPkcs7 | Extracts files from PKCS7 certificate files | N/A | | ScanPlist | Collects attributes from binary and XML property list files | `keys` -- list of keys to log (defaults to `all`) | | ScanQr | Collects QR code metadata from image files | `support_inverted` -- Enable/disable image inversion to support inverted QR codes (white on black). Adds some image processing overhead. | [Aaron Herman](https://github.com/aaronherman) -| ScanRar | Extracts files from RAR archives | `limit` -- maximum number of files to extract (defaults to `1000`)
`password_file` -- location of passwords file for RAR archives (defaults to `/etc/strelka/passwords.dat`) | +| ScanRar | Extracts files from RAR archives | `limit` -- maximum number of files to extract (defaults to `1000`)
`limit_metadata` -- stop adding file metadata when `limit` is reached (defaults to true)
`size_limit` -- maximum size for extracted files (defaults to `250000000`)
`crack_pws` -- use a dictionary to crack encrypted files (defaults to false)
`log_pws` -- log cracked passwords (defaults to true)
`password_file` -- location of passwords file for rar archives (defaults to `/etc/strelka/passwords.dat`) | | ScanRpm | Collects metadata and extracts files from RPM files | `tempfile_directory` -- location where `tempfile` will write temporary files (defaults to `/tmp/`) | | ScanRtf | Extracts embedded files from RTF files | `limit` -- maximum number of files to extract (defaults to `1000`) | | ScanSave | Exposes raw file data in the output response in an encoded and compressed format | `compression` -- compression algorithm to use on the raw file data (defaults to `gzip` - `bzip2`, `lzma`, and `none` are available)
`encoding` -- JSON compatible encoding algorithm to use on the raw file data (defaults to `base64` - `base85` also available) | [Kevin Eiche](https://github.com/keiche) @@ -839,7 +839,7 @@ The table below describes each scanner and its options. Each scanner has the hid | ScanXml | Log metadata and extract files from XML files | `extract_tags` -- list of XML tags that will have their text extracted as child files (defaults to empty list)
`metadata_tags` -- list of XML tags that will have their text logged as metadata (defaults to empty list) | | ScanYara | Scans files with YARA rules | `location` -- location of the YARA rules file or directory (defaults to `/etc/strelka/yara/`)
`compiled` -- Enable use of compiled YARA rules, as well as the path.
`store_offset` -- Stores file offset for YARA match
`offset_meta_key` -- YARA meta key that must exist in the YARA rule for the offset to be stored.
`offset_padding` -- Amount of data to be stored before and after offset for additional context.
`category_key` -- Metadata key used to extract categories for YARA matches.
`categories` -- List of categories to organize YARA rules, which can be individually toggled to show metadata.
`show_meta` -- Toggles whether to show metadata for matches in each category.
`meta_fields` -- Specifies which metadata fields should be extracted for display.
`show_all_meta` -- Displays all metadata for each YARA rule match when enabled. | | ScanZip | Extracts files from zip archives | `limit` -- maximum number of files to extract (defaults to `1000`)
`limit_metadata` -- stop adding file metadata when `limit` is reached (defaults to true)
`size_limit` -- maximum size for extracted files (defaults to `250000000`)
`crack_pws` -- use a dictionary to crack encrypted files (defaults to false)
`log_pws` -- log cracked passwords (defaults to true)
`password_file` -- location of passwords file for zip archives (defaults to `/etc/strelka/passwords.dat`) | -| ScanZlib | Decompresses gzip files | N/A +| ScanZlib | Decompresses gzip files | N/A ## Tests As Strelka consists of many scanners and dependencies for those scanners. Pytests are particularly valuable for testing the ongoing functionality of Strelka and it's scanners. Tests allow users to write test cases that verify the correct behavior of Strelka scanners to ensure that the scanners remain reliable and accurate. Additionally, using pytests can help streamline the development process, allowing developers to focus on writing new features and improvements for the scanners. Strelka contains a set of standard test fixture files that represent the types of files Strelka ingests. Test fixtures can also be loaded remotely with the helper functions `get_remote_fixture` and `get_remote_fixture_archive` for scanner tests that need malicious samples. diff --git a/src/python/strelka/scanners/scan_rar.py b/src/python/strelka/scanners/scan_rar.py index 51717e5f..245e99ee 100644 --- a/src/python/strelka/scanners/scan_rar.py +++ b/src/python/strelka/scanners/scan_rar.py @@ -35,103 +35,186 @@ def init(self): self.passwords = [] def scan(self, data, file, options, expire_at): - file_limit = options.get("limit", 1000) + file_limit = options.get("limit", 100) + size_limit = options.get("size_limit", 250000000) + limit_metadata = options.get("limit_metadata", True) + crack_pws = options.get("crack_pws", False) + log_pws = options.get("log_pws", True) password_file = options.get("password_file", "/etc/strelka/passwords.dat") + # Gather count and list of files to be extracted self.event["total"] = {"files": 0, "extracted": 0} - - if not self.passwords: - if os.path.isfile(password_file): - with open(password_file, "rb") as f: - for line in f: - self.passwords.append(line.strip()) + self.event["files"] = [] + + # Temporary top level compression metrics + compress_size_total = 0 + file_size_total = 0 + + if crack_pws: + if not self.passwords: + if os.path.isfile(password_file): + with open(password_file, "rb") as f: + for line in f: + self.passwords.append(line.strip()) + + if ( + len(self.passwords) == 0 + and "no_passwords_loaded" not in self.flags + ): + self.flags.append("no_passwords_loaded") + else: + if "password_file_missing" not in self.flags: + self.flags.append("password_file_missing") + + self.passwords.insert(0, None) with io.BytesIO(data) as rar_io: try: with rarfile.RarFile(rar_io) as rar_obj: - rf_info_list = rar_obj.infolist() - for info in rf_info_list: - if info.is_file(): - self.event["total"]["files"] += 1 + filelist = rar_obj.infolist() + + # Count the file entries, in case the function encounters an unhandled exception + for compressed_file in filelist: + if compressed_file.is_dir(): + continue + self.event["total"]["files"] += 1 - password = "" - for i, name in enumerate(rf_info_list): + # For each file in rar, gather metadata and pass extracted file back to Strelka + for i, name in enumerate(filelist): if not name.isdir(): - if self.event["total"]["extracted"] >= file_limit: - break + + extract = True + extracted = False + compression_rate = 0 try: extract_data = b"" - file_info = rar_obj.getinfo(name) + compressed_file = rar_obj.getinfo(name) + + if compressed_file.file_size > size_limit: + extract = False + if "file_size_limit" not in self.flags: + self.flags.append("file_size_limit") + + if self.event["total"]["extracted"] >= file_limit: + extract = False + if "file_count_limit" not in self.flags: + self.flags.append("file_count_limit") + + if ( + compressed_file.file_size > 0 + and compressed_file.compress_size > 0 + ): + compress_size_total += compressed_file.compress_size + file_size_total += compressed_file.file_size + + size_difference = ( + compressed_file.file_size + - compressed_file.compress_size + ) + compression_rate = ( + size_difference * 100.0 + ) / compressed_file.file_size + self.event["host_os"] = HOST_OS_MAPPING[ - file_info.host_os + compressed_file.host_os ] - if not file_info.needs_password(): + if not compressed_file.needs_password(): extract_data = rar_obj.read(name) else: - if i == 0: + if "password_protected" not in self.flags: self.flags.append("password_protected") - if not password and i == 0: - for pw in self.passwords: - try: - data = rar_obj.open( - name, - mode="r", - psw=pw.decode("utf-8"), - ) - if data.readable(): - extract_data = data.readall() - password = pw.decode("utf-8") - self.event["password"] = pw.decode( - "utf-8" - ) - break - except ( - RuntimeError, - rarfile.BadRarFile, - rarfile.RarCRCError, - rarfile.RarWrongPassword, - ): - pass - elif not password and i > 0: - break - else: + for password in self.passwords: try: data = rar_obj.open( - name, mode="r", psw=password + name, + mode="r", + pwd=password.decode("utf-8") + if password + else None, ) if data.readable(): extract_data = data.readall() + self.passwords.insert( + 0, + self.passwords.pop( + self.passwords.index(password) + ), + ) + if password and log_pws: + self.event[ + "password" + ] = password.decode("utf-8") + break except ( RuntimeError, - rarfile.BadRarFile, rarfile.RarCRCError, rarfile.RarWrongPassword, + ): + raise + except ( + rarfile.BadRarFile, + rarfile.PasswordRequired, ): pass if ( not extract_data and "no_password_match_found" not in self.flags + and not crack_pws ): self.flags.append("no_password_match_found") - if extract_data: + # If there's data in it, and no limits have been met, emit the file + if extract_data and extract: # Send extracted file back to Strelka self.emit_file( - extract_data, name=f"{file_info.filename}" + extract_data, name=f"{compressed_file.filename}" ) + extracted = True + + if not ( + limit_metadata + and self.event["total"]["extracted"] >= file_limit + ): + self.event["files"].append( + { + "file_name": compressed_file.filename, + "file_size": compressed_file.file_size, + "compression_size": compressed_file.compress_size, + "compression_rate": round( + compression_rate, 2 + ), + "extracted": extracted, + "encrypted": compressed_file.needs_password(), + } + ) + + if extracted: self.event["total"]["extracted"] += 1 except NotImplementedError: self.flags.append("unsupport_compression") + raise except RuntimeError: self.flags.append("runtime_error") + raise except ValueError: self.flags.append("value_error") + raise except rarfile.BadRarFile: + raise self.flags.append("bad_rar") + + # Top level compression metric + if file_size_total > 0 and compress_size_total > 0: + size_difference_total = file_size_total - compress_size_total + self.event["compression_rate"] = round( + (size_difference_total * 100.0) / file_size_total, 2 + ) + else: + self.event["compression_rate"] = 0.00 diff --git a/src/python/strelka/scanners/scan_zip.py b/src/python/strelka/scanners/scan_zip.py index a5297a0d..d1e3c44c 100644 --- a/src/python/strelka/scanners/scan_zip.py +++ b/src/python/strelka/scanners/scan_zip.py @@ -20,6 +20,9 @@ class ScanZip(strelka.Scanner): Defaults to /etc/strelka/passwords.dat. """ + def init(self): + self.passwords = [] + def scan(self, data, file, options, expire_at): file_limit = options.get("limit", 100) size_limit = options.get("size_limit", 250000000) @@ -28,8 +31,6 @@ def scan(self, data, file, options, expire_at): log_pws = options.get("log_pws", True) password_file = options.get("password_file", "/etc/strelka/passwords.dat") - passwords = [None] - # Gather count and list of files to be extracted self.event["total"] = {"files": 0, "extracted": 0} self.event["files"] = [] @@ -38,10 +39,23 @@ def scan(self, data, file, options, expire_at): compress_size_total = 0 file_size_total = 0 - if crack_pws and os.path.isfile(password_file): - with open(password_file, "rb") as f: - for line in f: - passwords.append(line.strip()) + if crack_pws: + if not self.passwords: + if os.path.isfile(password_file): + with open(password_file, "rb") as f: + for line in f: + self.passwords.append(line.strip()) + + if ( + len(self.passwords) == 0 + and "no_passwords_loaded" not in self.flags + ): + self.flags.append("no_passwords_loaded") + else: + if "password_file_missing" not in self.flags: + self.flags.append("password_file_missing") + + self.passwords.insert(0, None) with io.BytesIO(data) as zip_io: try: @@ -96,17 +110,17 @@ def scan(self, data, file, options, expire_at): if "encrypted" not in self.flags: self.flags.append("encrypted") - for password in passwords: + for password in self.passwords: try: if extract: extract_data = zip_obj.read( compressed_file.filename, password ) if extract_data: - passwords.insert( + self.passwords.insert( 0, - passwords.pop( - passwords.index(password) + self.passwords.pop( + self.passwords.index(password) ), ) if password and crack_pws and log_pws: diff --git a/src/python/strelka/tests/fixtures/test_big.rar b/src/python/strelka/tests/fixtures/test_big.rar new file mode 100755 index 0000000000000000000000000000000000000000..d6aeeae5ee2a6face12c377cd4fd969900243494 GIT binary patch literal 20848 zcmeI4zbk}s9LGQ3=W)~>P6kT(wOABMS%kr2;|#Kik}T*bW$`r`B;`LiWsm`@#ia~L z-KH#_GRVYWxPd$G^A9NLvAnLQ&t2bcdiQ>R-mm5PPR?dKTWXv&o)yXsE>UYK=XMXa zeC1<@L3d+c5T-73I01BYM|0rO4)7wc`ZZBPRypB}RG+jZ}@EcvrUOzcR5ZS;mLnIYQ2b;mm2W&;Q zqHMrcBnSlIuYv%I0$GvF1Y`kOUOu2(=oZ-k$RY>?fgr@$2%sn^8p&{=C@AXVTs%}g zRWHB6yaa(D5Ckazih`n%d;p4qqFz3r7%4{C074`P1c4yL*$AL0C>qIdpeQKn<6Jyc zJykEi!Mp^4AP@v80E&X5k$eD(f}&nNpcpAe*#JT$2n2y3#Mubf!^hRUK~DpNqM)dc zbMa91RQ*p?U-Ka(ZhzB-E}Qvu(di1prJK{K`mj20rpN1A!pyk4czPbRV$Kcx?o!0o K^{GslsIo5~q87CP literal 0 HcmV?d00001 diff --git a/src/python/strelka/tests/fixtures/test_password.rar b/src/python/strelka/tests/fixtures/test_password.rar new file mode 100755 index 0000000000000000000000000000000000000000..ba7daf75afbb2146730292c3d53ffa44bec2ef08 GIT binary patch literal 4832 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 literal 0 HcmV?d00001 diff --git a/src/python/strelka/tests/test_scan_rar.py b/src/python/strelka/tests/test_scan_rar.py index 1be94e30..fb5949b6 100644 --- a/src/python/strelka/tests/test_scan_rar.py +++ b/src/python/strelka/tests/test_scan_rar.py @@ -16,6 +16,33 @@ def test_scan_rar(mocker): "flags": [], "total": {"files": 3, "extracted": 3}, "host_os": "RAR_OS_WIN32", + "files": [ + { + "file_name": "hidden/lorem-hidden.txt", + "file_size": 4015, + "compression_size": 1484, + "compression_rate": 63.04, + "extracted": True, + "encrypted": False, + }, + { + "file_name": "hidden/lorem-readonly.txt", + "file_size": 4015, + "compression_size": 1484, + "compression_rate": 63.04, + "extracted": True, + "encrypted": False, + }, + { + "file_name": "lorem.txt", + "file_size": 4015, + "compression_size": 1484, + "compression_rate": 63.04, + "extracted": True, + "encrypted": False, + }, + ], + "compression_rate": 63.04, } scanner_event = run_test_scan( @@ -26,3 +53,494 @@ def test_scan_rar(mocker): TestCase.maxDiff = None TestCase().assertDictEqual(test_scan_event, scanner_event) + + +def test_scan_rar_file_limit(mocker): + """ + Pass: Sample event matches output of scanner. + Failure: Unable to load file or sample event fails to match. + """ + + test_scan_event = { + "elapsed": mock.ANY, + "flags": ["file_count_limit"], + "total": {"files": 3, "extracted": 1}, + "host_os": "RAR_OS_WIN32", + "files": [ + { + "file_name": "hidden/lorem-hidden.txt", + "file_size": 4015, + "compression_size": 1484, + "compression_rate": 63.04, + "extracted": True, + "encrypted": False, + }, + { + "file_name": "hidden/lorem-readonly.txt", + "file_size": 4015, + "compression_size": 1484, + "compression_rate": 63.04, + "extracted": False, + "encrypted": False, + }, + { + "file_name": "lorem.txt", + "file_size": 4015, + "compression_size": 1484, + "compression_rate": 63.04, + "extracted": False, + "encrypted": False, + }, + ], + "compression_rate": 63.04, + } + + scanner_event = run_test_scan( + mocker=mocker, + scan_class=ScanUnderTest, + fixture_path=Path(__file__).parent / "fixtures/test.rar", + options={ + "limit": 1, + "limit_metadata": False, + "size_limit": 250000000, + "crack_pws": True, + "log_pws": True, + "password_file": "/etc/strelka/passwords.dat", + }, + ) + + TestCase.maxDiff = None + TestCase().assertDictEqual(test_scan_event, scanner_event) + + +def test_scan_rar_file_limit_no_meta(mocker): + """ + Pass: Sample event matches output of scanner. + Failure: Unable to load file or sample event fails to match. + """ + + test_scan_event = { + "elapsed": mock.ANY, + "flags": ["file_count_limit"], + "total": {"files": 3, "extracted": 1}, + "host_os": "RAR_OS_WIN32", + "files": [ + { + "file_name": "hidden/lorem-hidden.txt", + "file_size": 4015, + "compression_size": 1484, + "compression_rate": 63.04, + "extracted": True, + "encrypted": False, + }, + ], + "compression_rate": 63.04, + } + + scanner_event = run_test_scan( + mocker=mocker, + scan_class=ScanUnderTest, + fixture_path=Path(__file__).parent / "fixtures/test.rar", + options={ + "limit": 1, + "limit_metadata": True, + "size_limit": 250000000, + "crack_pws": True, + "log_pws": True, + "password_file": "/etc/strelka/passwords.dat", + }, + ) + + TestCase.maxDiff = None + TestCase().assertDictEqual(test_scan_event, scanner_event) + + +def test_scan_rar_crack_pws_unencrypted(mocker): + """ + Pass: Sample event matches output of scanner. + Failure: Unable to load file or sample event fails to match. + """ + + test_scan_event = { + "elapsed": mock.ANY, + "flags": [], + "total": {"files": 3, "extracted": 3}, + "host_os": "RAR_OS_WIN32", + "files": [ + { + "file_name": "hidden/lorem-hidden.txt", + "file_size": 4015, + "compression_size": 1484, + "compression_rate": 63.04, + "extracted": True, + "encrypted": False, + }, + { + "file_name": "hidden/lorem-readonly.txt", + "file_size": 4015, + "compression_size": 1484, + "compression_rate": 63.04, + "extracted": True, + "encrypted": False, + }, + { + "file_name": "lorem.txt", + "file_size": 4015, + "compression_size": 1484, + "compression_rate": 63.04, + "extracted": True, + "encrypted": False, + }, + ], + "compression_rate": 63.04, + } + + scanner_event = run_test_scan( + mocker=mocker, + scan_class=ScanUnderTest, + fixture_path=Path(__file__).parent / "fixtures/test.rar", + options={ + "limit": 1000, + "limit_metadata": True, + "size_limit": 250000000, + "crack_pws": True, + "log_pws": True, + "password_file": "/etc/strelka/passwords.dat", + }, + ) + + TestCase.maxDiff = None + TestCase().assertDictEqual(test_scan_event, scanner_event) + + +def test_scan_rar_password(mocker): + """ + Pass: Sample event matches output of scanner. + Failure: Unable to load file or sample event fails to match. + """ + + test_scan_event = { + "elapsed": mock.ANY, + "flags": ["password_protected"], + "total": {"files": 3, "extracted": 3}, + "host_os": "RAR_OS_WIN32", + "files": [ + { + "file_name": "hidden/lorem-hidden.txt", + "file_size": 4015, + "compression_size": 1488, + "compression_rate": 62.94, + "extracted": True, + "encrypted": True, + }, + { + "file_name": "hidden/lorem-readonly.txt", + "file_size": 4015, + "compression_size": 1488, + "compression_rate": 62.94, + "extracted": True, + "encrypted": True, + }, + { + "file_name": "lorem.txt", + "file_size": 4015, + "compression_size": 1488, + "compression_rate": 62.94, + "extracted": True, + "encrypted": True, + }, + ], + "compression_rate": 62.94, + } + + scanner_event = run_test_scan( + mocker=mocker, + scan_class=ScanUnderTest, + fixture_path=Path(__file__).parent / "fixtures/test_password.rar", + options={ + "limit": 1000, + "limit_metadata": True, + "size_limit": 250000000, + "crack_pws": True, + "log_pws": False, + "password_file": "/etc/strelka/passwords.dat", + }, + ) + + TestCase.maxDiff = None + TestCase().assertDictEqual(test_scan_event, scanner_event) + + +def test_scan_rar_password_log_pwd(mocker): + """ + Pass: Sample event matches output of scanner. + Failure: Unable to load file or sample event fails to match. + """ + + test_scan_event = { + "elapsed": mock.ANY, + "flags": ["password_protected"], + "password": "password", + "total": {"files": 3, "extracted": 3}, + "host_os": "RAR_OS_WIN32", + "files": [ + { + "file_name": "hidden/lorem-hidden.txt", + "file_size": 4015, + "compression_size": 1488, + "compression_rate": 62.94, + "extracted": True, + "encrypted": True, + }, + { + "file_name": "hidden/lorem-readonly.txt", + "file_size": 4015, + "compression_size": 1488, + "compression_rate": 62.94, + "extracted": True, + "encrypted": True, + }, + { + "file_name": "lorem.txt", + "file_size": 4015, + "compression_size": 1488, + "compression_rate": 62.94, + "extracted": True, + "encrypted": True, + }, + ], + "compression_rate": 62.94, + } + + scanner_event = run_test_scan( + mocker=mocker, + scan_class=ScanUnderTest, + fixture_path=Path(__file__).parent / "fixtures/test_password.rar", + options={ + "limit": 1000, + "limit_metadata": True, + "size_limit": 250000000, + "crack_pws": True, + "log_pws": True, + "password_file": "/etc/strelka/passwords.dat", + }, + ) + + TestCase.maxDiff = None + TestCase().assertDictEqual(test_scan_event, scanner_event) + + +def test_scan_rar_password_crack_pws(mocker): + """ + Pass: Sample event matches output of scanner. + Failure: Unable to load file or sample event fails to match. + """ + + test_scan_event = { + "elapsed": mock.ANY, + "flags": ["password_protected", "no_password_match_found"], + "total": {"files": 3, "extracted": 0}, + "host_os": "RAR_OS_WIN32", + "files": [ + { + "file_name": "hidden/lorem-hidden.txt", + "file_size": 4015, + "compression_size": 1488, + "compression_rate": 62.94, + "extracted": False, + "encrypted": True, + }, + { + "file_name": "hidden/lorem-readonly.txt", + "file_size": 4015, + "compression_size": 1488, + "compression_rate": 62.94, + "extracted": False, + "encrypted": True, + }, + { + "file_name": "lorem.txt", + "file_size": 4015, + "compression_size": 1488, + "compression_rate": 62.94, + "extracted": False, + "encrypted": True, + }, + ], + "compression_rate": 62.94, + } + + scanner_event = run_test_scan( + mocker=mocker, + scan_class=ScanUnderTest, + fixture_path=Path(__file__).parent / "fixtures/test_password.rar", + options={ + "limit": 1000, + "limit_metadata": True, + "size_limit": 250000000, + "crack_pws": False, + "log_pws": True, + "password_file": "/etc/strelka/passwords.dat", + }, + ) + + TestCase.maxDiff = None + TestCase().assertDictEqual(test_scan_event, scanner_event) + + +def test_scan_rar_password_bad_path(mocker): + """ + Pass: Sample event matches output of scanner. + Failure: Unable to load file or sample event fails to match. + """ + + test_scan_event = { + "elapsed": mock.ANY, + "flags": ["password_file_missing", "password_protected"], + "total": {"files": 3, "extracted": 0}, + "host_os": "RAR_OS_WIN32", + "files": [ + { + "file_name": "hidden/lorem-hidden.txt", + "file_size": 4015, + "compression_size": 1488, + "compression_rate": 62.94, + "extracted": False, + "encrypted": True, + }, + { + "file_name": "hidden/lorem-readonly.txt", + "file_size": 4015, + "compression_size": 1488, + "compression_rate": 62.94, + "extracted": False, + "encrypted": True, + }, + { + "file_name": "lorem.txt", + "file_size": 4015, + "compression_size": 1488, + "compression_rate": 62.94, + "extracted": False, + "encrypted": True, + }, + ], + "compression_rate": 62.94, + } + + scanner_event = run_test_scan( + mocker=mocker, + scan_class=ScanUnderTest, + fixture_path=Path(__file__).parent / "fixtures/test_password.rar", + options={ + "limit": 1000, + "limit_metadata": True, + "size_limit": 250000000, + "crack_pws": True, + "log_pws": True, + "password_file": "/etc/strelka/nosuchfile", + }, + ) + + TestCase.maxDiff = None + TestCase().assertDictEqual(test_scan_event, scanner_event) + + +def test_scan_rar_password_empty_file(mocker): + """ + Pass: Sample event matches output of scanner. + Failure: Unable to load file or sample event fails to match. + """ + + test_scan_event = { + "elapsed": mock.ANY, + "flags": ["no_passwords_loaded", "password_protected"], + "total": {"files": 3, "extracted": 0}, + "host_os": "RAR_OS_WIN32", + "files": [ + { + "file_name": "hidden/lorem-hidden.txt", + "file_size": 4015, + "compression_size": 1488, + "compression_rate": 62.94, + "extracted": False, + "encrypted": True, + }, + { + "file_name": "hidden/lorem-readonly.txt", + "file_size": 4015, + "compression_size": 1488, + "compression_rate": 62.94, + "extracted": False, + "encrypted": True, + }, + { + "file_name": "lorem.txt", + "file_size": 4015, + "compression_size": 1488, + "compression_rate": 62.94, + "extracted": False, + "encrypted": True, + }, + ], + "compression_rate": 62.94, + } + + scanner_event = run_test_scan( + mocker=mocker, + scan_class=ScanUnderTest, + fixture_path=Path(__file__).parent / "fixtures/test_password.rar", + options={ + "limit": 1000, + "limit_metadata": True, + "size_limit": 250000000, + "crack_pws": True, + "log_pws": True, + "password_file": Path(__file__).parent / "fixtures/test.empty", + }, + ) + + TestCase.maxDiff = None + TestCase().assertDictEqual(test_scan_event, scanner_event) + + +def test_scan_rar_big(mocker): + """ + Pass: Sample event matches output of scanner. + Failure: Unable to load file or sample event fails to match. + """ + + test_scan_event = { + "elapsed": mock.ANY, + "flags": ["file_size_limit"], + "total": {"files": 1, "extracted": 0}, + "host_os": "RAR_OS_WIN32", + "files": [ + { + "file_name": "test_big.zero", + "file_size": 512000000, + "compression_size": 20674, + "compression_rate": 100.0, + "extracted": False, + "encrypted": False, + }, + ], + "compression_rate": 100.0, + } + + scanner_event = run_test_scan( + mocker=mocker, + scan_class=ScanUnderTest, + fixture_path=Path(__file__).parent / "fixtures/test_big.rar", + options={ + "limit": 1000, + "limit_metadata": True, + "size_limit": 511999999, + "crack_pws": True, + "log_pws": True, + "password_file": "/etc/strelka/passwords.dat", + }, + ) + + TestCase.maxDiff = None + TestCase().assertDictEqual(test_scan_event, scanner_event) From eac7c8e2d546932f4a739d4561a195e4890450e1 Mon Sep 17 00:00:00 2001 From: Paul Hutelmyer Date: Thu, 7 Mar 2024 10:45:09 -0500 Subject: [PATCH 2/8] Formatting --- src/python/strelka/scanners/scan_rar.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/python/strelka/scanners/scan_rar.py b/src/python/strelka/scanners/scan_rar.py index 245e99ee..8accb3b1 100644 --- a/src/python/strelka/scanners/scan_rar.py +++ b/src/python/strelka/scanners/scan_rar.py @@ -131,9 +131,11 @@ def scan(self, data, file, options, expire_at): data = rar_obj.open( name, mode="r", - pwd=password.decode("utf-8") - if password - else None, + pwd=( + password.decode("utf-8") + if password + else None + ), ) if data.readable(): extract_data = data.readall() @@ -144,9 +146,9 @@ def scan(self, data, file, options, expire_at): ), ) if password and log_pws: - self.event[ - "password" - ] = password.decode("utf-8") + self.event["password"] = ( + password.decode("utf-8") + ) break except ( RuntimeError, From 2bcc03292670155eee5385479ecae8e79d298c6e Mon Sep 17 00:00:00 2001 From: Ryan O'Horo <10855297+ryanohoro@users.noreply.github.com> Date: Thu, 7 Mar 2024 20:28:11 -0600 Subject: [PATCH 3/8] New rar test fixtures --- .../strelka/tests/fixtures/test_comment_time.rar | Bin 0 -> 1648 bytes src/python/strelka/tests/fixtures/test_mixed.rar | Bin 0 -> 6359 bytes .../tests/fixtures/test_mixed_filenames.rar | Bin 0 -> 4948 bytes 3 files changed, 0 insertions(+), 0 deletions(-) create mode 100755 src/python/strelka/tests/fixtures/test_comment_time.rar create mode 100755 src/python/strelka/tests/fixtures/test_mixed.rar create mode 100755 src/python/strelka/tests/fixtures/test_mixed_filenames.rar diff --git a/src/python/strelka/tests/fixtures/test_comment_time.rar b/src/python/strelka/tests/fixtures/test_comment_time.rar new file mode 100755 index 0000000000000000000000000000000000000000..ca18c5b84ec60eb2867cc4f128c6fc922b9d786b GIT binary patch literal 1648 zcmV-$29NntVR9iF2LS-{;ezW60R;yD1_1$pfPeraR7EQj0|K}J1h@bIGAX(#fB*mk zLrqjnZ*paAAZc)Ob!{MIZ)|UJAaiMSAYpA~bSxlaZ*FsCV{~P7b#fqKWNC0|b7N_4 zXCP&4X>=~pJ#o4<0s|Yy3k0tpAP944Z-4^;32bk2Wo<5Wcyt;A4(<;Lq1O%B0i`BL z=lO5i0i`BL=lO5i0nJdt1x8gwG&4UmWlwhy^f=HC7Kbe);im;An<=kwYy)ICv4M;i zzv3*ec4lgT2%~}N23oKxrh7PhGgLI}{uAza^G$W%jdsyy8EI`5U&!^ND>I^-YtQtf zh4*D&49_hMT51sN%Y~P5qP|uw!)BzQdReAc-gyYN;Fk^Y2uZ6;fIScHRQ93a4>%e; zZKsXYfnW#yr9R3%{Dn%yl-bDF1oH>z=6+J@2GBMGEgp~{IpS~2PC$Jf)Qkamc+wix zTR?&8dpD0r7Ci)?sLT~`r!@dtox9!ZuZ?*Fc~A63_>M8-(NhWZ8vw-$C{ZNPliEk4 zzPvE+q<=HUl_YtJ$-Ze`E!NP93wGEBeGkmf=awUgn^p}HM}V{r_iuR%0LUSt4gfAp z=cx7I2$tvfMHT2q*OJAN>ztc#0T^oNoe~*N)s>6C3Yj@ae2?=>hw9rNq8mMeJ_5~N z0gB9qLpJ#k>8$?T>>SKT5<(((x67=LKnUK%(ji0|H%(J?NfEAxSS4T&>UgGj+ElAz zU?HsP>I!r{W5S1q9($QxvtLP|A0z~86g=9^0AkflPSAL-#FmDRZco?ixL6Qam@n>a zIc1T@3CR!0TuEq5EyHY5XMZ&=04v60(NC=DL{Am4fY*mG2Ld2AiTCaT3NW6|YKC^3 z3H!&s`;_8n4+ONG9|Jj!G7-ABfdCsM2l@U-{q$=mR!Zl>zCwOCt9V5lD8MU1EoC{_%f9MQjOVD4BGjibBYo^o(4* z82>ja(0DnwoX<4!Ib~bc@Kp)aRkFy~8e^DBnR@RrS{lg1ce!h z_jvdapm-Ow^0T>Y55^5yfn?qxjUwcA%lV~^Aj+N(v(eemvAduCbL4PB{4Jo|p9P2C-1H3SLJ8Q z3XAvZcYLwO;lZV>d4hANb?doO9AxdaQdnBH<-1!LoXB3!^xBKZoh8mX~ z?>JWnkB-LXjZGoVmU|l*Ep9&-nj5aH0OUU$qh?MC& zcVYLHdWw`Q&j9J%qqx(%jf$l%8+#${jHy02n!;kNh(ElYSyvBC*zYzV5BAy3r#)%S zx|4=)RbnDta=bUeey^nX#+3F)@WmZF<4D|z`o&ecZ-G+7Dk1%zZd(Z-xtA1&Topzs zyg`vKy+3d3ua2T+Wr@c0-cY2_J%Tj#ig41oKqjN*yA!*WVUJ(c6E@SL;a^ESxn}# za^=&J1h-O3f8yIt(O%L&d$li{MIG}yS{cXZZdDlZWDy@AD}g6aD_bqF=BbcUDmY1q1+^Rt`@9 literal 0 HcmV?d00001 diff --git a/src/python/strelka/tests/fixtures/test_mixed.rar b/src/python/strelka/tests/fixtures/test_mixed.rar new file mode 100755 index 0000000000000000000000000000000000000000..c913eec0edac0c0f046ede324e8a79fea8c44103 GIT binary patch literal 6359 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 literal 0 HcmV?d00001 diff --git a/src/python/strelka/tests/fixtures/test_mixed_filenames.rar b/src/python/strelka/tests/fixtures/test_mixed_filenames.rar new file mode 100755 index 0000000000000000000000000000000000000000..dc14957ffb2aba1a6716e256788283df2fd59243 GIT binary patch literal 4948 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 S%h-L6Bn&Jp71$a)$TLU^iE-!v literal 0 HcmV?d00001 From 03cd52c62753b6926a780f9a8f103538eff28833 Mon Sep 17 00:00:00 2001 From: Ryan O'Horo <10855297+ryanohoro@users.noreply.github.com> Date: Fri, 8 Mar 2024 18:27:20 -0600 Subject: [PATCH 4/8] Formatting updates --- src/python/strelka/scanners/scan_docx.py | 18 ++++++++-------- src/python/strelka/scanners/scan_iso.py | 26 ++++++++++++------------ src/python/strelka/scanners/scan_lnk.py | 12 +++++------ src/python/strelka/scanners/scan_pe.py | 24 +++++++++++----------- src/python/strelka/scanners/scan_pgp.py | 24 +++++++++++----------- 5 files changed, 52 insertions(+), 52 deletions(-) diff --git a/src/python/strelka/scanners/scan_docx.py b/src/python/strelka/scanners/scan_docx.py index ae1baabd..738806d2 100644 --- a/src/python/strelka/scanners/scan_docx.py +++ b/src/python/strelka/scanners/scan_docx.py @@ -30,17 +30,17 @@ def scan(self, data, file, options, expire_at): self.event["identifier"] = docx_doc.core_properties.identifier self.event["keywords"] = docx_doc.core_properties.keywords self.event["language"] = docx_doc.core_properties.language - self.event["last_modified_by"] = ( - docx_doc.core_properties.last_modified_by - ) + self.event[ + "last_modified_by" + ] = docx_doc.core_properties.last_modified_by if docx_doc.core_properties.last_printed is not None: - self.event["last_printed"] = ( - docx_doc.core_properties.last_printed.isoformat() - ) + self.event[ + "last_printed" + ] = docx_doc.core_properties.last_printed.isoformat() if docx_doc.core_properties.modified is not None: - self.event["modified"] = ( - docx_doc.core_properties.modified.isoformat() - ) + self.event[ + "modified" + ] = docx_doc.core_properties.modified.isoformat() self.event["revision"] = docx_doc.core_properties.revision self.event["subject"] = docx_doc.core_properties.subject self.event["title"] = docx_doc.core_properties.title diff --git a/src/python/strelka/scanners/scan_iso.py b/src/python/strelka/scanners/scan_iso.py index b949e5cc..9e825245 100644 --- a/src/python/strelka/scanners/scan_iso.py +++ b/src/python/strelka/scanners/scan_iso.py @@ -27,19 +27,19 @@ def scan(self, data, file, options, expire_at): # Attempt to get Meta try: - self.event["meta"]["date_created"] = ( - self._datetime_from_volume_date(iso.pvd.volume_creation_date) - ) - self.event["meta"]["date_effective"] = ( - self._datetime_from_volume_date(iso.pvd.volume_effective_date) - ) - self.event["meta"]["date_expiration"] = ( - self._datetime_from_volume_date(iso.pvd.volume_expiration_date) - ) - self.event["meta"]["date_modification"] = ( - self._datetime_from_volume_date( - iso.pvd.volume_modification_date - ) + self.event["meta"][ + "date_created" + ] = self._datetime_from_volume_date(iso.pvd.volume_creation_date) + self.event["meta"][ + "date_effective" + ] = self._datetime_from_volume_date(iso.pvd.volume_effective_date) + self.event["meta"][ + "date_expiration" + ] = self._datetime_from_volume_date(iso.pvd.volume_expiration_date) + self.event["meta"][ + "date_modification" + ] = self._datetime_from_volume_date( + iso.pvd.volume_modification_date ) self.event["meta"][ "volume_identifier" diff --git a/src/python/strelka/scanners/scan_lnk.py b/src/python/strelka/scanners/scan_lnk.py index 7759529d..ed405d0d 100644 --- a/src/python/strelka/scanners/scan_lnk.py +++ b/src/python/strelka/scanners/scan_lnk.py @@ -140,18 +140,18 @@ def scan(self, data, file, options, expire_at): try: if extradata.IconEnvironmentDataBlock: - self.event["icon_target"] = ( - extradata.IconEnvironmentDataBlock.TargetAnsi - ) + self.event[ + "icon_target" + ] = extradata.IconEnvironmentDataBlock.TargetAnsi except strelka.ScannerTimeout: raise except Exception: self.flags.append("Unable to parse IconEnvironmentDataBlock") if extradata.TrackerDataBlock: - self.event["machine_id"] = ( - extradata.TrackerDataBlock.MachineID.strip(b"\x00") - ) + self.event[ + "machine_id" + ] = extradata.TrackerDataBlock.MachineID.strip(b"\x00") self.event["mac"] = str( uuid.UUID(bytes_le=extradata.TrackerDataBlock.Droid[16:]) ).split("-")[-1] diff --git a/src/python/strelka/scanners/scan_pe.py b/src/python/strelka/scanners/scan_pe.py index fa8dd84b..32565e5b 100644 --- a/src/python/strelka/scanners/scan_pe.py +++ b/src/python/strelka/scanners/scan_pe.py @@ -532,18 +532,18 @@ def scan(self, data, file, options, expire_at): self.event["address_of_entry_point"] = pe.OPTIONAL_HEADER.AddressOfEntryPoint self.event["image_base"] = pe.OPTIONAL_HEADER.ImageBase self.event["size_of_code"] = pe.OPTIONAL_HEADER.SizeOfCode - self.event["size_of_initialized_data"] = ( - pe.OPTIONAL_HEADER.SizeOfInitializedData - ) + self.event[ + "size_of_initialized_data" + ] = pe.OPTIONAL_HEADER.SizeOfInitializedData self.event["size_of_headers"] = pe.OPTIONAL_HEADER.SizeOfHeaders self.event["size_of_heap_reserve"] = pe.OPTIONAL_HEADER.SizeOfHeapReserve self.event["size_of_image"] = pe.OPTIONAL_HEADER.SizeOfImage self.event["size_of_stack_commit"] = pe.OPTIONAL_HEADER.SizeOfStackCommit self.event["size_of_stack_reserve"] = pe.OPTIONAL_HEADER.SizeOfStackReserve self.event["size_of_heap_commit"] = pe.OPTIONAL_HEADER.SizeOfHeapCommit - self.event["size_of_uninitialized_data"] = ( - pe.OPTIONAL_HEADER.SizeOfUninitializedData - ) + self.event[ + "size_of_uninitialized_data" + ] = pe.OPTIONAL_HEADER.SizeOfUninitializedData self.event["file_alignment"] = pe.OPTIONAL_HEADER.FileAlignment self.event["section_alignment"] = pe.OPTIONAL_HEADER.SectionAlignment self.event["checksum"] = pe.OPTIONAL_HEADER.CheckSum @@ -552,12 +552,12 @@ def scan(self, data, file, options, expire_at): self.event["minor_image_version"] = pe.OPTIONAL_HEADER.MinorImageVersion self.event["major_linker_version"] = pe.OPTIONAL_HEADER.MajorLinkerVersion self.event["minor_linker_version"] = pe.OPTIONAL_HEADER.MinorLinkerVersion - self.event["major_operating_system_version"] = ( - pe.OPTIONAL_HEADER.MajorOperatingSystemVersion - ) - self.event["minor_operating_system_version"] = ( - pe.OPTIONAL_HEADER.MinorOperatingSystemVersion - ) + self.event[ + "major_operating_system_version" + ] = pe.OPTIONAL_HEADER.MajorOperatingSystemVersion + self.event[ + "minor_operating_system_version" + ] = pe.OPTIONAL_HEADER.MinorOperatingSystemVersion self.event["major_subsystem_version"] = pe.OPTIONAL_HEADER.MajorSubsystemVersion self.event["minor_subsystem_version"] = pe.OPTIONAL_HEADER.MinorSubsystemVersion self.event["image_version"] = float( diff --git a/src/python/strelka/scanners/scan_pgp.py b/src/python/strelka/scanners/scan_pgp.py index 7c3259d4..1a9f4e7a 100644 --- a/src/python/strelka/scanners/scan_pgp.py +++ b/src/python/strelka/scanners/scan_pgp.py @@ -74,9 +74,9 @@ def parse_pgpdump(self, data): secret_key_entry["creation_time"] = creation_time.isoformat() expiration_time = getattr(packet, "expiration_time", None) if expiration_time is not None: - secret_key_entry["expiration_time"] = ( - expiration_time.isoformat() - ) + secret_key_entry[ + "expiration_time" + ] = expiration_time.isoformat() if secret_key_entry not in self.event["secret_keys"]: self.event["secret_keys"].append(secret_key_entry) @@ -98,9 +98,9 @@ def parse_pgpdump(self, data): public_key_entry["creation_time"] = creation_time.isoformat() expiration_time = getattr(packet, "expiration_time", None) if expiration_time is not None: - public_key_entry["expiration_time"] = ( - expiration_time.isoformat() - ) + public_key_entry[ + "expiration_time" + ] = expiration_time.isoformat() if public_key_entry not in self.event["public_keys"]: self.event["public_keys"].append(public_key_entry) @@ -135,14 +135,14 @@ def parse_pgpdump(self, data): } creation_time = getattr(packet, "creation_time", None) if creation_time is not None: - signature_packet_entry["creation_time"] = ( - creation_time.isoformat() - ) + signature_packet_entry[ + "creation_time" + ] = creation_time.isoformat() expiration_time = getattr(packet, "expiration_time", None) if expiration_time is not None: - signature_packet_entry["expiration_time"] = ( - expiration_time.isoformat() - ) + signature_packet_entry[ + "expiration_time" + ] = expiration_time.isoformat() if signature_packet_entry not in self.event["signatures"]: self.event["signatures"].append(signature_packet_entry) From 1a0717dccc6cc43ee1e3dfdef2258e988c90b188 Mon Sep 17 00:00:00 2001 From: Ryan O'Horo <10855297+ryanohoro@users.noreply.github.com> Date: Fri, 8 Mar 2024 18:28:55 -0600 Subject: [PATCH 5/8] Let tests use source filename in file.name attribute in scan_wrapper --- src/python/strelka/tests/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/python/strelka/tests/__init__.py b/src/python/strelka/tests/__init__.py index d452ce6b..c861f08f 100644 --- a/src/python/strelka/tests/__init__.py +++ b/src/python/strelka/tests/__init__.py @@ -42,7 +42,7 @@ def run_test_scan( scanner.scan_wrapper( data=data, - file=File(name="test"), + file=File(name=fixture_path if fixture_path else "test"), options=options, expire_at=datetime.date.today(), ) From 274da4d0c968285dd431c8febbaca8de7fd56301 Mon Sep 17 00:00:00 2001 From: Ryan O'Horo <10855297+ryanohoro@users.noreply.github.com> Date: Fri, 8 Mar 2024 18:31:10 -0600 Subject: [PATCH 6/8] Add timestamps and comments to ScanRar, new ScanRar tests, upgrade rarfile to 4.1 --- poetry.lock | 278 +++++++---- pyproject.toml | 3 +- src/python/strelka/scanners/scan_rar.py | 192 +++++-- .../test_password_encrypted_headers.rar | Bin 0 -> 5006 bytes ...test_password_mixed_encrypted_headers.rar} | Bin .../helpers/test_passwords_alternate.dat | 2 + src/python/strelka/tests/test_scan_rar.py | 469 +++++++++++++++++- 7 files changed, 801 insertions(+), 143 deletions(-) create mode 100755 src/python/strelka/tests/fixtures/test_password_encrypted_headers.rar rename src/python/strelka/tests/fixtures/{test_mixed_filenames.rar => test_password_mixed_encrypted_headers.rar} (100%) create mode 100644 src/python/strelka/tests/helpers/test_passwords_alternate.dat diff --git a/poetry.lock b/poetry.lock index 5f10e91e..15a26d2b 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,9 +1,10 @@ -# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand. +# This file is automatically @generated by Poetry and should not be changed by hand. [[package]] name = "aplib" version = "0.6" description = "Module for decompressing aPLib compressed data" +category = "main" optional = false python-versions = ">=2.7" files = [ @@ -15,6 +16,7 @@ files = [ name = "arc4" version = "0.0.4" description = "A small and insanely fast ARCFOUR (RC4) cipher implementation of Python" +category = "main" optional = false python-versions = "*" files = [ @@ -36,6 +38,7 @@ files = [ name = "asn1crypto" version = "1.5.1" description = "Fast ASN.1 parser and serializer with definitions for private keys, public keys, certificates, CRL, OCSP, CMS, PKCS#3, PKCS#7, PKCS#8, PKCS#12, PKCS#5, X.509 and TSP" +category = "main" optional = false python-versions = "*" files = [ @@ -47,6 +50,7 @@ files = [ name = "async-timeout" version = "4.0.3" description = "Timeout context manager for asyncio programs" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -58,6 +62,7 @@ files = [ name = "attrs" version = "23.2.0" description = "Classes Without Boilerplate" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -77,6 +82,7 @@ tests-no-zope = ["attrs[tests-mypy]", "cloudpickle", "hypothesis", "pympler", "p name = "backoff" version = "2.2.1" description = "Function decoration for backoff and retry" +category = "main" optional = false python-versions = ">=3.7,<4.0" files = [ @@ -88,6 +94,7 @@ files = [ name = "beautifulsoup4" version = "4.12.2" description = "Screen-scraping library" +category = "main" optional = false python-versions = ">=3.6.0" files = [ @@ -106,6 +113,7 @@ lxml = ["lxml"] name = "boltons" version = "23.0.0" description = "When they're not builtins, they're boltons." +category = "main" optional = false python-versions = "*" files = [ @@ -117,6 +125,7 @@ files = [ name = "boto3" version = "1.28.60" description = "The AWS SDK for Python" +category = "main" optional = false python-versions = ">= 3.7" files = [ @@ -136,6 +145,7 @@ crt = ["botocore[crt] (>=1.21.0,<2.0a0)"] name = "botocore" version = "1.31.85" description = "Low-level, data-driven core of boto 3." +category = "main" optional = false python-versions = ">= 3.7" files = [ @@ -155,6 +165,7 @@ crt = ["awscrt (==0.19.12)"] name = "brotli" version = "1.1.0" description = "Python bindings for the Brotli compression library" +category = "main" optional = false python-versions = "*" files = [ @@ -247,6 +258,7 @@ files = [ name = "brotlicffi" version = "1.1.0.0" description = "Python CFFI bindings to the Brotli library" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -286,6 +298,7 @@ cffi = ">=1.0.0" name = "capstone" version = "5.0.1" description = "Capstone disassembly engine" +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" files = [ @@ -303,6 +316,7 @@ files = [ name = "certifi" version = "2024.2.2" description = "Python package for providing Mozilla's CA Bundle." +category = "main" optional = false python-versions = ">=3.6" files = [ @@ -314,6 +328,7 @@ files = [ name = "certvalidator" version = "0.11.1" description = "Validates X.509 certificates and paths" +category = "main" optional = false python-versions = "*" files = [ @@ -329,6 +344,7 @@ oscrypto = ">=0.16.1" name = "cffi" version = "1.16.0" description = "Foreign Function Interface for Python calling C code." +category = "main" optional = false python-versions = ">=3.8" files = [ @@ -393,6 +409,7 @@ pycparser = "*" name = "charset-normalizer" version = "3.3.2" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +category = "main" optional = false python-versions = ">=3.7.0" files = [ @@ -492,6 +509,7 @@ files = [ name = "chaskey" version = "0.0.1" description = "Pure Python Chaskey LTS implementation" +category = "main" optional = false python-versions = ">=3.6" files = [] @@ -507,6 +525,7 @@ resolved_reference = "2fd80f732dd9422a9e92556758180ceee3b5b4ec" name = "colorama" version = "0.4.6" description = "Cross-platform colored terminal text." +category = "main" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" files = [ @@ -518,6 +537,7 @@ files = [ name = "colorclass" version = "2.2.2" description = "Colorful worry-free console applications for Linux, Mac OS X, and Windows." +category = "main" optional = false python-versions = ">=2.6" files = [ @@ -529,6 +549,7 @@ files = [ name = "construct" version = "2.10.68" description = "A powerful declarative symmetric parser/builder for binary data" +category = "main" optional = false python-versions = ">=3.6" files = [ @@ -542,6 +563,7 @@ extras = ["arrow", "cloudpickle", "enum34", "lz4", "numpy", "ruamel.yaml"] name = "cryptography" version = "42.0.5" description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -596,6 +618,7 @@ test-randomorder = ["pytest-randomly"] name = "cssselect2" version = "0.7.0" description = "CSS selectors for Python ElementTree" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -615,6 +638,7 @@ test = ["flake8", "isort", "pytest"] name = "decorator" version = "5.1.1" description = "Decorators for Humans" +category = "main" optional = false python-versions = ">=3.5" files = [ @@ -626,6 +650,7 @@ files = [ name = "deprecated" version = "1.2.14" description = "Python @deprecated decorator to deprecate old python classes, functions or methods." +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" files = [ @@ -643,6 +668,7 @@ dev = ["PyTest", "PyTest-Cov", "bump2version (<1)", "sphinx (<2)", "tox"] name = "dncil" version = "1.0.2" description = "The FLARE team's open-source library to disassemble Common Intermediate Language (CIL) instructions." +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -657,6 +683,7 @@ dev = ["black (==22.12.0)", "dnfile (==0.12.0)", "hexdump (==3.3.0)", "isort (== name = "dnfile" version = "0.14.1" description = "Parse .NET executable files." +category = "main" optional = false python-versions = ">=3.8" files = [ @@ -671,6 +698,7 @@ pefile = ">=2019.4.18" name = "docker" version = "6.1.3" description = "A Python library for the Docker Engine API." +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -692,6 +720,7 @@ ssh = ["paramiko (>=2.4.3)"] name = "donut-decryptor" version = "0.0.1" description = "Decryptor for files containing Donut shellcode" +category = "main" optional = false python-versions = ">=3.7" files = [] @@ -713,6 +742,7 @@ resolved_reference = "5a0f9f87094efe6e8d131d3fa4abf23f31123784" name = "dotnetfile" version = "0.2.4" description = "Library to parse the CLR header of .NET assemblies" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -727,6 +757,7 @@ pefile = "*" name = "easygui" version = "0.98.3" description = "EasyGUI is a module for very simple, very easy GUI programming in Python. EasyGUI is different from other GUI generators in that EasyGUI is NOT event-driven. Instead, all GUI interactions are invoked by simple function calls." +category = "main" optional = false python-versions = "*" files = [ @@ -738,6 +769,7 @@ files = [ name = "editorconfig" version = "0.12.4" description = "EditorConfig File Locator and Interpreter for Python" +category = "main" optional = false python-versions = "*" files = [ @@ -748,6 +780,7 @@ files = [ name = "eml-parser" version = "1.17.5" description = "Python EML parser library" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -772,6 +805,7 @@ test = ["coverage", "pytest"] name = "entropy" version = "0.11" description = "extension module for calculating entropy fast" +category = "main" optional = false python-versions = "*" files = [] @@ -787,6 +821,7 @@ resolved_reference = "a49f1addccb88d54115832a6d1982b957a56f329" name = "esprima" version = "4.0.1" description = "ECMAScript parsing infrastructure for multipurpose analysis in Python" +category = "main" optional = false python-versions = "*" files = [ @@ -797,6 +832,7 @@ files = [ name = "et-xmlfile" version = "1.1.0" description = "An implementation of lxml.xmlfile for the standard library" +category = "main" optional = false python-versions = ">=3.6" files = [ @@ -808,6 +844,7 @@ files = [ name = "exceptiongroup" version = "1.2.0" description = "Backport of PEP 654 (exception groups)" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -822,6 +859,7 @@ test = ["pytest (>=6)"] name = "filelock" version = "3.13.1" description = "A platform independent file lock." +category = "main" optional = false python-versions = ">=3.8" files = [ @@ -838,6 +876,7 @@ typing = ["typing-extensions (>=4.8)"] name = "fonttools" version = "4.49.0" description = "Tools to manipulate font files" +category = "main" optional = false python-versions = ">=3.8" files = [ @@ -908,6 +947,7 @@ woff = ["brotli (>=1.0.1)", "brotlicffi (>=0.8.0)", "zopfli (>=0.1.4)"] name = "formulas" version = "1.2.6" description = "Parse and compile Excel formulas and workbooks in python code." +category = "main" optional = false python-versions = "*" files = [ @@ -932,6 +972,7 @@ plot = ["Pygments", "docutils", "flask", "graphviz", "jinja2", "regex"] name = "googleapis-common-protos" version = "1.56.1" description = "Common protobufs used in Google APIs" +category = "main" optional = false python-versions = ">=3.6" files = [ @@ -949,6 +990,7 @@ grpc = ["grpcio (>=1.0.0)"] name = "grpcio" version = "1.62.0" description = "HTTP/2-based RPC framework" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1015,6 +1057,7 @@ protobuf = ["grpcio-tools (>=1.62.0)"] name = "html5lib" version = "1.1" description = "HTML parser based on the WHATWG HTML specification" +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" files = [ @@ -1036,6 +1079,7 @@ lxml = ["lxml"] name = "idna" version = "3.6" description = "Internationalized Domain Names in Applications (IDNA)" +category = "main" optional = false python-versions = ">=3.5" files = [ @@ -1047,6 +1091,7 @@ files = [ name = "importlib-metadata" version = "6.0.1" description = "Read metadata from Python packages" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1066,6 +1111,7 @@ testing = ["flake8 (<5)", "flufl.flake8", "importlib-resources (>=1.3)", "packag name = "inflate64" version = "1.0.0" description = "deflate64 compression/decompression library" +category = "main" optional = false python-versions = ">=3.8" files = [ @@ -1133,6 +1179,7 @@ test = ["pyannotate", "pytest"] name = "inflection" version = "0.5.1" description = "A port of Ruby on Rails inflector to Python" +category = "main" optional = false python-versions = ">=3.5" files = [ @@ -1144,6 +1191,7 @@ files = [ name = "iniconfig" version = "2.0.0" description = "brain-dead simple config-ini parsing" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1155,6 +1203,7 @@ files = [ name = "jmespath" version = "1.0.1" description = "JSON Matching Expressions" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1166,6 +1215,7 @@ files = [ name = "jsbeautifier" version = "1.14.9" description = "JavaScript unobfuscator and beautifier." +category = "main" optional = false python-versions = "*" files = [ @@ -1180,6 +1230,7 @@ six = ">=1.13.0" name = "jsonschema" version = "4.21.1" description = "An implementation of JSON Schema validation for Python" +category = "main" optional = false python-versions = ">=3.8" files = [ @@ -1201,6 +1252,7 @@ format-nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339- name = "jsonschema-specifications" version = "2023.12.1" description = "The JSON Schema meta-schemas and vocabularies, exposed as a Registry" +category = "main" optional = false python-versions = ">=3.8" files = [ @@ -1215,6 +1267,7 @@ referencing = ">=0.31.0" name = "libarchive-c" version = "5.0" description = "Python interface to libarchive" +category = "main" optional = false python-versions = "*" files = [ @@ -1226,6 +1279,7 @@ files = [ name = "lief" version = "0.13.2" description = "Library to instrument executable formats" +category = "main" optional = false python-versions = ">=3.8" files = [ @@ -1258,6 +1312,7 @@ files = [ name = "lxml" version = "4.9.3" description = "Powerful and Pythonic XML processing library combining libxml2/libxslt with the ElementTree API." +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, != 3.4.*" files = [ @@ -1365,6 +1420,7 @@ source = ["Cython (>=0.29.35)"] name = "lznt1" version = "0.2" description = "Python module for LZNT1 compression/decompression" +category = "main" optional = false python-versions = "*" files = [ @@ -1375,6 +1431,7 @@ files = [ name = "m2crypto" version = "0.39.0" description = "M2Crypto: A Python crypto and SSL toolkit" +category = "main" optional = false python-versions = "*" files = [ @@ -1385,6 +1442,7 @@ files = [ name = "mscerts" version = "2024.2.28" description = "Python package for providing Microsoft's CA Bundle." +category = "main" optional = false python-versions = ">=3.6" files = [ @@ -1399,6 +1457,7 @@ stlupdate = ["asn1crypto", "requests", "signify"] name = "msoffcrypto-tool" version = "5.3.1" description = "Python tool and library for decrypting MS Office files with passwords or other keys" +category = "main" optional = false python-versions = ">=3.8,<4.0" files = [ @@ -1414,6 +1473,7 @@ olefile = ">=0.46" name = "multivolumefile" version = "0.2.3" description = "multi volume file wrapper library" +category = "main" optional = false python-versions = ">=3.6" files = [ @@ -1430,6 +1490,7 @@ type = ["mypy", "mypy-extensions"] name = "nested-lookup" version = "0.2.25" description = "Python functions for working with deeply nested documents (lists and dicts)" +category = "main" optional = false python-versions = "*" files = [ @@ -1443,6 +1504,7 @@ six = "*" name = "numpy" version = "1.26.0" description = "Fundamental package for array computing in Python" +category = "main" optional = false python-versions = "<3.13,>=3.9" files = [ @@ -1484,6 +1546,7 @@ files = [ name = "numpy-financial" version = "1.0.0" description = "Simple financial functions" +category = "main" optional = false python-versions = ">=3.5" files = [ @@ -1498,6 +1561,7 @@ numpy = ">=1.15" name = "olefile" version = "0.46" description = "Python package to parse, read and write Microsoft OLE2 files (Structured Storage or Compound Document, Microsoft Office)" +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" files = [ @@ -1508,6 +1572,7 @@ files = [ name = "oletools" version = "0.60.1" description = "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" +category = "main" optional = false python-versions = "*" files = [ @@ -1518,7 +1583,7 @@ files = [ [package.dependencies] colorclass = "*" easygui = "*" -msoffcrypto-tool = {version = "*", markers = "platform_python_implementation != \"PyPy\" or python_version >= \"3\" and (platform_system != \"Windows\" and platform_system != \"Darwin\")"} +msoffcrypto-tool = {version = "*", markers = "platform_python_implementation != \"PyPy\" or python_version >= \"3\" and platform_system != \"Windows\" and platform_system != \"Darwin\""} olefile = ">=0.46" pcodedmp = ">=1.2.5" pyparsing = ">=2.1.0,<3" @@ -1530,6 +1595,7 @@ full = ["XLMMacroDeobfuscator"] name = "opencv-contrib-python" version = "4.8.1.78" description = "Wrapper package for OpenCV python bindings." +category = "main" optional = false python-versions = ">=3.6" files = [ @@ -1544,15 +1610,19 @@ files = [ [package.dependencies] numpy = [ + {version = ">=1.21.2", markers = "python_version >= \"3.10\""}, + {version = ">=1.21.4", markers = "python_version >= \"3.10\" and platform_system == \"Darwin\""}, {version = ">=1.23.5", markers = "python_version >= \"3.11\""}, - {version = ">=1.21.4", markers = "python_version >= \"3.10\" and platform_system == \"Darwin\" and python_version < \"3.11\""}, - {version = ">=1.21.2", markers = "platform_system != \"Darwin\" and python_version >= \"3.10\" and python_version < \"3.11\""}, + {version = ">=1.19.3", markers = "python_version >= \"3.6\" and platform_system == \"Linux\" and platform_machine == \"aarch64\" or python_version >= \"3.9\""}, + {version = ">=1.17.0", markers = "python_version >= \"3.7\""}, + {version = ">=1.17.3", markers = "python_version >= \"3.8\""}, ] [[package]] name = "opencv-python" version = "4.8.1.78" description = "Wrapper package for OpenCV python bindings." +category = "main" optional = false python-versions = ">=3.6" files = [ @@ -1567,15 +1637,19 @@ files = [ [package.dependencies] numpy = [ + {version = ">=1.21.2", markers = "python_version >= \"3.10\""}, + {version = ">=1.21.4", markers = "python_version >= \"3.10\" and platform_system == \"Darwin\""}, {version = ">=1.23.5", markers = "python_version >= \"3.11\""}, - {version = ">=1.21.4", markers = "python_version >= \"3.10\" and platform_system == \"Darwin\" and python_version < \"3.11\""}, - {version = ">=1.21.2", markers = "platform_system != \"Darwin\" and python_version >= \"3.10\" and python_version < \"3.11\""}, + {version = ">=1.19.3", markers = "python_version >= \"3.6\" and platform_system == \"Linux\" and platform_machine == \"aarch64\" or python_version >= \"3.9\""}, + {version = ">=1.17.0", markers = "python_version >= \"3.7\""}, + {version = ">=1.17.3", markers = "python_version >= \"3.8\""}, ] [[package]] name = "openpyxl" version = "3.0.10" description = "A Python library to read/write Excel 2010 xlsx/xlsm files" +category = "main" optional = false python-versions = ">=3.6" files = [ @@ -1590,6 +1664,7 @@ et-xmlfile = "*" name = "opentelemetry-api" version = "1.17.0" description = "OpenTelemetry Python API" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1606,6 +1681,7 @@ setuptools = ">=16.0" name = "opentelemetry-exporter-jaeger" version = "1.17.0" description = "Jaeger Exporters for OpenTelemetry" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1621,6 +1697,7 @@ opentelemetry-exporter-jaeger-thrift = "1.17.0" name = "opentelemetry-exporter-jaeger-proto-grpc" version = "1.17.0" description = "Jaeger Protobuf Exporter for OpenTelemetry" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1638,6 +1715,7 @@ opentelemetry-sdk = ">=1.11,<2.0" name = "opentelemetry-exporter-jaeger-thrift" version = "1.17.0" description = "Jaeger Thrift Exporter for OpenTelemetry" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1654,6 +1732,7 @@ thrift = ">=0.10.0" name = "opentelemetry-exporter-otlp" version = "1.17.0" description = "OpenTelemetry Collector Exporters" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1669,6 +1748,7 @@ opentelemetry-exporter-otlp-proto-http = "1.17.0" name = "opentelemetry-exporter-otlp-proto-grpc" version = "1.17.0" description = "OpenTelemetry Collector Protobuf over gRPC Exporter" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1691,6 +1771,7 @@ test = ["pytest-grpc"] name = "opentelemetry-exporter-otlp-proto-http" version = "1.17.0" description = "OpenTelemetry Collector Protobuf over HTTP Exporter" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1713,6 +1794,7 @@ test = ["responses (==0.22.0)"] name = "opentelemetry-proto" version = "1.17.0" description = "OpenTelemetry Python Proto" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1727,6 +1809,7 @@ protobuf = ">=3.19,<5.0" name = "opentelemetry-sdk" version = "1.17.0" description = "OpenTelemetry Python SDK" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1744,6 +1827,7 @@ typing-extensions = ">=3.7.4" name = "opentelemetry-semantic-conventions" version = "0.38b0" description = "OpenTelemetry Semantic Conventions" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1755,6 +1839,7 @@ files = [ name = "oscrypto" version = "1.3.0" description = "TLS (SSL) sockets, key generation, encryption, decryption, signing, verification and KDFs using the OS crypto libraries. Does not require a compiler, and relies on the OS for patching. Works on Windows, OS X and Linux/BSD." +category = "main" optional = false python-versions = "*" files = [ @@ -1769,6 +1854,7 @@ asn1crypto = ">=1.5.1" name = "packaging" version = "23.2" description = "Core utilities for Python packages" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1780,6 +1866,7 @@ files = [ name = "pcodedmp" version = "1.2.6" description = "A VBA p-code disassembler" +category = "main" optional = false python-versions = "*" files = [ @@ -1795,6 +1882,7 @@ win-unicode-console = {version = "*", markers = "platform_system == \"Windows\" name = "pefile" version = "2023.2.7" description = "Python PE parsing module" +category = "main" optional = false python-versions = ">=3.6.0" files = [ @@ -1806,6 +1894,7 @@ files = [ name = "pgpdump3" version = "1.5.2" description = "PGP packet parser library" +category = "main" optional = false python-versions = "*" files = [ @@ -1817,6 +1906,7 @@ files = [ name = "pillow" version = "10.2.0" description = "Python Imaging Library (Fork)" +category = "main" optional = false python-versions = ">=3.8" files = [ @@ -1902,6 +1992,7 @@ xmp = ["defusedxml"] name = "pillow-avif-plugin" version = "1.4.2" description = "A pillow plugin that adds avif support via libavif" +category = "main" optional = false python-versions = "*" files = [ @@ -1949,6 +2040,7 @@ files = [ name = "pillow-heif" version = "0.14.0" description = "Python interface for libheif library" +category = "main" optional = false python-versions = ">=3.8" files = [ @@ -2018,6 +2110,7 @@ tests-min = ["defusedxml", "packaging", "pytest"] name = "pluggy" version = "1.4.0" description = "plugin and hook calling mechanisms for python" +category = "main" optional = false python-versions = ">=3.8" files = [ @@ -2033,6 +2126,7 @@ testing = ["pytest", "pytest-benchmark"] name = "protobuf" version = "4.25.3" description = "" +category = "main" optional = false python-versions = ">=3.8" files = [ @@ -2053,6 +2147,7 @@ files = [ name = "psutil" version = "5.9.8" description = "Cross-platform lib for process and system monitoring in Python." +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" files = [ @@ -2081,6 +2176,7 @@ test = ["enum34", "ipaddress", "mock", "pywin32", "wmi"] name = "publicsuffixlist" version = "0.10.0.20240303" description = "publicsuffixlist implement" +category = "main" optional = false python-versions = ">=2.6" files = [ @@ -2096,6 +2192,7 @@ update = ["requests"] name = "py-tlsh" version = "4.7.2" description = "TLSH (C++ Python extension)" +category = "main" optional = false python-versions = ">=2.7" files = [ @@ -2106,6 +2203,7 @@ files = [ name = "py7zr" version = "0.20.2" description = "Pure python 7-zip library" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -2136,6 +2234,7 @@ test-compat = ["libarchive-c"] name = "pyasn1" version = "0.5.1" description = "Pure-Python implementation of ASN.1 types and DER/BER/CER codecs (X.208)" +category = "main" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" files = [ @@ -2147,6 +2246,7 @@ files = [ name = "pyasn1-modules" version = "0.3.0" description = "A collection of ASN.1-based protocols modules" +category = "main" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" files = [ @@ -2161,6 +2261,7 @@ pyasn1 = ">=0.4.6,<0.6.0" name = "pybcj" version = "1.0.2" description = "bcj filter library" +category = "main" optional = false python-versions = ">=3.8" files = [ @@ -2215,6 +2316,7 @@ test = ["coverage[toml] (>=5.2)", "hypothesis", "pytest (>=6.0)", "pytest-cov"] name = "pycdlib" version = "1.14.0" description = "Pure python ISO manipulation library" +category = "main" optional = false python-versions = "*" files = [ @@ -2226,6 +2328,7 @@ files = [ name = "pycparser" version = "2.21" description = "C parser in Python" +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" files = [ @@ -2237,6 +2340,7 @@ files = [ name = "pycryptodome" version = "3.20.0" description = "Cryptographic library for Python" +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" files = [ @@ -2278,6 +2382,7 @@ files = [ name = "pycryptodomex" version = "3.20.0" description = "Cryptographic library for Python" +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" files = [ @@ -2319,6 +2424,7 @@ files = [ name = "pydyf" version = "0.9.0" description = "A low-level PDF generator." +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -2334,6 +2440,7 @@ test = ["flake8", "isort", "pillow", "pytest"] name = "pyelftools" version = "0.29" description = "Library for analyzing ELF files and DWARF debugging information" +category = "main" optional = false python-versions = "*" files = [ @@ -2345,6 +2452,7 @@ files = [ name = "pygments" version = "2.15.0" description = "Pygments is a syntax highlighting package written in Python." +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -2359,6 +2467,7 @@ plugins = ["importlib-metadata"] name = "pylzma" version = "0.5.0" description = "Python bindings for the LZMA library by Igor Pavlov." +category = "main" optional = false python-versions = "*" files = [ @@ -2369,6 +2478,7 @@ files = [ name = "pymupdf" version = "1.21.1" description = "Python bindings for the PDF toolkit and renderer MuPDF" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -2408,6 +2518,7 @@ files = [ name = "pyparsing" version = "2.4.7" description = "Python parsing module" +category = "main" optional = false python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" files = [ @@ -2419,6 +2530,7 @@ files = [ name = "pyphen" version = "0.14.0" description = "Pure Python module to hyphenate text" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -2434,6 +2546,7 @@ test = ["flake8", "isort", "pytest"] name = "pyppmd" version = "1.0.0" description = "PPMd compression/decompression library" +category = "main" optional = false python-versions = ">=3.6" files = [ @@ -2527,6 +2640,7 @@ test = ["coverage[toml] (>=5.2)", "hypothesis", "pytest (>=6.0)", "pytest-benchm name = "pytesseract" version = "0.3.10" description = "Python-tesseract is a python wrapper for Google's Tesseract-OCR" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -2542,6 +2656,7 @@ Pillow = ">=8.0.0" name = "pytest" version = "7.2.2" description = "pytest: simple powerful testing with Python" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -2565,6 +2680,7 @@ testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2. name = "pytest-mock" version = "3.10.0" description = "Thin-wrapper around the mock package for easier use with pytest" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -2582,6 +2698,7 @@ dev = ["pre-commit", "pytest-asyncio", "tox"] name = "pytest-unordered" version = "0.5.2" description = "Test equality of unordered collections in pytest" +category = "main" optional = false python-versions = "*" files = [ @@ -2596,6 +2713,7 @@ pytest = ">=6.0.0" name = "python-dateutil" version = "2.9.0.post0" description = "Extensions to the standard Python datetime module" +category = "main" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" files = [ @@ -2610,6 +2728,7 @@ six = ">=1.5" name = "python-docx" version = "0.8.11" description = "Create and update Microsoft Word .docx files." +category = "main" optional = false python-versions = "*" files = [ @@ -2623,6 +2742,7 @@ lxml = ">=2.3.2" name = "python-magic" version = "0.4.27" description = "File type identification using libmagic" +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" files = [ @@ -2634,6 +2754,7 @@ files = [ name = "pytz" version = "2024.1" description = "World timezone definitions, modern and historical" +category = "main" optional = false python-versions = "*" files = [ @@ -2645,6 +2766,7 @@ files = [ name = "pywin32" version = "306" description = "Python for Window Extensions" +category = "main" optional = false python-versions = "*" files = [ @@ -2668,6 +2790,7 @@ files = [ name = "pyxlsb2" version = "0.0.9" description = "Excel 2007+ Binary Workbook (xlsb) parser" +category = "main" optional = false python-versions = "*" files = [ @@ -2679,6 +2802,7 @@ files = [ name = "pyyaml" version = "6.0.1" description = "YAML parser and emitter for Python" +category = "main" optional = false python-versions = ">=3.6" files = [ @@ -2738,6 +2862,7 @@ files = [ name = "pyzbar" version = "0.1.9" description = "Read one-dimensional barcodes and QR codes from Python 2 and 3." +category = "main" optional = false python-versions = "*" files = [ @@ -2753,6 +2878,7 @@ scripts = ["Pillow (>=3.2.0)"] name = "pyzipper" version = "0.3.6" description = "AES encryption for zipfile." +category = "main" optional = false python-versions = ">=3.4" files = [ @@ -2767,6 +2893,7 @@ pycryptodomex = "*" name = "pyzstd" version = "0.15.9" description = "Python bindings to Zstandard (zstd) compression library, the API style is similar to Python's bz2/lzma/zlib modules." +category = "main" optional = false python-versions = ">=3.5" files = [ @@ -2898,19 +3025,21 @@ files = [ [[package]] name = "rarfile" -version = "4.0" +version = "4.1" description = "RAR archive reader for Python" +category = "main" optional = false -python-versions = "*" +python-versions = ">=3.6" files = [ - {file = "rarfile-4.0-py3-none-any.whl", hash = "sha256:1094869119012f95c31a6f22cc3a9edbdca61861b805241116adbe2d737b68f8"}, - {file = "rarfile-4.0.tar.gz", hash = "sha256:67548769229c5bda0827c1663dce3f54644f9dbfba4ae86d4da2b2afd3e602a1"}, + {file = "rarfile-4.1-py3-none-any.whl", hash = "sha256:17d7554c93c776ceae677e9d927051267d4c5eba38bf64b9cc89a415d9a5f901"}, + {file = "rarfile-4.1.tar.gz", hash = "sha256:db60b3b5bc1c4bdeb941427d50b606d51df677353385255583847639473eda48"}, ] [[package]] name = "redis" version = "4.5.4" description = "Python client for Redis database and key-value store" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -2929,6 +3058,7 @@ ocsp = ["cryptography (>=36.0.1)", "pyopenssl (==20.0.1)", "requests (>=2.26.0)" name = "referencing" version = "0.33.0" description = "JSON Referencing + Python" +category = "main" optional = false python-versions = ">=3.8" files = [ @@ -2944,6 +3074,7 @@ rpds-py = ">=0.7.0" name = "regex" version = "2023.12.25" description = "Alternative regular expression module, to replace re." +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -3046,6 +3177,7 @@ files = [ name = "requests" version = "2.31.0" description = "Python HTTP for Humans." +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -3067,6 +3199,7 @@ use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] name = "requests-file" version = "2.0.0" description = "File transport adapter for Requests" +category = "main" optional = false python-versions = "*" files = [ @@ -3081,6 +3214,7 @@ requests = ">=1.0.0" name = "rpds-py" version = "0.18.0" description = "Python bindings to Rust's persistent data structures (rpds)" +category = "main" optional = false python-versions = ">=3.8" files = [ @@ -3189,6 +3323,7 @@ files = [ name = "rpmfile" version = "1.1.1" description = "Read rpm archive files" +category = "main" optional = false python-versions = "*" files = [ @@ -3203,6 +3338,7 @@ zstd = ["zstandard (>=0.13.0)"] name = "s3transfer" version = "0.7.0" description = "An Amazon S3 Transfer Manager" +category = "main" optional = false python-versions = ">= 3.7" files = [ @@ -3220,6 +3356,7 @@ crt = ["botocore[crt] (>=1.20.29,<2.0a.0)"] name = "schedula" version = "1.5.2" description = "Produce a plan that dispatches calls based on a graph of functions, satisfying data dependencies." +category = "main" optional = false python-versions = "*" files = [ @@ -3241,6 +3378,7 @@ web = ["flask", "regex", "requests"] name = "setuptools" version = "69.1.1" description = "Easily download, build, install, upgrade, and uninstall Python packages" +category = "main" optional = false python-versions = ">=3.8" files = [ @@ -3257,6 +3395,7 @@ testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jar name = "signify" version = "0.5.1" description = "Module to generate and verify PE signatures" +category = "main" optional = false python-versions = "*" files = [ @@ -3275,6 +3414,7 @@ pyasn1-modules = ">=0.2.8" name = "six" version = "1.16.0" description = "Python 2 and 3 compatibility utilities" +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" files = [ @@ -3286,6 +3426,7 @@ files = [ name = "soupsieve" version = "2.5" description = "A modern CSS selector implementation for Beautiful Soup." +category = "main" optional = false python-versions = ">=3.8" files = [ @@ -3297,6 +3438,7 @@ files = [ name = "speakeasy-emulator" version = "1.5.11" description = "Speakeasy malware emulation framework" +category = "main" optional = false python-versions = ">=3.6" files = [] @@ -3320,6 +3462,7 @@ resolved_reference = "1cb52a92ab4bae3659b0f8db4ed29f591d932c88" name = "ssdeep" version = "3.4" description = "Python wrapper for the ssdeep library" +category = "main" optional = false python-versions = "*" files = [ @@ -3337,6 +3480,7 @@ docstest = ["doc8", "readme_renderer (>=16.0)", "sphinx", "sphinx_rtd_theme"] name = "texttable" version = "1.7.0" description = "module to create simple ASCII tables" +category = "main" optional = false python-versions = "*" files = [ @@ -3348,6 +3492,7 @@ files = [ name = "thrift" version = "0.16.0" description = "Python bindings for the Apache Thrift RPC system" +category = "main" optional = false python-versions = "*" files = [ @@ -3366,6 +3511,7 @@ twisted = ["twisted"] name = "tinycss2" version = "1.2.1" description = "A tiny CSS parser" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -3384,6 +3530,7 @@ test = ["flake8", "isort", "pytest"] name = "tldextract" version = "3.4.0" description = "Accurately separates a URL's subdomain, domain, and public suffix, using the Public Suffix List (PSL). By default, this includes the public ICANN TLDs and their exceptions. You can optionally support the Public Suffix List's private domains as well." +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -3401,6 +3548,7 @@ requests-file = ">=1.4" name = "tnefparse" version = "1.4.0" description = "a TNEF decoding library written in Python, without external dependencies" +category = "main" optional = false python-versions = "*" files = [ @@ -3415,6 +3563,7 @@ optional = ["compressed-rtf"] name = "tomli" version = "2.0.1" description = "A lil' TOML parser" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -3426,6 +3575,7 @@ files = [ name = "typing-extensions" version = "4.10.0" description = "Backported and Experimental Type Hints for Python 3.8+" +category = "main" optional = false python-versions = ">=3.8" files = [ @@ -3437,6 +3587,7 @@ files = [ name = "unicorn" version = "1.0.2" description = "Unicorn CPU emulator engine" +category = "main" optional = false python-versions = "*" files = [ @@ -3452,6 +3603,7 @@ files = [ name = "urllib3" version = "2.0.7" description = "HTTP library with thread-safe connection pooling, file post, and more." +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -3469,6 +3621,7 @@ zstd = ["zstandard (>=0.18.0)"] name = "validators" version = "0.20.0" description = "Python Data Validation for Humansâ„¢." +category = "main" optional = false python-versions = ">=3.4" files = [ @@ -3485,6 +3638,7 @@ test = ["flake8 (>=2.4.0)", "isort (>=4.2.2)", "pytest (>=2.2.3)"] name = "weasyprint" version = "60.2" description = "The Awesome Document Factory" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -3510,6 +3664,7 @@ test = ["flake8", "isort", "pytest"] name = "webencodings" version = "0.5.1" description = "Character encoding aliases for legacy web content" +category = "main" optional = false python-versions = "*" files = [ @@ -3521,6 +3676,7 @@ files = [ name = "websocket-client" version = "1.7.0" description = "WebSocket client for Python with low level API options" +category = "main" optional = false python-versions = ">=3.8" files = [ @@ -3537,6 +3693,7 @@ test = ["websockets"] name = "win-unicode-console" version = "0.5" description = "Enable Unicode input and display when running Python from Windows console." +category = "main" optional = false python-versions = "*" files = [ @@ -3547,6 +3704,7 @@ files = [ name = "wrapt" version = "1.16.0" description = "Module for decorators, wrappers and monkey patching." +category = "main" optional = false python-versions = ">=3.6" files = [ @@ -3626,6 +3784,7 @@ files = [ name = "xlrd" version = "2.0.1" description = "Library for developers to extract data from Microsoft Excel (tm) .xls spreadsheet files" +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" files = [ @@ -3642,6 +3801,7 @@ test = ["pytest", "pytest-cov"] name = "xlrd2" version = "1.3.4" description = "Library for developers to extract data from Microsoft Excel legacy spreadsheet files (xls)" +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" files = [ @@ -3653,6 +3813,7 @@ files = [ name = "xmltodict" version = "0.13.0" description = "Makes working with XML feel like you are working with JSON" +category = "main" optional = false python-versions = ">=3.4" files = [ @@ -3662,96 +3823,30 @@ files = [ [[package]] name = "yara-python" -version = "4.5.0" +version = "4.3.1" description = "Python interface for YARA" +category = "main" optional = false python-versions = "*" files = [ - {file = "yara_python-4.5.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3feb72c2146c50e583d7e3cacbb49f280fb5cac0494cae1b48e5980ecbdc1571"}, - {file = "yara_python-4.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1a036c41eaebdf6c0a674e0bd9d37c9d06d1d7427455866ace09d3a159c442b0"}, - {file = "yara_python-4.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:acbc950b051a576dd4c6825bf4ebb1c0f0c5eea35b3e01bde921b744e2cc92e1"}, - {file = "yara_python-4.5.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:11bb478dee0450af1f6643a138b9eaec085ab79aa4a4aef786390c5dfc70f0d0"}, - {file = "yara_python-4.5.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1633d5653d37b6ef6517113ed446159d8de67d02893c00c43daecd9bbcb9700b"}, - {file = "yara_python-4.5.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:82109dd8c932c3326a9747ac3ac62857868fcc9d9fe55d6f46ff0b4dcf14eb57"}, - {file = "yara_python-4.5.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:85af2755732081e5fb99fc4295659690f97b61b7a1428e857dbfe91f12f9cf7d"}, - {file = "yara_python-4.5.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a776f86060aebd4e31c74927ddd13e6b3c21a59e4586da8667e69849e5008872"}, - {file = "yara_python-4.5.0-cp310-cp310-win32.whl", hash = "sha256:535f274738d6ad5501b4450b0bf3cd3c0d1547e9773a7ca1cf9e855d8a1cf741"}, - {file = "yara_python-4.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:5de0bf8ea0d530e8af2b59c56c5506219f060b4b25b196ba88fd19753529246d"}, - {file = "yara_python-4.5.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:aeadaa62aa523e71abcb438f6f07761b9d62177021bb0e919a180d5b1e8cc05f"}, - {file = "yara_python-4.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0b4cc4eb937c1fd3747682cae639179a6bc03a24c86dde861ca2ba1abafe8404"}, - {file = "yara_python-4.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7c77dfe89038a1e0a134d44aacb05d48b268312ca3a58df1fc0cf212aaaa79ff"}, - {file = "yara_python-4.5.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:570d1f0f9893ec114a25ed9704e992ce58fb28c3399f6a3d81ea42d4e90c6a9d"}, - {file = "yara_python-4.5.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b4650def8807d5b6736a1144ca2d880198deafec2313816a4fcc365c00f9b61b"}, - {file = "yara_python-4.5.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f557ce581e35ae23f19da6e80be1f84a1ac98b17b67b04cf46a9877d8d4fa870"}, - {file = "yara_python-4.5.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:2d400917ebaf35663975ff001a6c13e432af68e641879194152212b8e87dd974"}, - {file = "yara_python-4.5.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c5de6ed584bf59005186628f88df718475ccb6818912b0b12a151a2e61f0c29c"}, - {file = "yara_python-4.5.0-cp311-cp311-win32.whl", hash = "sha256:17bee3e24258ef9d31cbc11342d22d513e005b7a551083adbb01d3ffc748c945"}, - {file = "yara_python-4.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:36acc235bf7aa61145daf364c97d3723e9c0e350cca6ae00bf717088a2c72f3b"}, - {file = "yara_python-4.5.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:6918ddc68d89f4da1eb851d6a841ebc615cec31532f74698e4f51e58b44213e0"}, - {file = "yara_python-4.5.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:9a9d666acc4d6d1ec59da4864ac4e25552169bd92520b050e5fcf92212d3ab1e"}, - {file = "yara_python-4.5.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a96d7b4a7e060c40b290d2264c17623f063a5604e6502b06cd145f2f0e6e82ed"}, - {file = "yara_python-4.5.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:91f782bdea16441c87f0231f038cf6d151e36718154871f5c5fb274364d144e7"}, - {file = "yara_python-4.5.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:006f4af0cf30725425b6777016e6d6d7b2854955f0402b227f70d6fbdc594272"}, - {file = "yara_python-4.5.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7645a4d23d7749c390da92de105831762499eba22e39e956d0fddfda05d949eb"}, - {file = "yara_python-4.5.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:244fd95d0e4f4f1fbe85032f7e9b779e2cb4c6678787725482d2b43925ff2ed3"}, - {file = "yara_python-4.5.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:c17980d60ff3f7cfabab2df28965efb8e0b38508a5c47a0ad6917016edc0328c"}, - {file = "yara_python-4.5.0-cp312-cp312-win32.whl", hash = "sha256:86549302cae8f9c5fb1394e2e9fd9dc802e3c488723935e50fdcb0d6b639ab51"}, - {file = "yara_python-4.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:36423ffd5e2e1f39562df6bd569be997c2fd06e0f30019dfadf339ff88278a4f"}, - {file = "yara_python-4.5.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:49f8e215c9ebd0b43a782d3cfbeee6f82e2a647482cff6c676e96da57014d878"}, - {file = "yara_python-4.5.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:262be4841add4d7e978db317e63f657291b4c9a64469bcdf43cdee8b3f28e0c0"}, - {file = "yara_python-4.5.0-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a0ee9e222daa982d31dc4be5fabf838726a3fb6a6425253c73f53aa0f0e2f457"}, - {file = "yara_python-4.5.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d6d7d16fffc0fe92cd8d4ead9fbd58f1da7fcd9d39ff5c32924691db66257927"}, - {file = "yara_python-4.5.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:6ebbe0c112da598828d8fad15356a20b739dd2ef0dd0d90f6ec0f00ce9aa7ae1"}, - {file = "yara_python-4.5.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:a08162e9b4bdbae1e2e34ab83b6f145d334d650399aab9afb5341ce67a866a5c"}, - {file = "yara_python-4.5.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:65b0e4c168f0210d9b6be7b95bb7e2a97a545d52e9d3403370c3c5506b9d31f1"}, - {file = "yara_python-4.5.0-cp37-cp37m-win32.whl", hash = "sha256:3393eced9af44507193f7724b8f37ed5f0c5620fb402b886d6f07f578aab43af"}, - {file = "yara_python-4.5.0-cp37-cp37m-win_amd64.whl", hash = "sha256:de539783415e5b73e81d0a26fbe21cf2ade1b32fbd653be8a0f7430907337883"}, - {file = "yara_python-4.5.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:067610dfedfe361b748bbf9148183df11141748f2a83b4bdb873fde96a62e204"}, - {file = "yara_python-4.5.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8342783942dd8bdc46f1047cdb45ff68dcfe7aef0499b644deabd3d685a2d7e6"}, - {file = "yara_python-4.5.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f14db61a8e54c61cced58d070630033b68e276036053f402e76a1b7ebc5ad0f0"}, - {file = "yara_python-4.5.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:468743b991341440fa4fd6c1414b2f5948970d26330ac1ae811f10d559c57771"}, - {file = "yara_python-4.5.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cde3be15f16c3b24a3e7a08327deeec9225cecf060c930fef58d38ae4554b17e"}, - {file = "yara_python-4.5.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:1b07363c9b44796fe991e29a5756a48ae195f3cbe4d72bb70b1932f5ccadd153"}, - {file = "yara_python-4.5.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8072d6f89d7352d09d1eeda6a8fa15582ff141495f24ceb448c7b7f7592d1820"}, - {file = "yara_python-4.5.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:29735530650093e251abe11f76d78382fb8e87f24e7bc154e4fe28c624e7d4f7"}, - {file = "yara_python-4.5.0-cp38-cp38-win32.whl", hash = "sha256:016a7ce7ec60b1dcecec6a23557abd3b78b442a8ac7dd14c4e8047ac07227ea6"}, - {file = "yara_python-4.5.0-cp38-cp38-win_amd64.whl", hash = "sha256:47fe5195687fff0f7ec435ce75b46a35b05b257c7550de5cb417bad9750bebde"}, - {file = "yara_python-4.5.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:aea5ba7084b48fb3d5edd6b01c7e45763a1f0a4dd83bc0c3c1975e61aeae86cf"}, - {file = "yara_python-4.5.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:16894888f599b4acdf57a4fc157108fb804823149f60e81a0afba0b7d2ebbf5f"}, - {file = "yara_python-4.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5e80d2d8e5f7e9c25c8150f7281183abb2e53d81c0d33cecdf1459faeb042572"}, - {file = "yara_python-4.5.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:898fbdc6c5f5913935ce2586242dd94673d27f3a031dc0704598237c25ed4431"}, - {file = "yara_python-4.5.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8e94d576dd98f7b957d34b8b03cabdf0372d11e9f65cd45bb823cbc3a086ec25"}, - {file = "yara_python-4.5.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d95e525074624c2618cb8146b4cc22455e786fbbe575b99bf191824a23f460db"}, - {file = "yara_python-4.5.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:185d542aefc154c28d59b7385f3d8d7baacbb74451f2f153a51c1216ae063d70"}, - {file = "yara_python-4.5.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:e7571f164a2da121fbc3a0d4df68fec82d2ae39bb8ea48b5a77467a467741573"}, - {file = "yara_python-4.5.0-cp39-cp39-win32.whl", hash = "sha256:692ae9d9647026908d5202a9ec68da6c8b4e4fb161828a425bc847ad03e66341"}, - {file = "yara_python-4.5.0-cp39-cp39-win_amd64.whl", hash = "sha256:b08ebeab39aab6fd80da875aa9fe0ec21ab8432f4656a0f4011a5069a9c3aab4"}, - {file = "yara_python-4.5.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:cf9758f5b22cd0d9e14cec363d5eda3af637d93728bf5287ee0887a2fce036b1"}, - {file = "yara_python-4.5.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2b68c7367df3d10d55fbeda985c02425ef9b549805fa9893505e25d68ef039e8"}, - {file = "yara_python-4.5.0-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fe47ea00cd442eeb0bfea9ffea3cec97074e0e88843358a0552063b978655736"}, - {file = "yara_python-4.5.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:344b679576d51fc6397eac3282e4ad27b1ab093cf9571a47e47b50debcd03dbf"}, - {file = "yara_python-4.5.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:5c02545da623e58f5df8292a7d214b97667989904271d30b5e389b9ab1a81395"}, - {file = "yara_python-4.5.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:e344f6953be0f60dc4fa94f629568e12ed9d2b981997e22bf32bd83083774c2f"}, - {file = "yara_python-4.5.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77da34bac1807d270df4840d28c01a7d879e0ce0a0c692a0b0bd8f2adf4f6d1c"}, - {file = "yara_python-4.5.0-pp37-pypy37_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2d262ddf7032f1a9efe9c7533315c37db86ad58c10b5888d704f50f0f82bdfb8"}, - {file = "yara_python-4.5.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7d04c17b27e33f9541a47f12972fc1a5836d2dfa947f48ed3242c5c9c32fc47b"}, - {file = "yara_python-4.5.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:847c249bbf6847f6d056921315eeef2d9c2ac79fcf063384d9b752b51e087afd"}, - {file = "yara_python-4.5.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:0361820a3a8ec522cbb259d6af664d5f7dd19b29e4bcce9c36e480832b4172fc"}, - {file = "yara_python-4.5.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:00a7c3d593ef0d065d84c06ed365ce24735f553669de1e066851e8f6cdfc2051"}, - {file = "yara_python-4.5.0-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a45b201d634be0f37bcb5fcf898fc91c66032c3f87b3e7656953f111e475f409"}, - {file = "yara_python-4.5.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7c1cbcfc262840ea1fb19bc961eaff51d764896bd411643f5695a46046cae99b"}, - {file = "yara_python-4.5.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:445d80c3a327345cae346cb12c86b01265127319a9cf3a1d93e301ee080fb2c7"}, - {file = "yara_python-4.5.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:c966f098254d6883ea9b1bba9743a298658cc58f43904ea6564e31e3e0d699d8"}, - {file = "yara_python-4.5.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c901ce071141261a857ea779bcdf0c1e63e238d1b597a411dc2f816796a25c63"}, - {file = "yara_python-4.5.0-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:26085645d894bdfa75e2e88ba57d8297ed89c415ae3c2224ef27095c13c0c050"}, - {file = "yara_python-4.5.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a0778e2d41bde173dd5e4758bb2f6d58832f149ff10d333e8751ae3cc4b195b2"}, - {file = "yara_python-4.5.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:648072ba075b930d976b5f6ae8c4563e4e32a057a2a8bd5be0e82ac98b682c60"}, + {file = "yara-python-4.3.1.tar.gz", hash = "sha256:7af4354ee0f1561f51fd01771a121d8d385b93bbc6138a25a38ce68aa6801c2c"}, + {file = "yara_python-4.3.1-cp310-cp310-win32.whl", hash = "sha256:834d8fe2cdb5cf61d5e505d32c504b62891df5ce5af9f04dbd8c7575c1dbef30"}, + {file = "yara_python-4.3.1-cp310-cp310-win_amd64.whl", hash = "sha256:ec05d0b7cc46728119d450875382d5386305510b8cfb14bad3627060c9e6f199"}, + {file = "yara_python-4.3.1-cp311-cp311-win32.whl", hash = "sha256:c8663f75f0c5f82e0f6053c9ddbddd97f956787bffb7f64cac1e40e0a21db89c"}, + {file = "yara_python-4.3.1-cp311-cp311-win_amd64.whl", hash = "sha256:51f1bdbcdbcfa29a7a2cf7e7e5d6a7d8dbd00568e4ab6103adceda5a499895a6"}, + {file = "yara_python-4.3.1-cp37-cp37m-win32.whl", hash = "sha256:8847bb5e74d2b33cf48f372bed02805876ab2e414aa7fd50c16a7c6150316601"}, + {file = "yara_python-4.3.1-cp37-cp37m-win_amd64.whl", hash = "sha256:fbf8281a6eb538b5c219a9c54c65a910806e7adf28a7d878f2cff551cbbd43ea"}, + {file = "yara_python-4.3.1-cp38-cp38-win32.whl", hash = "sha256:fd70fe0ba522d4ecd73cbf45e528872844e61e2febe8f66e8a94c2674751b831"}, + {file = "yara_python-4.3.1-cp38-cp38-win_amd64.whl", hash = "sha256:950377dd53d17870f66406f0db9ed9f6b04ac7e61c7f6fd5429459e2a00fd874"}, + {file = "yara_python-4.3.1-cp39-cp39-win32.whl", hash = "sha256:c032ad4ec6698a4f485b3d9721e6223028953bb61362482bdd7eabb147271e80"}, + {file = "yara_python-4.3.1-cp39-cp39-win_amd64.whl", hash = "sha256:f0b7b2ea840638415a075a25860dbcb6466d83000a8367196188a6367b01af71"}, ] [[package]] name = "zipp" version = "3.17.0" description = "Backport of pathlib-compatible object wrapper for zip files" +category = "main" optional = false python-versions = ">=3.8" files = [ @@ -3767,6 +3862,7 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "p name = "zopfli" version = "0.2.3" description = "Zopfli module for python" +category = "main" optional = false python-versions = ">=3.8" files = [ @@ -3839,4 +3935,4 @@ test = ["pytest"] [metadata] lock-version = "2.0" python-versions = ">=3.10,<=3.12" -content-hash = "860bf3d7bd7370177816188ff060b5a3dbef6d287438209247e4bd015b2ecf3f" +content-hash = "78109e2b46dcf698a144a166a04de36c11fd278efb299ae1c7f344253842782e" diff --git a/pyproject.toml b/pyproject.toml index c0841b50..02248d53 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -66,7 +66,7 @@ pyxlsb2 = "0.0.9" pyyaml = "6.0.1" pyzbar = "0.1.9" pyzipper = "0.3.6" -rarfile = "4.0" +rarfile = "4.1" redis = "4.5.4" requests = "2.31.0" rpmfile = "1.1.1" @@ -79,6 +79,7 @@ weasyprint = "60.2" xlrd2 = "1.3.4" xlrd = "2.0.1" xmltodict = "0.13.0" +yara-python = "4.3.1" # Git dependencies chaskey = { git = "https://github.com/volexity/chaskey-lts", rev = "2fd80f732dd9422a9e92556758180ceee3b5b4ec" } # For donut-decryptor diff --git a/src/python/strelka/scanners/scan_rar.py b/src/python/strelka/scanners/scan_rar.py index 8accb3b1..0021b48d 100644 --- a/src/python/strelka/scanners/scan_rar.py +++ b/src/python/strelka/scanners/scan_rar.py @@ -1,3 +1,4 @@ +import datetime import io import os @@ -70,7 +71,61 @@ def scan(self, data, file, options, expire_at): with io.BytesIO(data) as rar_io: try: - with rarfile.RarFile(rar_io) as rar_obj: + with rarfile.RarFile(rar_io, part_only=True) as rar_obj: + # If any file in the archive is encrypted, set a flag + if ( + rar_obj.needs_password() + and "password_protected" not in self.flags + ): + self.flags.append("password_protected") + + # If filename encryption is enabled, set a flag + if ( + rar_obj.needs_password() + and len(list(rar_obj.infolist())) == 0 + and "encrypted_filenames" not in self.flags + ): + self.flags.append("encrypted_filenames") + + # If filename encryption is enabled, attempt to recover the password + if ( + crack_pws + and self.passwords + and len(list(rar_obj.infolist())) == 0 + ): + for password in self.passwords: + try: + # Re-instantiate rarfile to address password change behavior + rar_obj = rarfile.RarFile(rar_io, part_only=True) + rar_obj.setpassword( + password.decode("utf-8") if password else None + ) + + if list(rar_obj.infolist()): + self.passwords.insert( + 0, + self.passwords.pop( + self.passwords.index(password) + ), + ) + + break + except rarfile.RarWrongPassword: + pass + except rarfile.PasswordRequired: + pass + except rarfile.BadRarFile: + pass + + if ( + len(list(rar_obj.infolist())) == 0 + and "no_password_match_found" not in self.flags + ): + self.flags.append("no_password_match_found") + + if rar_obj.comment: + self.event["comment"] = rar_obj.comment + filelist = rar_obj.infolist() # Count the file entries, in case the function encounters an unhandled exception @@ -82,7 +137,6 @@ def scan(self, data, file, options, expire_at): # For each file in rar, gather metadata and pass extracted file back to Strelka for i, name in enumerate(filelist): if not name.isdir(): - extract = True extracted = False compression_rate = 0 @@ -120,52 +174,83 @@ def scan(self, data, file, options, expire_at): compressed_file.host_os ] - if not compressed_file.needs_password(): - extract_data = rar_obj.read(name) - else: - if "password_protected" not in self.flags: - self.flags.append("password_protected") - - for password in self.passwords: - try: - data = rar_obj.open( - name, - mode="r", - pwd=( - password.decode("utf-8") + try: + rar_data_io = rar_obj.open( + compressed_file, mode="r" + ) + if rar_data_io.readable(): + extract_data = rar_data_io.readall() + except Exception: + try: + # rar_obj = rarfile.RarFile(rar_io, part_only=True) + rar_data_io = rar_obj.open( + compressed_file, + mode="r", + pwd="", + ) + if rar_data_io.readable(): + extract_data = rar_data_io.readall() + + except Exception: + if "password_protected" not in self.flags: + self.flags.append("password_protected") + + for password in self.passwords: + try: + # rar_obj = rarfile.RarFile(rar_io, part_only=True) + rar_data_io = rar_obj.open( + compressed_file, + mode="r", + pwd=password.decode("utf-8") if password - else None - ), - ) - if data.readable(): - extract_data = data.readall() - self.passwords.insert( - 0, - self.passwords.pop( - self.passwords.index(password) - ), + else None, ) - if password and log_pws: - self.event["password"] = ( - password.decode("utf-8") + if rar_data_io.readable(): + extract_data = rar_data_io.readall() + self.passwords.insert( + 0, + self.passwords.pop( + self.passwords.index( + password + ) + ), ) - break - except ( - RuntimeError, - rarfile.RarCRCError, - rarfile.RarWrongPassword, - ): - raise - except ( - rarfile.BadRarFile, - rarfile.PasswordRequired, - ): - pass - + if ( + password + and crack_pws + and log_pws + ): + if ( + "password" + not in self.event.keys() + ): + self.event["password"] = [] + if password.decode( + "utf-8" + ) not in self.event.get( + "password", [] + ): + self.event[ + "password" + ].append( + password.decode("utf-8") + ) + break + except ( + RuntimeError, + rarfile.RarCRCError, + rarfile.RarWrongPassword, + rarfile.BadRarFile, + rarfile.PasswordRequired, + ): + pass + + # If we're cracking passwords, a file was encrypted, but failed to decrypt, set a flag if ( - not extract_data + crack_pws + and compressed_file.needs_password() + and not extract_data and "no_password_match_found" not in self.flags - and not crack_pws ): self.flags.append("no_password_match_found") @@ -185,6 +270,22 @@ def scan(self, data, file, options, expire_at): self.event["files"].append( { "file_name": compressed_file.filename, + "datetime": compressed_file.mtime.isoformat(), + "ctime": compressed_file.ctime.isoformat() + if isinstance( + compressed_file.ctime, datetime.datetime + ) + else None, + "mtime": compressed_file.mtime.isoformat() + if isinstance( + compressed_file.mtime, datetime.datetime + ) + else None, + "atime": compressed_file.atime.isoformat() + if isinstance( + compressed_file.atime, datetime.datetime + ) + else None, "file_size": compressed_file.file_size, "compression_size": compressed_file.compress_size, "compression_rate": round( @@ -200,17 +301,14 @@ def scan(self, data, file, options, expire_at): except NotImplementedError: self.flags.append("unsupport_compression") - raise except RuntimeError: self.flags.append("runtime_error") - raise except ValueError: self.flags.append("value_error") - raise except rarfile.BadRarFile: - raise self.flags.append("bad_rar") + raise # Top level compression metric if file_size_total > 0 and compress_size_total > 0: diff --git a/src/python/strelka/tests/fixtures/test_password_encrypted_headers.rar b/src/python/strelka/tests/fixtures/test_password_encrypted_headers.rar new file mode 100755 index 0000000000000000000000000000000000000000..af00a391916be767c98b02469b303da8570e6f46 GIT binary patch literal 5006 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 YNId<4ck#e6L$bcpD5Sm6Firz8Q~ubvvH$=8 literal 0 HcmV?d00001 diff --git a/src/python/strelka/tests/fixtures/test_mixed_filenames.rar b/src/python/strelka/tests/fixtures/test_password_mixed_encrypted_headers.rar similarity index 100% rename from src/python/strelka/tests/fixtures/test_mixed_filenames.rar rename to src/python/strelka/tests/fixtures/test_password_mixed_encrypted_headers.rar diff --git a/src/python/strelka/tests/helpers/test_passwords_alternate.dat b/src/python/strelka/tests/helpers/test_passwords_alternate.dat new file mode 100644 index 00000000..62d5a7da --- /dev/null +++ b/src/python/strelka/tests/helpers/test_passwords_alternate.dat @@ -0,0 +1,2 @@ +Spring2024! +hunter2 diff --git a/src/python/strelka/tests/test_scan_rar.py b/src/python/strelka/tests/test_scan_rar.py index fb5949b6..93d4fb9c 100644 --- a/src/python/strelka/tests/test_scan_rar.py +++ b/src/python/strelka/tests/test_scan_rar.py @@ -19,6 +19,10 @@ def test_scan_rar(mocker): "files": [ { "file_name": "hidden/lorem-hidden.txt", + "datetime": "2022-12-12T03:12:55.499569400+00:00", + "atime": None, + "ctime": None, + "mtime": "2022-12-12T03:12:55.499569400+00:00", "file_size": 4015, "compression_size": 1484, "compression_rate": 63.04, @@ -27,6 +31,10 @@ def test_scan_rar(mocker): }, { "file_name": "hidden/lorem-readonly.txt", + "datetime": "2022-12-12T03:12:55.499569400+00:00", + "atime": None, + "ctime": None, + "mtime": "2022-12-12T03:12:55.499569400+00:00", "file_size": 4015, "compression_size": 1484, "compression_rate": 63.04, @@ -35,6 +43,10 @@ def test_scan_rar(mocker): }, { "file_name": "lorem.txt", + "datetime": "2022-12-12T03:12:55.499569400+00:00", + "atime": None, + "ctime": None, + "mtime": "2022-12-12T03:12:55.499569400+00:00", "file_size": 4015, "compression_size": 1484, "compression_rate": 63.04, @@ -69,6 +81,10 @@ def test_scan_rar_file_limit(mocker): "files": [ { "file_name": "hidden/lorem-hidden.txt", + "datetime": "2022-12-12T03:12:55.499569400+00:00", + "atime": None, + "ctime": None, + "mtime": "2022-12-12T03:12:55.499569400+00:00", "file_size": 4015, "compression_size": 1484, "compression_rate": 63.04, @@ -77,6 +93,10 @@ def test_scan_rar_file_limit(mocker): }, { "file_name": "hidden/lorem-readonly.txt", + "datetime": "2022-12-12T03:12:55.499569400+00:00", + "atime": None, + "ctime": None, + "mtime": "2022-12-12T03:12:55.499569400+00:00", "file_size": 4015, "compression_size": 1484, "compression_rate": 63.04, @@ -85,6 +105,10 @@ def test_scan_rar_file_limit(mocker): }, { "file_name": "lorem.txt", + "datetime": "2022-12-12T03:12:55.499569400+00:00", + "atime": None, + "ctime": None, + "mtime": "2022-12-12T03:12:55.499569400+00:00", "file_size": 4015, "compression_size": 1484, "compression_rate": 63.04, @@ -127,6 +151,10 @@ def test_scan_rar_file_limit_no_meta(mocker): "files": [ { "file_name": "hidden/lorem-hidden.txt", + "datetime": "2022-12-12T03:12:55.499569400+00:00", + "atime": None, + "ctime": None, + "mtime": "2022-12-12T03:12:55.499569400+00:00", "file_size": 4015, "compression_size": 1484, "compression_rate": 63.04, @@ -169,6 +197,10 @@ def test_scan_rar_crack_pws_unencrypted(mocker): "files": [ { "file_name": "hidden/lorem-hidden.txt", + "datetime": "2022-12-12T03:12:55.499569400+00:00", + "atime": None, + "ctime": None, + "mtime": "2022-12-12T03:12:55.499569400+00:00", "file_size": 4015, "compression_size": 1484, "compression_rate": 63.04, @@ -177,6 +209,10 @@ def test_scan_rar_crack_pws_unencrypted(mocker): }, { "file_name": "hidden/lorem-readonly.txt", + "datetime": "2022-12-12T03:12:55.499569400+00:00", + "atime": None, + "ctime": None, + "mtime": "2022-12-12T03:12:55.499569400+00:00", "file_size": 4015, "compression_size": 1484, "compression_rate": 63.04, @@ -185,6 +221,10 @@ def test_scan_rar_crack_pws_unencrypted(mocker): }, { "file_name": "lorem.txt", + "datetime": "2022-12-12T03:12:55.499569400+00:00", + "atime": None, + "ctime": None, + "mtime": "2022-12-12T03:12:55.499569400+00:00", "file_size": 4015, "compression_size": 1484, "compression_rate": 63.04, @@ -227,6 +267,10 @@ def test_scan_rar_password(mocker): "files": [ { "file_name": "hidden/lorem-hidden.txt", + "datetime": "2022-12-12T03:12:55.499569400+00:00", + "atime": None, + "ctime": None, + "mtime": "2022-12-12T03:12:55.499569400+00:00", "file_size": 4015, "compression_size": 1488, "compression_rate": 62.94, @@ -235,6 +279,10 @@ def test_scan_rar_password(mocker): }, { "file_name": "hidden/lorem-readonly.txt", + "datetime": "2022-12-12T03:12:55.499569400+00:00", + "atime": None, + "ctime": None, + "mtime": "2022-12-12T03:12:55.499569400+00:00", "file_size": 4015, "compression_size": 1488, "compression_rate": 62.94, @@ -243,6 +291,10 @@ def test_scan_rar_password(mocker): }, { "file_name": "lorem.txt", + "datetime": "2022-12-12T03:12:55.499569400+00:00", + "atime": None, + "ctime": None, + "mtime": "2022-12-12T03:12:55.499569400+00:00", "file_size": 4015, "compression_size": 1488, "compression_rate": 62.94, @@ -280,12 +332,16 @@ def test_scan_rar_password_log_pwd(mocker): test_scan_event = { "elapsed": mock.ANY, "flags": ["password_protected"], - "password": "password", + "password": ["password"], "total": {"files": 3, "extracted": 3}, "host_os": "RAR_OS_WIN32", "files": [ { "file_name": "hidden/lorem-hidden.txt", + "datetime": "2022-12-12T03:12:55.499569400+00:00", + "atime": None, + "ctime": None, + "mtime": "2022-12-12T03:12:55.499569400+00:00", "file_size": 4015, "compression_size": 1488, "compression_rate": 62.94, @@ -294,6 +350,10 @@ def test_scan_rar_password_log_pwd(mocker): }, { "file_name": "hidden/lorem-readonly.txt", + "datetime": "2022-12-12T03:12:55.499569400+00:00", + "atime": None, + "ctime": None, + "mtime": "2022-12-12T03:12:55.499569400+00:00", "file_size": 4015, "compression_size": 1488, "compression_rate": 62.94, @@ -302,6 +362,10 @@ def test_scan_rar_password_log_pwd(mocker): }, { "file_name": "lorem.txt", + "datetime": "2022-12-12T03:12:55.499569400+00:00", + "atime": None, + "ctime": None, + "mtime": "2022-12-12T03:12:55.499569400+00:00", "file_size": 4015, "compression_size": 1488, "compression_rate": 62.94, @@ -338,12 +402,16 @@ def test_scan_rar_password_crack_pws(mocker): test_scan_event = { "elapsed": mock.ANY, - "flags": ["password_protected", "no_password_match_found"], + "flags": ["password_protected"], "total": {"files": 3, "extracted": 0}, "host_os": "RAR_OS_WIN32", "files": [ { "file_name": "hidden/lorem-hidden.txt", + "datetime": "2022-12-12T03:12:55.499569400+00:00", + "atime": None, + "ctime": None, + "mtime": "2022-12-12T03:12:55.499569400+00:00", "file_size": 4015, "compression_size": 1488, "compression_rate": 62.94, @@ -352,6 +420,10 @@ def test_scan_rar_password_crack_pws(mocker): }, { "file_name": "hidden/lorem-readonly.txt", + "datetime": "2022-12-12T03:12:55.499569400+00:00", + "atime": None, + "ctime": None, + "mtime": "2022-12-12T03:12:55.499569400+00:00", "file_size": 4015, "compression_size": 1488, "compression_rate": 62.94, @@ -360,6 +432,10 @@ def test_scan_rar_password_crack_pws(mocker): }, { "file_name": "lorem.txt", + "datetime": "2022-12-12T03:12:55.499569400+00:00", + "atime": None, + "ctime": None, + "mtime": "2022-12-12T03:12:55.499569400+00:00", "file_size": 4015, "compression_size": 1488, "compression_rate": 62.94, @@ -388,6 +464,308 @@ def test_scan_rar_password_crack_pws(mocker): TestCase().assertDictEqual(test_scan_event, scanner_event) +def test_scan_rar_password_mixed(mocker): + """ + Pass: Sample event matches output of scanner. + Failure: Unable to load file or sample event fails to match. + """ + + test_scan_event = { + "elapsed": mock.ANY, + "flags": ["password_protected", "no_password_match_found"], + "password": ["password", "infected"], + "total": {"files": 4, "extracted": 3}, + "host_os": "RAR_OS_WIN32", + "files": [ + { + "file_name": "lorem.txt", + "datetime": "2022-12-12T03:12:55.499569400+00:00", + "atime": None, + "ctime": None, + "mtime": "2022-12-12T03:12:55.499569400+00:00", + "file_size": 4015, + "compression_size": 1478, + "compression_rate": 63.19, + "extracted": True, + "encrypted": False, + }, + { + "file_name": "hidden/lorem-hidden.txt", + "datetime": "2022-12-12T03:12:55.499569400+00:00", + "atime": None, + "ctime": None, + "mtime": "2022-12-12T03:12:55.499569400+00:00", + "file_size": 4015, + "compression_size": 1488, + "compression_rate": 62.94, + "extracted": True, + "encrypted": True, + }, + { + "file_name": "hidden/lorem.txt", + "datetime": "2022-12-12T03:12:55.499569400+00:00", + "atime": None, + "ctime": None, + "mtime": "2022-12-12T03:12:55.499569400+00:00", + "file_size": 4015, + "compression_size": 1488, + "compression_rate": 62.94, + "extracted": True, + "encrypted": True, + }, + { + "file_name": "hidden/lorem-readonly.txt", + "datetime": "2022-12-12T03:12:55.499569400+00:00", + "atime": None, + "ctime": None, + "mtime": "2022-12-12T03:12:55.499569400+00:00", + "file_size": 4015, + "compression_size": 1488, + "compression_rate": 62.94, + "extracted": False, + "encrypted": True, + }, + ], + "compression_rate": 63.0, + } + + scanner_event = run_test_scan( + mocker=mocker, + scan_class=ScanUnderTest, + fixture_path=Path(__file__).parent / "fixtures/test_mixed.rar", + options={ + "limit": 1000, + "limit_metadata": True, + "size_limit": 250000000, + "crack_pws": True, + "log_pws": True, + "password_file": "/etc/strelka/passwords.dat", + }, + ) + + TestCase.maxDiff = None + TestCase().assertDictEqual(test_scan_event, scanner_event) + + +# +# +# KNOWN LIMITATION: The following test uses an archive with encrypted headers, but an unencrypted member file. This +# causes unexpected behavior in the rarfile module, and will cause extraction of the unencrypted member file to fail +# +# + +# def test_scan_rar_password_mixed_encrypted_headers(mocker): +# """ +# Pass: Sample event matches output of scanner. +# Failure: Unable to load file or sample event fails to match. +# """ +# +# test_scan_event = { +# "elapsed": mock.ANY, +# "flags": ["password_protected", "encrypted_filenames"], +# "total": {"files": 3, "extracted": 3}, +# "files": [ +# { +# "file_name": "hidden/lorem-hidden.txt", +# "datetime": "2022-12-12T03:12:55.499569400+00:00", +# "atime": None, +# "ctime": None, +# "mtime": "2022-12-12T03:12:55.499569400+00:00", +# "file_size": 4015, +# "compression_size": 1488, +# "compression_rate": 62.94, +# "extracted": True, +# "encrypted": True, +# }, +# { +# "file_name": "hidden/lorem-readonly.txt", +# "datetime": "2022-12-12T03:12:55.499569400+00:00", +# "atime": None, +# "ctime": None, +# "mtime": "2022-12-12T03:12:55.499569400+00:00", +# "file_size": 4015, +# "compression_size": 1488, +# "compression_rate": 62.94, +# "extracted": True, +# "encrypted": True, +# }, +# { +# "file_name": "lorem.txt", +# "datetime": "2022-12-12T03:12:55.499569400+00:00", +# "atime": None, +# "ctime": None, +# "mtime": "2022-12-12T03:12:55.499569400+00:00", +# "file_size": 4015, +# "compression_size": 1488, +# "compression_rate": 62.94, +# "extracted": True, +# "encrypted": True, +# }, +# ], +# "compression_rate": 62.94, +# } +# +# scanner_event = run_test_scan( +# mocker=mocker, +# scan_class=ScanUnderTest, +# fixture_path=Path(__file__).parent / "fixtures/test_mixed_filenames.rar", +# options={ +# "limit": 1000, +# "limit_metadata": True, +# "size_limit": 250000000, +# "crack_pws": True, +# "log_pws": True, +# "password_file": "/etc/strelka/passwords.dat", +# }, +# ) +# +# TestCase.maxDiff = None +# TestCase().assertDictEqual(test_scan_event, scanner_event) + + +def test_scan_rar_password_mixed_encrypted_headers_nomatch(mocker): + """ + Pass: Sample event matches output of scanner. + Failure: Unable to load file or sample event fails to match. + """ + + test_scan_event = { + "elapsed": mock.ANY, + "flags": [ + "password_protected", + "encrypted_filenames", + "no_password_match_found", + ], + "total": {"files": 0, "extracted": 0}, + "files": [], + "compression_rate": 0.0, + } + + scanner_event = run_test_scan( + mocker=mocker, + scan_class=ScanUnderTest, + fixture_path=Path(__file__).parent + / "fixtures/test_password_mixed_encrypted_headers.rar", + options={ + "limit": 1000, + "limit_metadata": True, + "size_limit": 250000000, + "crack_pws": True, + "log_pws": True, + "password_file": str( + Path(Path(__file__).parent / "helpers/test_passwords_alternate.dat") + ), + }, + ) + + TestCase.maxDiff = None + TestCase().assertDictEqual(test_scan_event, scanner_event) + + +def test_scan_rar_password_mixed_encrypted_headers_nocrack(mocker): + """ + Pass: Sample event matches output of scanner. + Failure: Unable to load file or sample event fails to match. + """ + + test_scan_event = { + "elapsed": mock.ANY, + "flags": ["password_protected", "encrypted_filenames"], + "total": {"files": 0, "extracted": 0}, + "files": [], + "compression_rate": 0.0, + } + + scanner_event = run_test_scan( + mocker=mocker, + scan_class=ScanUnderTest, + fixture_path=Path(__file__).parent + / "fixtures/test_password_mixed_encrypted_headers.rar", + options={ + "limit": 1000, + "limit_metadata": True, + "size_limit": 250000000, + "crack_pws": False, + "log_pws": True, + "password_file": "/etc/strelka/passwords.dat", + }, + ) + + TestCase.maxDiff = None + TestCase().assertDictEqual(test_scan_event, scanner_event) + + +def test_scan_rar_password_encrypted_headers(mocker): + """ + Pass: Sample event matches output of scanner. + Failure: Unable to load file or sample event fails to match. + """ + + test_scan_event = { + "elapsed": mock.ANY, + "flags": ["password_protected", "encrypted_filenames"], + "total": {"files": 3, "extracted": 3}, + "host_os": "RAR_OS_WIN32", + "files": [ + { + "file_name": "hidden/lorem-hidden.txt", + "datetime": "2022-12-12T03:12:55.499569400+00:00", + "atime": None, + "ctime": None, + "mtime": "2022-12-12T03:12:55.499569400+00:00", + "file_size": 4015, + "compression_size": 1488, + "compression_rate": 62.94, + "extracted": True, + "encrypted": True, + }, + { + "file_name": "hidden/lorem-readonly.txt", + "datetime": "2022-12-12T03:12:55.499569400+00:00", + "atime": None, + "ctime": None, + "mtime": "2022-12-12T03:12:55.499569400+00:00", + "file_size": 4015, + "compression_size": 1488, + "compression_rate": 62.94, + "extracted": True, + "encrypted": True, + }, + { + "file_name": "lorem.txt", + "datetime": "2022-12-12T03:12:55.499569400+00:00", + "atime": None, + "ctime": None, + "mtime": "2022-12-12T03:12:55.499569400+00:00", + "file_size": 4015, + "compression_size": 1488, + "compression_rate": 62.94, + "extracted": True, + "encrypted": True, + }, + ], + "compression_rate": 62.94, + } + + scanner_event = run_test_scan( + mocker=mocker, + scan_class=ScanUnderTest, + fixture_path=Path(__file__).parent + / "fixtures/test_password_encrypted_headers.rar", + options={ + "limit": 1000, + "limit_metadata": True, + "size_limit": 250000000, + "crack_pws": True, + "log_pws": True, + "password_file": "/etc/strelka/passwords.dat", + }, + ) + + TestCase.maxDiff = None + TestCase().assertDictEqual(test_scan_event, scanner_event) + + def test_scan_rar_password_bad_path(mocker): """ Pass: Sample event matches output of scanner. @@ -396,12 +774,20 @@ def test_scan_rar_password_bad_path(mocker): test_scan_event = { "elapsed": mock.ANY, - "flags": ["password_file_missing", "password_protected"], + "flags": [ + "password_file_missing", + "password_protected", + "no_password_match_found", + ], "total": {"files": 3, "extracted": 0}, "host_os": "RAR_OS_WIN32", "files": [ { "file_name": "hidden/lorem-hidden.txt", + "datetime": "2022-12-12T03:12:55.499569400+00:00", + "atime": None, + "ctime": None, + "mtime": "2022-12-12T03:12:55.499569400+00:00", "file_size": 4015, "compression_size": 1488, "compression_rate": 62.94, @@ -410,6 +796,10 @@ def test_scan_rar_password_bad_path(mocker): }, { "file_name": "hidden/lorem-readonly.txt", + "datetime": "2022-12-12T03:12:55.499569400+00:00", + "atime": None, + "ctime": None, + "mtime": "2022-12-12T03:12:55.499569400+00:00", "file_size": 4015, "compression_size": 1488, "compression_rate": 62.94, @@ -418,6 +808,10 @@ def test_scan_rar_password_bad_path(mocker): }, { "file_name": "lorem.txt", + "datetime": "2022-12-12T03:12:55.499569400+00:00", + "atime": None, + "ctime": None, + "mtime": "2022-12-12T03:12:55.499569400+00:00", "file_size": 4015, "compression_size": 1488, "compression_rate": 62.94, @@ -454,12 +848,20 @@ def test_scan_rar_password_empty_file(mocker): test_scan_event = { "elapsed": mock.ANY, - "flags": ["no_passwords_loaded", "password_protected"], + "flags": [ + "no_passwords_loaded", + "password_protected", + "no_password_match_found", + ], "total": {"files": 3, "extracted": 0}, "host_os": "RAR_OS_WIN32", "files": [ { "file_name": "hidden/lorem-hidden.txt", + "datetime": "2022-12-12T03:12:55.499569400+00:00", + "atime": None, + "ctime": None, + "mtime": "2022-12-12T03:12:55.499569400+00:00", "file_size": 4015, "compression_size": 1488, "compression_rate": 62.94, @@ -468,6 +870,10 @@ def test_scan_rar_password_empty_file(mocker): }, { "file_name": "hidden/lorem-readonly.txt", + "datetime": "2022-12-12T03:12:55.499569400+00:00", + "atime": None, + "ctime": None, + "mtime": "2022-12-12T03:12:55.499569400+00:00", "file_size": 4015, "compression_size": 1488, "compression_rate": 62.94, @@ -476,6 +882,10 @@ def test_scan_rar_password_empty_file(mocker): }, { "file_name": "lorem.txt", + "datetime": "2022-12-12T03:12:55.499569400+00:00", + "atime": None, + "ctime": None, + "mtime": "2022-12-12T03:12:55.499569400+00:00", "file_size": 4015, "compression_size": 1488, "compression_rate": 62.94, @@ -504,6 +914,53 @@ def test_scan_rar_password_empty_file(mocker): TestCase().assertDictEqual(test_scan_event, scanner_event) +def test_scan_rar_comment_expanded_timestamps(mocker): + """ + Pass: Sample event matches output of scanner. + Failure: Unable to load file or sample event fails to match. + """ + + test_scan_event = { + "elapsed": mock.ANY, + "flags": [], + "comment": "Lorem ipsum dolor sit amet, consectetur adipiscing elit.", + "total": {"files": 1, "extracted": 1}, + "host_os": "RAR_OS_WIN32", + "files": [ + { + "file_name": "lorem.txt", + "datetime": "2022-12-12T03:12:55.499569400+00:00", + "atime": "2024-03-06T19:10:07.865616500+00:00", + "ctime": "2024-03-06T19:10:07.865616500+00:00", + "mtime": "2022-12-12T03:12:55.499569400+00:00", + "file_size": 4015, + "compression_size": 1478, + "compression_rate": 63.19, + "extracted": True, + "encrypted": False, + }, + ], + "compression_rate": 63.19, + } + + scanner_event = run_test_scan( + mocker=mocker, + scan_class=ScanUnderTest, + fixture_path=Path(__file__).parent / "fixtures/test_comment_time.rar", + options={ + "limit": 1000, + "limit_metadata": True, + "size_limit": 250000000, + "crack_pws": True, + "log_pws": True, + "password_file": "/etc/strelka/passwords.dat", + }, + ) + + TestCase.maxDiff = None + TestCase().assertDictEqual(test_scan_event, scanner_event) + + def test_scan_rar_big(mocker): """ Pass: Sample event matches output of scanner. @@ -518,6 +975,10 @@ def test_scan_rar_big(mocker): "files": [ { "file_name": "test_big.zero", + "datetime": "2024-01-26T22:48:36+00:00", + "atime": None, + "ctime": None, + "mtime": "2024-01-26T22:48:36+00:00", "file_size": 512000000, "compression_size": 20674, "compression_rate": 100.0, From 330a75d7a5e0aea22372b30d3017039aaf42bc5b Mon Sep 17 00:00:00 2001 From: Ryan O'Horo <10855297+ryanohoro@users.noreply.github.com> Date: Sat, 9 Mar 2024 00:43:57 -0600 Subject: [PATCH 7/8] Update pre-commit config --- .pre-commit-config.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a121cff1..376e12d9 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,7 +1,7 @@ --- repos: - repo: https://github.com/psf/black - rev: "22.6.0" + rev: "24.2.0" hooks: - id: black - repo: https://github.com/pre-commit/pre-commit-hooks @@ -17,16 +17,16 @@ repos: args: - -b main - repo: https://github.com/PyCQA/flake8 - rev: "4.0.1" + rev: "7.0.0" hooks: - id: flake8 - repo: https://github.com/PyCQA/isort - rev: "5.11.5" + rev: "5.13.2" hooks: - id: isort args: ["--profile", "black", "--filter-files"] # - repo: https://github.com/pre-commit/mirrors-mypy -# rev: v0.961 +# rev: "1.9.0" # hooks: # - id: mypy # additional_dependencies: From ca285080bf226d74bc33ccd89c2ab68a35654101 Mon Sep 17 00:00:00 2001 From: Ryan O'Horo <10855297+ryanohoro@users.noreply.github.com> Date: Sat, 9 Mar 2024 00:44:36 -0600 Subject: [PATCH 8/8] Formatting --- src/python/strelka/scanners/scan_docx.py | 18 ++++----- src/python/strelka/scanners/scan_iso.py | 26 ++++++------- src/python/strelka/scanners/scan_lnk.py | 12 +++--- src/python/strelka/scanners/scan_pe.py | 24 ++++++------ src/python/strelka/scanners/scan_pgp.py | 24 ++++++------ src/python/strelka/scanners/scan_rar.py | 47 +++++++++++++++--------- 6 files changed, 81 insertions(+), 70 deletions(-) diff --git a/src/python/strelka/scanners/scan_docx.py b/src/python/strelka/scanners/scan_docx.py index 738806d2..ae1baabd 100644 --- a/src/python/strelka/scanners/scan_docx.py +++ b/src/python/strelka/scanners/scan_docx.py @@ -30,17 +30,17 @@ def scan(self, data, file, options, expire_at): self.event["identifier"] = docx_doc.core_properties.identifier self.event["keywords"] = docx_doc.core_properties.keywords self.event["language"] = docx_doc.core_properties.language - self.event[ - "last_modified_by" - ] = docx_doc.core_properties.last_modified_by + self.event["last_modified_by"] = ( + docx_doc.core_properties.last_modified_by + ) if docx_doc.core_properties.last_printed is not None: - self.event[ - "last_printed" - ] = docx_doc.core_properties.last_printed.isoformat() + self.event["last_printed"] = ( + docx_doc.core_properties.last_printed.isoformat() + ) if docx_doc.core_properties.modified is not None: - self.event[ - "modified" - ] = docx_doc.core_properties.modified.isoformat() + self.event["modified"] = ( + docx_doc.core_properties.modified.isoformat() + ) self.event["revision"] = docx_doc.core_properties.revision self.event["subject"] = docx_doc.core_properties.subject self.event["title"] = docx_doc.core_properties.title diff --git a/src/python/strelka/scanners/scan_iso.py b/src/python/strelka/scanners/scan_iso.py index 9e825245..b949e5cc 100644 --- a/src/python/strelka/scanners/scan_iso.py +++ b/src/python/strelka/scanners/scan_iso.py @@ -27,19 +27,19 @@ def scan(self, data, file, options, expire_at): # Attempt to get Meta try: - self.event["meta"][ - "date_created" - ] = self._datetime_from_volume_date(iso.pvd.volume_creation_date) - self.event["meta"][ - "date_effective" - ] = self._datetime_from_volume_date(iso.pvd.volume_effective_date) - self.event["meta"][ - "date_expiration" - ] = self._datetime_from_volume_date(iso.pvd.volume_expiration_date) - self.event["meta"][ - "date_modification" - ] = self._datetime_from_volume_date( - iso.pvd.volume_modification_date + self.event["meta"]["date_created"] = ( + self._datetime_from_volume_date(iso.pvd.volume_creation_date) + ) + self.event["meta"]["date_effective"] = ( + self._datetime_from_volume_date(iso.pvd.volume_effective_date) + ) + self.event["meta"]["date_expiration"] = ( + self._datetime_from_volume_date(iso.pvd.volume_expiration_date) + ) + self.event["meta"]["date_modification"] = ( + self._datetime_from_volume_date( + iso.pvd.volume_modification_date + ) ) self.event["meta"][ "volume_identifier" diff --git a/src/python/strelka/scanners/scan_lnk.py b/src/python/strelka/scanners/scan_lnk.py index ed405d0d..7759529d 100644 --- a/src/python/strelka/scanners/scan_lnk.py +++ b/src/python/strelka/scanners/scan_lnk.py @@ -140,18 +140,18 @@ def scan(self, data, file, options, expire_at): try: if extradata.IconEnvironmentDataBlock: - self.event[ - "icon_target" - ] = extradata.IconEnvironmentDataBlock.TargetAnsi + self.event["icon_target"] = ( + extradata.IconEnvironmentDataBlock.TargetAnsi + ) except strelka.ScannerTimeout: raise except Exception: self.flags.append("Unable to parse IconEnvironmentDataBlock") if extradata.TrackerDataBlock: - self.event[ - "machine_id" - ] = extradata.TrackerDataBlock.MachineID.strip(b"\x00") + self.event["machine_id"] = ( + extradata.TrackerDataBlock.MachineID.strip(b"\x00") + ) self.event["mac"] = str( uuid.UUID(bytes_le=extradata.TrackerDataBlock.Droid[16:]) ).split("-")[-1] diff --git a/src/python/strelka/scanners/scan_pe.py b/src/python/strelka/scanners/scan_pe.py index 32565e5b..fa8dd84b 100644 --- a/src/python/strelka/scanners/scan_pe.py +++ b/src/python/strelka/scanners/scan_pe.py @@ -532,18 +532,18 @@ def scan(self, data, file, options, expire_at): self.event["address_of_entry_point"] = pe.OPTIONAL_HEADER.AddressOfEntryPoint self.event["image_base"] = pe.OPTIONAL_HEADER.ImageBase self.event["size_of_code"] = pe.OPTIONAL_HEADER.SizeOfCode - self.event[ - "size_of_initialized_data" - ] = pe.OPTIONAL_HEADER.SizeOfInitializedData + self.event["size_of_initialized_data"] = ( + pe.OPTIONAL_HEADER.SizeOfInitializedData + ) self.event["size_of_headers"] = pe.OPTIONAL_HEADER.SizeOfHeaders self.event["size_of_heap_reserve"] = pe.OPTIONAL_HEADER.SizeOfHeapReserve self.event["size_of_image"] = pe.OPTIONAL_HEADER.SizeOfImage self.event["size_of_stack_commit"] = pe.OPTIONAL_HEADER.SizeOfStackCommit self.event["size_of_stack_reserve"] = pe.OPTIONAL_HEADER.SizeOfStackReserve self.event["size_of_heap_commit"] = pe.OPTIONAL_HEADER.SizeOfHeapCommit - self.event[ - "size_of_uninitialized_data" - ] = pe.OPTIONAL_HEADER.SizeOfUninitializedData + self.event["size_of_uninitialized_data"] = ( + pe.OPTIONAL_HEADER.SizeOfUninitializedData + ) self.event["file_alignment"] = pe.OPTIONAL_HEADER.FileAlignment self.event["section_alignment"] = pe.OPTIONAL_HEADER.SectionAlignment self.event["checksum"] = pe.OPTIONAL_HEADER.CheckSum @@ -552,12 +552,12 @@ def scan(self, data, file, options, expire_at): self.event["minor_image_version"] = pe.OPTIONAL_HEADER.MinorImageVersion self.event["major_linker_version"] = pe.OPTIONAL_HEADER.MajorLinkerVersion self.event["minor_linker_version"] = pe.OPTIONAL_HEADER.MinorLinkerVersion - self.event[ - "major_operating_system_version" - ] = pe.OPTIONAL_HEADER.MajorOperatingSystemVersion - self.event[ - "minor_operating_system_version" - ] = pe.OPTIONAL_HEADER.MinorOperatingSystemVersion + self.event["major_operating_system_version"] = ( + pe.OPTIONAL_HEADER.MajorOperatingSystemVersion + ) + self.event["minor_operating_system_version"] = ( + pe.OPTIONAL_HEADER.MinorOperatingSystemVersion + ) self.event["major_subsystem_version"] = pe.OPTIONAL_HEADER.MajorSubsystemVersion self.event["minor_subsystem_version"] = pe.OPTIONAL_HEADER.MinorSubsystemVersion self.event["image_version"] = float( diff --git a/src/python/strelka/scanners/scan_pgp.py b/src/python/strelka/scanners/scan_pgp.py index 1a9f4e7a..7c3259d4 100644 --- a/src/python/strelka/scanners/scan_pgp.py +++ b/src/python/strelka/scanners/scan_pgp.py @@ -74,9 +74,9 @@ def parse_pgpdump(self, data): secret_key_entry["creation_time"] = creation_time.isoformat() expiration_time = getattr(packet, "expiration_time", None) if expiration_time is not None: - secret_key_entry[ - "expiration_time" - ] = expiration_time.isoformat() + secret_key_entry["expiration_time"] = ( + expiration_time.isoformat() + ) if secret_key_entry not in self.event["secret_keys"]: self.event["secret_keys"].append(secret_key_entry) @@ -98,9 +98,9 @@ def parse_pgpdump(self, data): public_key_entry["creation_time"] = creation_time.isoformat() expiration_time = getattr(packet, "expiration_time", None) if expiration_time is not None: - public_key_entry[ - "expiration_time" - ] = expiration_time.isoformat() + public_key_entry["expiration_time"] = ( + expiration_time.isoformat() + ) if public_key_entry not in self.event["public_keys"]: self.event["public_keys"].append(public_key_entry) @@ -135,14 +135,14 @@ def parse_pgpdump(self, data): } creation_time = getattr(packet, "creation_time", None) if creation_time is not None: - signature_packet_entry[ - "creation_time" - ] = creation_time.isoformat() + signature_packet_entry["creation_time"] = ( + creation_time.isoformat() + ) expiration_time = getattr(packet, "expiration_time", None) if expiration_time is not None: - signature_packet_entry[ - "expiration_time" - ] = expiration_time.isoformat() + signature_packet_entry["expiration_time"] = ( + expiration_time.isoformat() + ) if signature_packet_entry not in self.event["signatures"]: self.event["signatures"].append(signature_packet_entry) diff --git a/src/python/strelka/scanners/scan_rar.py b/src/python/strelka/scanners/scan_rar.py index 0021b48d..f6124c2f 100644 --- a/src/python/strelka/scanners/scan_rar.py +++ b/src/python/strelka/scanners/scan_rar.py @@ -201,9 +201,11 @@ def scan(self, data, file, options, expire_at): rar_data_io = rar_obj.open( compressed_file, mode="r", - pwd=password.decode("utf-8") - if password - else None, + pwd=( + password.decode("utf-8") + if password + else None + ), ) if rar_data_io.readable(): extract_data = rar_data_io.readall() @@ -271,21 +273,30 @@ def scan(self, data, file, options, expire_at): { "file_name": compressed_file.filename, "datetime": compressed_file.mtime.isoformat(), - "ctime": compressed_file.ctime.isoformat() - if isinstance( - compressed_file.ctime, datetime.datetime - ) - else None, - "mtime": compressed_file.mtime.isoformat() - if isinstance( - compressed_file.mtime, datetime.datetime - ) - else None, - "atime": compressed_file.atime.isoformat() - if isinstance( - compressed_file.atime, datetime.datetime - ) - else None, + "ctime": ( + compressed_file.ctime.isoformat() + if isinstance( + compressed_file.ctime, + datetime.datetime, + ) + else None + ), + "mtime": ( + compressed_file.mtime.isoformat() + if isinstance( + compressed_file.mtime, + datetime.datetime, + ) + else None + ), + "atime": ( + compressed_file.atime.isoformat() + if isinstance( + compressed_file.atime, + datetime.datetime, + ) + else None + ), "file_size": compressed_file.file_size, "compression_size": compressed_file.compress_size, "compression_rate": round(