CVE-2026-49836
Python vulnerability analysis and mitigation

Overview

CVE-2026-49836 is a path traversal vulnerability in the Python library psd-tools that enables arbitrary file write and read via attacker-controlled smart-object filenames embedded in PSD files. It affects all releases of psd-tools exposing the SmartObject API through v1.17.0 (pip package). The vulnerability was reported on May 25, 2026, published by maintainer kyamagu on June 2, 2026, and added to the GitHub Advisory Database on July 9, 2026. It carries a CVSS v4.0 base score of 4.6 (Medium) (GitHub Advisory, psd-tools Advisory).

Technical details

The root cause is improper limitation of a pathname to a restricted directory (CWE-22) combined with external control of file name or path (CWE-73). In SmartObject.save() (src/psd_tools/api/smart_object.py:170-179, tag v1.17.0), when no explicit filename is provided, the method uses self.filename verbatim — a value read directly from the PSD file's linked-layer block via read_unicode_string() with no sanitization, no os.path.basename() stripping, no absolute-path rejection, and no ../ filtering. This allows an attacker-crafted PSD to specify a filename like ../../PWNED.bin or /etc/cron.d/backdoor, causing the library to write attacker-controlled bytes to an arbitrary path. A secondary issue in SmartObject.open() for external-kind smart objects allows the fullPath descriptor embedded in the PSD to be used as an arbitrary file read source, enabling exfiltration of sensitive files (e.g., /etc/passwd) to the attacker-controlled write destination. A public proof-of-concept script is included in the advisory (GitHub Advisory, Fix PR #657).

Impact

Successful exploitation allows an attacker who can supply a malicious .psd file to write arbitrary bytes to any filesystem path writable by the process running psd-tools, including configuration files, cron jobs, or shell initialization scripts — potentially escalating to remote code execution. The secondary read-path issue additionally enables exfiltration of any file readable by the process (e.g., credentials, private keys, /etc/passwd) by embedding a malicious fullPath in an external-kind smart object. The severity is higher for services that automatically extract smart objects from user-uploaded PSD files without user interaction, as no authentication or special configuration is required for exploitation (GitHub Advisory, psd-tools Advisory).

Exploitability

A standalone proof-of-concept script is publicly documented in the advisory, demonstrating confirmed arbitrary file write outside the intended output directory using psd-tools==1.17.0. Exploitation requires no privileges and no special configuration — only that a victim application opens an attacker-supplied PSD and calls SmartObject.save(). There is no evidence of in-the-wild exploitation, no known threat actor attribution, and the CVE status remains "Reserved" as of the advisory publication date. No EPSS score or CISA KEV catalog entry is currently available for this CVE (GitHub Advisory, psd-tools Advisory).

Exploitation steps

  1. Craft a malicious PSD file: Starting from a valid PSD containing a smart object layer, modify the embedded smart-object filename in the linked-layer block (parsed from src/psd_tools/psd/linked_layer.py:100) to a path-traversing value such as ../../PWNED.bin or an absolute path like /etc/cron.d/backdoor. The embedded data payload (attacker-controlled bytes) is set to the desired file content.
  2. Deliver the malicious PSD: Distribute the crafted .psd file to a target application that processes untrusted PSD files — e.g., upload it to a web service that extracts smart objects, or send it to a user of a desktop tool.
  3. Trigger smart object extraction: The victim application opens the PSD with PSDImage.open() and iterates over layers, calling l.smart_object.save() for smart object layers — a common pattern for extracting embedded assets.
  4. Achieve arbitrary file write: Because SmartObject.save() uses self.filename verbatim with no path sanitization, the library writes the attacker-controlled bytes to the attacker-specified path (e.g., ../../PWNED.bin resolves outside the output directory). An absolute path like /home/user/.bashrc is honored identically.
  5. Optional — arbitrary file read (external-kind): For external-kind smart objects, embed a malicious fullPath (e.g., /etc/passwd) in the PSD descriptor. When save() reads content via the data property, it calls open() with no external_dir constraint, reading the target file and writing its contents to the controlled output directory, enabling exfiltration (GitHub Advisory, psd-tools Advisory).

Indicators of compromise

  • File System: Unexpected files appearing outside the designated PSD extraction/output directory, particularly with names matching embedded smart-object filenames from processed PSD files; files with binary content matching PSD embedded data written to sensitive paths (e.g., /etc/, home directories, cron directories).
  • File System: Sensitive system files (e.g., /etc/passwd, SSH keys, application config files) appearing in the PSD output/extraction directory, indicating the read-path exploit was triggered.
  • Logs: Application logs showing psd-tools processing of externally supplied .psd files followed by unexpected file creation events in parent directories or system paths.
  • Process: File write system calls (e.g., open() with wb mode) originating from a Python process running psd-tools, targeting paths outside the expected output directory — detectable via auditd or similar file integrity monitoring tools.

Mitigation and workarounds

Upgrade psd-tools to v1.17.1 or later, which fixes both the write and read path traversal issues via PR #657. The patch applies os.path.basename() to strip directory components from embedded filenames, enforces output containment using os.path.realpath() and os.path.commonpath(), adds an external_dir parameter to SmartObject.open() to constrain external-kind read sources, and explicitly rejects empty or dot basenames. No configuration-based workaround is available for v1.17.0 and earlier; the only safe remediation is upgrading. Applications that cannot upgrade immediately should avoid calling SmartObject.save() on PSD files from untrusted sources (psd-tools Release v1.17.1, Fix PR #657).

Additional resources

Linux Distribution fix status

Fix availability across major Linux distributions and their releases.

Debian

Fixed

bookworm

psd-tools

Affected

sid

psd-tools: 1.17.4+dfsg.1-1

Fixed

trixie

psd-tools

Affected

SourceThis report was generated using AI

Related Python vulnerabilities:

CVE ID

Severity

Score

Technologies

Component name

CISA KEV exploit

Has fix

Published date

CVE-2026-59971CRITICAL10
  • Python logoPython
  • mysql-mcp-server
NoYesSep 11, 2026
CVE-2026-59177HIGH8.8
  • Python logoPython
  • esphome-device-builder
NoYesSep 09, 2026
CVE-2026-88006MEDIUM6.5
  • Python logoPython
  • open-webui
NoYesSep 10, 2026
CVE-2026-88005MEDIUM6.5
  • Python logoPython
  • open-webui
NoYesSep 10, 2026
CVE-2026-49836MEDIUM4.6
  • Python logoPython
  • psd-tools
NoYesSep 10, 2026

Free Vulnerability Assessment

Benchmark your Cloud Security Posture

Evaluate your cloud security practices across 9 security domains to benchmark your risk level and identify gaps in your defenses.

Request assessment

Get a personalized demo

Ready to see Wiz in action?

"Best User Experience I have ever seen, provides full visibility to cloud workloads."
David EstlickCISO
"Wiz provides a single pane of glass to see what is going on in our cloud environments."
Adam FletcherChief Security Officer
"We know that if Wiz identifies something as critical, it actually is."
Greg PoniatowskiHead of Threat and Vulnerability Management