Root cause
Other / insufficient public mechanism detail introduced
fix: Add Python 3.13 compatibility for Whirlpool hash module
AI-assisted change: Claude
How AI contributed: Direct introductionCriticalPublished Aug 17, 2026 in
jahlives/openssl_encryptAll 20 findings in this repository
openssl_encrypt versions before 1.4.0 contain an arbitrary code execution vulnerability in the Whirlpool hash implementation that uses broad glob patterns to load .so modules without integrity verification. Attackers can place malicious .so files matching the whirlpool*py313*.so pattern in site-packages directories to achieve native code execution when the module is loaded.
The Whirlpool fallback added to crypt_core.py scans site-packages for native modules matching the whirlpool and py313 name pattern, takes the first hit, and executes it with ExtensionFileLoader.exec_module without verifying integrity or ownership, so a planted module runs native code whenever Whirlpool is probed.
Root cause
fix: Add Python 3.13 compatibility for Whirlpool hash module
AI-assisted change: Claude
Fix status
No minimum fix commit has been established for this finding.
Fix status unresolved
Case facts
@@ -1,6 +1,8 @@-from setuptools import setup, find_packages+from setuptools import setup, find_packages, Command import os import subprocess+import sys+import logging # Read the contents of your README file@@ -46,18 +48,45 @@ ''') +# Custom install command that runs setup_whirlpool after installation+class PostInstallCommand(Command):+ description = "Command to run post install tasks"+ user_options = []+ + def initialize_options(self):+ pass+ + def finalize_options(self):+ pass+ + def run(self):+ try:+ # Run the post-install script+ import subprocess+ subprocess.check_call([sys.executable, "-m", "openssl_encrypt.post_install"])+ except Exception as e:+ print(f"Warning: Failed to run post-install setup: {e}")+ print("You may need to manually install Whirlpool: pip install whirlpool-py311")++ setup(+ cmdclass={+ 'post_install': PostInstallCommand,+ }, name="openssl_encrypt", version=VERSION, install_requires=[- "cryptography>=42.0.0,<43.0.0",- "argon2-cffi>=23.1.0,<24.0.0",- "pywin32>=306,<307; sys_platform == 'win32'",+ "cryptography>=42.0.0,<43.0.0",+ "argon2-cffi>=23.1.0,<24.0.0",+ "pywin32>=306,<307; sys_platform == 'win32'", "PyYAML",+ "Whirlpool; python_version < '3.11'",+ "whirlpool-py311; python_version >= '3.11'", ], entry_points={ 'console_scripts': [ 'openssl-encrypt=openssl_encrypt.cli:main',+ 'whirlpool-setup=openssl_encrypt.modules.setup_whirlpool:setup_whirlpool', ], }, extras_require={@@ -0,0 +1,203 @@+"""+Module to handle the compatibility of Whirlpool hash library across Python versions.+"""++import os+import sys+import importlib.util+import sysconfig+import site+import glob+import logging+from pathlib import Path+import subprocess+import platform++# Set up logging+logging.basicConfig(level=logging.INFO)+logger = logging.getLogger("setup_whirlpool")+++def find_whirlpool_modules():+ """Find all Whirlpool modules installed in the system."""+ whirlpool_modules = []+ + # Get all site-packages directories+ site_packages = set()+ site_packages.add(sysconfig.get_path('purelib'))+ site_packages.add(sysconfig.get_path('platlib'))+ + # Add user site-packages+ user_site = site.getusersitepackages()+ if isinstance(user_site, str):+ site_packages.add(user_site)+ elif isinstance(user_site, list):+ site_packages.update(user_site)+ + # Check for modules in all site-packages+ for site_pkg in site_packages:+ if not os.path.exists(site_pkg):+ continue+ + # Look for any whirlpool-related files+ whirlpool_files = []+ pattern = os.path.join(site_pkg, "whirlpool*.so")+ whirlpool_files.extend(glob.glob(pattern))+ + pattern = os.path.join(site_pkg, "pywhirlpool*.so")+ whirlpool_files.extend(glob.glob(pattern))+ + # For Python 3.11+, look for the py311 variant+ pattern = os.path.join(site_pkg, "whirlpool-py311*.so")+ whirlpool_files.extend(glob.glob(pattern))+ + # For Windows+ pattern = os.path.join(site_pkg, "whirlpool*.pyd")+ whirlpool_files.extend(glob.glob(pattern))+ + pattern = os.path.join(site_pkg, "pywhirlpool*.pyd")+ whirlpool_files.extend(glob.glob(pattern))+ + # Add all found modules to the list+ whirlpool_modules.extend(whirlpool_files)+ + return whirlpool_modules+++def create_whirlpool_symlink():+ """Create a symbolic link to the appropriate Whirlpool module."""+ whirlpool_modules = find_whirlpool_modules()+ logger.info(f"Found Whirlpool modules: {whirlpool_modules}")+ + if not whirlpool_modules:+ logger.warning("No Whirlpool modules found. Attempting to install...")+ install_whirlpool()+ # Refresh the list after installation+ whirlpool_modules = find_whirlpool_modules()+ + if not whirlpool_modules:+ logger.warning("Failed to find or install any Whirlpool modules")+ return False+ + # Get Python version+ python_version = sys.version_info+ + # Check if we already have a working module+ try:+ # Try to import whirlpool directly+ import whirlpool+ logger.info("Whirlpool module already working, no action needed")@@ -0,0 +1,41 @@+"""+Post-installation script for the openssl_encrypt package.+This script is executed after the package is installed to perform additional setup.+"""++import os+import sys+import logging++logging.basicConfig(level=logging.INFO)+logger = logging.getLogger("post_install")+++def main():+ """Main function to run post-installation tasks."""+ logger.info("Running post-installation setup for openssl_encrypt...")+ + try:+ from openssl_encrypt.modules.setup_whirlpool import setup_whirlpool+ + result = setup_whirlpool()+ if result:+ logger.info("Whirlpool module setup completed successfully.")+ else:+ logger.warning(+ "Whirlpool module setup completed with warnings. "+ "Some functionality may be limited. "+ "See documentation for manual setup instructions."+ )+ except Exception as e:+ logger.error(f"Error during Whirlpool setup: {e}")+ logger.info(+ "You may need to manually set up the Whirlpool module. "+ "See the documentation for instructions."+ )+ + logger.info("Post-installation setup complete.")+++if __name__ == "__main__":+ main()