CVEReports
Reports
CVEReports

Automated vulnerability intelligence platform. Comprehensive reports for high-severity CVEs generated by AI.

Product

  • Home
  • Reports
  • Sitemap
  • RSS Feed

Company

  • About
  • Privacy Policy
  • Terms of Service

© 2026 CVEReports. All rights reserved.

Powered by Google Gemini & CVE Feed

|
•

GHSA-84R2-JW7C-4R5Q
CVSS 9.8

Pickle Me This: How a 'Safe' Scanner Became an RCE Delivery System

Amit Schendel
Amit Schendel
Senior Security Researcher•January 3, 2026•7 min read
PoC Available

Executive Summary (TL;DR)

A security tool named `picklescan`, designed to find dangerous Python pickle files, had a critical command injection flaw. When scanning a ZIP file, it used an unsanitized internal filename to build a shell command, allowing an attacker who controls the filename to achieve remote code execution. The fix was a one-line change to properly quote the filename. Anyone using `picklescan` versions before 1.0.1 should upgrade immediately.

The picklescan library, a tool designed to safely inspect Python pickle files for malicious content without the risks of deserialization, ironically contained a critical command injection vulnerability. By crafting a malicious ZIP archive with a specially named file, an attacker could execute arbitrary commands on the machine running the scan. This vulnerability turns a security tool into a weapon, achieving remote code execution by exploiting the very mechanism meant to provide safety.

The Illusion of Safety

Python's pickle module is the digital equivalent of a mystery box from a stranger. It's a powerful serialization format, but with great power comes the terrifying ability to execute arbitrary code upon deserialization. The official documentation itself screams warnings at you, advising to never, ever unpickle data from an untrusted source. It's the boogeyman of Python security, responsible for countless RCEs over the years.

Enter picklescan, our supposed hero. Its mission, which it chose to accept, was to tame this beast. Instead of recklessly unpickling data, picklescan performs static analysis, scanning the raw byte stream for dangerous opcodes like os.system without actually executing anything. It was designed to be the security guard that checks for weapons before letting anyone into the building.

The irony, it seems, is a cruel mistress. The very tool built on the premise of 'safe scanning' harbored a vulnerability just as old and treacherous as the one it was designed to fight. The guard wasn't just asleep at his post; he was actively helping the intruder build a bomb, all because he trusted a piece of paper the intruder handed him.

A Trusting Heart and a Shell Command

The fatal flaw wasn't in the pickle analysis logic itself, but in a seemingly innocuous feature: scanning inside compressed archives. To inspect a file within a ZIP archive, picklescan cleverly uses a shell pipeline. It calls the unzip command to extract the file's contents to standard output and pipes it directly into grep to look for malicious opcodes. This is efficient, avoiding the need to write temporary files to disk.

Herein lies the original sin of so many vulnerabilities: building shell commands with untrusted strings. The developer correctly used shlex.quote on the main archive's path, protecting it from injection. However, they forgot to apply the same protection to the filename of the member inside the archive. An attacker has complete control over the filenames of files they place in a ZIP archive.

This oversight creates a classic command injection vulnerability (CWE-78). The unsanitized filename is concatenated directly into the command string passed to the shell. A filename is no longer just a name; it's a potential command payload. The developer locked the front door (file_path) but left a gaping hole in the wall (member.filename).

The Smoking Gun

Code tells a story, and this one is a tragedy in a single line. Let's look at the vulnerable piece of code from the scan_zip_file function in picklescan/scanner.py. The evidence is damning.

# The vulnerable line of code
scan_archive_cmd = f"unzip -p {shlex.quote(file_path)} {member.filename} | grep -aoE '({dangerous_opcode_re})'"

Notice the beautiful, secure shlex.quote(file_path). The developer was so close. But right next to it, member.filename sits naked and exposed, ready to be manipulated. The shell sees this string and interprets any special characters ($, ;, |, `) as instructions, not as part of a literal filename. It's a rookie mistake, but one that appears with depressing regularity.

The fix is as simple as it is obvious. It's the kind of change that makes you stare at the ceiling at 3 AM, wondering about the fragility of it all. One small addition was all it took to close the hole.

# The patched line of code
import shlex
scan_archive_cmd = f"unzip -p {shlex.quote(file_path)} {shlex.quote(member.filename)} | grep -aoE '({dangerous_opcode_re})'"

By wrapping member.filename in shlex.quote(), the string is properly escaped. A filename like pwn.pkl; id becomes 'pwn.pkl; id', which the shell now correctly interprets as a single, albeit weirdly named, file. The attack is completely neutralized. This patch highlights a critical lesson: sanitize all external input, especially when it's destined for a command interpreter.

Crafting the Trojan ZIP

Exploiting this is trivially easy, which is what makes it so severe. An attacker doesn't need complex memory corruption skills; they just need to know how a shell works and how to create a ZIP file. It's a low-skill, high-impact affair.

Here is the attack plan, step-by-step:

  1. Craft the Malicious Filename: The goal is to terminate the unzip command and inject our own. A filename like exploit.pkl; whoami > /tmp/proof; # is perfect. The semicolon ends the previous command, whoami executes, its output is redirected, and the # comments out the rest of the original command string to avoid syntax errors.
  2. Create the Payload File: Create a placeholder file with any content. It can be an empty file or a legitimate-looking pickle file to avoid suspicion. Let's call it payload.txt for now.
  3. Build the ZIP Archive: Now, create a ZIP file and add the payload file, but rename it to our malicious filename during the process. Using standard command-line tools:
    touch payload.txt
    zip malicious.zip "exploit.pkl; whoami > /tmp/proof; #"=payload.txt
  4. Deliver the Payload: The malicious.zip file is now the weapon. It needs to be sent to a service that uses a vulnerable version of picklescan. This could be a file upload endpoint, a machine learning model repository, or any automated analysis pipeline.

When picklescan processes this ZIP file, the vulnerable code will construct and execute the following shell command: unzip -p '/path/to/malicious.zip' exploit.pkl; whoami > /tmp/proof; # | grep -aoE '...'

The shell executes unzip, then whoami, and the grep command is ignored. The system is now compromised.

The Scanner Gets Scanned

The impact of this vulnerability is catastrophic, precisely because of the context in which picklescan is used. This isn't a client-side bug in some desktop application; it's a flaw in a server-side security tool. Systems running this code are explicitly processing untrusted, potentially malicious files. They are on the front lines.

A successful exploit grants the attacker Remote Code Execution on the server. From there, the playbook is wide open. An attacker can steal sensitive data, deploy ransomware, install persistent backdoors, or pivot deeper into the victim's network. The server running the scanner becomes a beachhead for a full-scale invasion.

What's worse is the betrayal of trust. Developers use a tool named picklescan because they are trying to improve their security posture. They are doing the right thing. This vulnerability turns their diligence against them, making the security measure the very vector of compromise. It's a painful reminder that every dependency, even a security-focused one, is a potential attack surface.

Applying the Band-Aid

Fortunately, the fix is straightforward and should be applied yesterday. There is no good reason to be running a vulnerable version. The path to redemption is a single command.

[!IMPORTANT] The primary remediation is to upgrade to version 1.0.1 or newer of picklescan.

pip install --upgrade picklescan

For those in bizarre corporate environments where patching is a multi-year bureaucratic process, other mitigation strategies exist, but they are poor substitutes. You could, for instance, run picklescan in a heavily restricted Docker container with no network access and minimal permissions. This contains the blast but doesn't defuse the bomb. Another option is to sanitize archive filenames before passing them to picklescan, but this just moves the problem and risks implementing a faulty sanitization routine yourself.

Ultimately, the lesson is clear and has been for decades: Never, ever build commands by concatenating strings with untrusted data. Use argument arrays or shell-safe quoting libraries (shlex in Python) religiously. Treat every piece of external data, including metadata like filenames, as if it were crafted by your worst enemy, because one day, it will be.

Fix Analysis (1)

Technical Appendix

CVSS Score
9.8/ 10
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H

Affected Systems

Python applications and services that use the picklescan library versions prior to 1.0.1 to scan ZIP archives.

Affected Versions Detail

ProductAffected VersionsFixed Version
picklescan
mmaitre314
< 1.0.11.0.1
AttributeDetail
CWE IDCWE-78
CWE NameImproper Neutralization of Special Elements used in a Command ('Command Injection')
Attack VectorNetwork / File Upload
CVSS 3.1 Score9.8 (Critical)
CVSS VectorCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
ImpactRemote Code Execution
Exploit StatusProof-of-Concept Available
KEV StatusNot Listed

MITRE ATT&CK Mapping

MITRE ATT&CK Mapping

T1190Exploit Public-Facing Application
Initial Access
T1059.004Command and Scripting Interpreter: Unix Shell
Execution
CWE-78
Improper Neutralization of Special Elements used in a Command ('Command Injection')

References & Sources

  • [1]GitHub Advisory: Command injection in picklescan
  • [2]Fix Commit for GHSA-84r2-jw7c-4r5q
  • [3]CWE-78: Improper Neutralization of Special Elements used in a Command ('Command Injection')

Subscribe to updates

Get the latest CVE analysis reports delivered to your inbox.

Attack Flow Diagram

Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.
Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.