CVE-2025-13837 Overview
CVE-2025-13837 is a denial-of-service vulnerability in the CPython plistlib module. When parsing a Property List (plist) file, the module reads data using a size value declared inside the file itself. A malicious plist crafted with oversized length fields causes the parser to allocate arbitrary amounts of memory. The condition can lead to MemoryError exceptions, host swapping, out-of-memory (OOM) process or container termination, or system instability. The vulnerability is tracked under [CWE-400: Uncontrolled Resource Consumption] and affects Python applications that deserialize plist data from untrusted sources.
Critical Impact
A crafted plist file passed to plistlib.load() or plistlib.loads() can trigger unbounded memory allocation, resulting in process termination or denial of service on the host or container.
Affected Products
- Python CPython 3.15.0 alpha1
- Python CPython 3.15.0 alpha2
- Python branches receiving the gh-119342 backport (3.10, 3.12, and main)
Discovery Timeline
- 2025-12-01 - CVE-2025-13837 published to NVD
- 2026-03-03 - Last updated in NVD database
Technical Details for CVE-2025-13837
Vulnerability Analysis
The plistlib module supports both XML and binary plist formats used primarily on Apple platforms. During parsing, the module trusts length and size fields embedded within the input file and uses them to pre-allocate buffers. There is no upper bound check against the actual file size or available memory before the allocation occurs. An attacker who can supply or influence a plist file processed by a Python application can declare an extremely large object length, forcing the interpreter to attempt allocation of gigabytes of memory.
The practical impact is process-level denial of service. In containerized environments with memory limits, the OOM killer terminates the affected workload. On unconstrained hosts, swapping and degraded performance affect co-located services. Because plist parsing is commonly used in macOS automation, iOS development tooling, and CI/CD pipelines, server-side processing of attacker-controlled plist files presents a realistic exposure path.
Root Cause
The root cause is the absence of incremental, chunked reads in the binary plist reader. The parser allocates a single buffer matching the size declared in the file header, with no sanity check against the file's actual length on disk. This pattern matches CWE-400 (Uncontrolled Resource Consumption).
Attack Vector
Exploitation requires local access to deliver a crafted plist file to a target Python process. Any tool or service that calls plistlib.load, plistlib.loads, or plistlib.readPlist on untrusted input is vulnerable. No authentication or user interaction is required beyond delivering the file to the parser.
# Patch excerpt from Lib/plistlib.py (CPython gh-119343)
PlistFormat = enum.Enum('PlistFormat', 'FMT_XML FMT_BINARY', module=__name__)
globals().update(PlistFormat.__members__)
# Data larger than this will be read in chunks, to prevent extreme
# overallocation.
_MIN_READ_BUF_SIZE = 1 << 20
class UID:
def __init__(self, data):
...
# Source: https://github.com/python/cpython/commit/694922cf40aa3a28f898b5f5ee08b71b4922df70
The fix introduces a _MIN_READ_BUF_SIZE constant (1 MiB) so that large declared sizes are satisfied by chunked reads rather than a single large allocation.
Detection Methods for CVE-2025-13837
Indicators of Compromise
- Python processes terminating with MemoryError tracebacks referencing plistlib functions in logs.
- Linux OOM killer events (oom-kill in dmesg or journald) targeting Python interpreters processing plist input.
- Sudden memory consumption spikes on services that ingest plist files from upload endpoints, mail attachments, or shared storage.
Detection Strategies
- Inspect application logs for stack traces containing plistlib._BinaryPlistParser or plistlib.load followed by memory exceptions.
- Audit codebases for calls to plistlib.load(), plistlib.loads(), and the deprecated plistlib.readPlist() operating on network or user-supplied data.
- Flag plist files where the declared object table size is disproportionate to the on-disk file size during pre-processing.
Monitoring Recommendations
- Establish memory utilization baselines for Python services that parse plist content and alert on anomalous growth.
- Monitor container restart counts and OOMKilled status codes in Kubernetes (kubectl get pods with lastState.terminated.reason=OOMKilled).
- Log file sizes and parser outcomes for any service exposed to externally sourced plist data.
How to Mitigate CVE-2025-13837
Immediate Actions Required
- Upgrade to the patched CPython release for your branch once available; pull the fix from commits 694922cf, 5a8b1967, 71fa8eb8, or b64441e4.
- Inventory all services that deserialize plist data and validate file sizes before passing input to plistlib.
- Enforce memory limits (cgroups, container resources.limits.memory, ulimit -v) on processes that handle untrusted plist files.
Patch Information
The upstream fix is tracked as gh-119342 with pull request GH-119343. The patch adds a _MIN_READ_BUF_SIZE of 1 MiB to chunk large reads inside Lib/plistlib.py. See the Python Security Announcement for distribution details.
Workarounds
- Reject plist inputs that exceed a maximum size threshold (for example, 10 MiB) before invoking the parser.
- Run plist parsing in a sandboxed subprocess with strict memory limits so allocation failures do not affect the parent service.
- Authenticate and validate the origin of plist files; treat any file from external users, email, or third-party APIs as untrusted.
# Configuration example: cap memory for a plist-processing worker
systemd-run --scope -p MemoryMax=512M -p MemorySwapMax=0 \
/usr/bin/python3 -m my_plist_worker /path/to/input.plist
# Kubernetes: enforce a memory limit on the parsing container
# resources:
# limits:
# memory: "512Mi"
# requests:
# memory: "128Mi"
Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.


