Attackers have begun using PyInstaller to bundle malicious Python code into Mach-O executables to deploy infostealers targeting macOS systems. This technique allows the malware to run without requiring Python installed on the system, posing a new threat to macOS security. #JamfThreatLabs #macOS
Keypoints
- New undetected macOS infostealer samples were discovered using PyInstaller to package Python code into Mach-O binaries.
- PyInstaller bundles Python bytecode and libraries into a standalone executable, enabling the malware to run without Python installed on macOS.
- The malware uses a universal FAT binary containing an arm64 slice with the embedded PyInstaller archive and an Intel slice without it.
- Dynamic analysis shows the malware executes AppleScript commands to trick users into supplying passwords and resets AppleEvent privacy permissions.
- The malware unpacks Python libraries and scripts into temporary directories that exist only during runtime to avoid persistence.
- Open-source tools like Pyinstxtractor and PyLingual were used to unpack and decompile the malware, revealing obfuscated Python code involving string reversal, base85 encoding, XOR, and zlib compression.
- The malware’s main functions include harvesting credentials via fake password prompts, executing arbitrary AppleScripts, dumping macOS Keychain items, and stealing cryptocurrency wallet data.
MITRE Techniques
- [T1059.007] Command and Scripting Interpreter: AppleScript – The malware triggers password prompt dialogs and executes arbitrary AppleScript commands to harvest credentials and manipulate system settings. (‘Triggering an AppleScript dialog, prompting the user for their password…’)
- [T1555] Credentials from Password Stores – The malware extracts sensitive data directly from the macOS Keychain to obtain stored credentials. (‘DumpKeychain(): extracts saved credentials and sensitive information directly from the macOS Keychain’)
- [T1074.002] Data Staged: Local Data Staging – The malware unpacks encrypted Python bytecode and libraries into a temporary run-time directory (_MEIxxxxxx) for execution without persistence. (‘The binary unpacks all bundled Python libraries into its temporary home directory… only exist on disk for the lifetime of the stl process.’)
- [T1027] Obfuscated Files or Information – The malicious Python payload uses multiple layers of obfuscation including string reversal, base85 encoding, XOR encryption, and compression. (‘The decompiled Python code reveals that the script is further obfuscated by a combination of: string reversal base85 encoding XOR’d bytes…’)
- [T1105] Ingress Tool Transfer – The malware communicates with a command-and-control domain ending with /connect to exfiltrate data or receive commands. (‘Communicating with a domain that ends with /connect which is seen in other stealer samples’)
Indicators of Compromise
- [File Name ] Malicious Mach-O sample named “stl” identified on VirusTotal.
- [Domain ] C2 domain with URL path ending “/connect” used for communicating and exfiltration.
- [File Hash ] Multiple PyInstaller-packed Mach-O binaries including one earliest from January 2025 (specific hashes not provided in article).

Read more: https://www.jamf.com/blog/pyinstaller-malware-jamf-threat-labs/