From 9a941dc7ac6a8d1d6ce26eefe48daa006f76ac82 Mon Sep 17 00:00:00 2001 From: SG Date: Thu, 4 Jan 2024 12:07:57 -0700 Subject: [PATCH] file carve download stream AES zip (idaholab/Malcolm#288) --- Dockerfiles/file-monitor.Dockerfile | 1 - docs/file-scanning.md | 4 ++-- shared/bin/zeek_carved_http_server.py | 19 ++++--------------- 3 files changed, 6 insertions(+), 18 deletions(-) diff --git a/Dockerfiles/file-monitor.Dockerfile b/Dockerfiles/file-monitor.Dockerfile index 088db2906..2ba1f0f2b 100644 --- a/Dockerfiles/file-monitor.Dockerfile +++ b/Dockerfiles/file-monitor.Dockerfile @@ -141,7 +141,6 @@ RUN sed -i "s/main$/main contrib non-free/g" /etc/apt/sources.list.d/debian.sour clamd \ psutil \ pycryptodome \ - pyminizip \ python-magic \ stream-zip \ supervisor \ diff --git a/docs/file-scanning.md b/docs/file-scanning.md index a511f43a3..327845326 100644 --- a/docs/file-scanning.md +++ b/docs/file-scanning.md @@ -25,7 +25,7 @@ The `EXTRACTED_FILE_PRESERVATION` [environment variable in `zeek.env`](malcolm-c * `all`: preserve flagged files in `./zeek-logs/extract_files/quarantine` and all other extracted files in `./zeek-logs/extract_files/preserved` * `none`: preserve no extracted files -The `EXTRACTED_FILE_HTTP_SERVER_…` [environment variables in `zeek.env` and `zeek-secret.env`](malcolm-config.md#MalcolmConfigEnvVars) configure access to the Zeek-extracted files path through the means of a simple HTTPS directory server accessible at **https://localhost/extracted-files/** if connecting locally. Beware that Zeek-extracted files may contain malware. As such, these files may be optionally ZIP archived (with or without a password) or encrypted (to be decrypted using `openssl`, e.g., `openssl enc -aes-256-cbc -d -in example.exe.encrypted -out example.exe`) upon download. In other words: +The `EXTRACTED_FILE_HTTP_SERVER_…` [environment variables in `zeek.env` and `zeek-secret.env`](malcolm-config.md#MalcolmConfigEnvVars) configure access to the Zeek-extracted files path through the means of a simple HTTPS directory server accessible at **https://localhost/extracted-files/** if connecting locally. Beware that Zeek-extracted files may contain malware. As such, these files may be optionally ZIP archived (without a password or password-protected according to the [WinZip AES encryption specification](https://www.winzip.com/en/support/aes-encryption/)) or encrypted (to be decrypted using `openssl`, e.g., `openssl enc -aes-256-cbc -d -in example.exe.encrypted -out example.exe`) upon download. In other words: * to disable the extracted files server: - `EXTRACTED_FILE_HTTP_SERVER_ENABLE=false` @@ -34,7 +34,7 @@ The `EXTRACTED_FILE_HTTP_SERVER_…` [environment variables in `zeek.env` and `z - downloaded files are zipped, without a password: + `EXTRACTED_FILE_HTTP_SERVER_ZIP=true` + `EXTRACTED_FILE_HTTP_SERVER_KEY=` - - downloaded files are zipped, with a password: + - downloaded files are zipped, [AES-encrypted](https://www.winzip.com/en/support/aes-encryption/) with a password: + `EXTRACTED_FILE_HTTP_SERVER_ZIP=true` + `EXTRACTED_FILE_HTTP_SERVER_KEY=xxxxxxxxxxxxx` - downloaded files are OpenSSL AES-256-CBC-compatibly encrypted: diff --git a/shared/bin/zeek_carved_http_server.py b/shared/bin/zeek_carved_http_server.py index d92cf89dd..6f416f108 100755 --- a/shared/bin/zeek_carved_http_server.py +++ b/shared/bin/zeek_carved_http_server.py @@ -10,7 +10,6 @@ import argparse import hashlib import os -import pyminizip import sys from Crypto.Cipher import AES from datetime import datetime @@ -75,24 +74,14 @@ def do_GET(self): elif os.path.isfile(fullpath) or os.path.islink(fullpath): if args.zip: - # ZIP file + # ZIP file (streamed, AES-encrypted with password or unencrypted) self.send_response(200) self.send_header('Content-type', "application/zip") self.send_header('Content-Disposition', f'attachment; filename={os.path.basename(fullpath)}.zip') self.end_headers() - if args.key: - # password-protected ZIP file (temporarily persisted to disk) - with temporary_filename(suffix='.zip') as tmpFileName: - pyminizip.compress(fullpath, None, tmpFileName, args.key, 1) - with open(tmpFileName, 'rb') as f: - while chunk := f.read(65536): - self.wfile.write(chunk) - - else: - # unprotected ZIP file (streamed) - for chunk in stream_zip(LocalFilesForZip([fullpath])): - self.wfile.write(chunk) + for chunk in stream_zip(LocalFilesForZip([fullpath]), password=args.key if args.key else None): + self.wfile.write(chunk) elif args.key: # openssl-compatible encrypted file @@ -119,7 +108,7 @@ def do_GET(self): break else: - # unencrypted file + # original file, unencrypted SimpleHTTPRequestHandler.do_GET(self) else: