CVE-2023-50447 - Breaking Down the Pillow PIL.ImageMath.eval Arbitrary Code Execution Vulnerability
In December 2023, security researchers revealed a serious vulnerability in the popular Python imaging library Pillow (a fork of PIL). Tracked as CVE-2023-50447, this bug impacts Pillow up to version 10.1. and permits arbitrary code execution through the manipulation of the environment parameter in the function PIL.ImageMath.eval. Unlike the earlier CVE-2022-22817 (which dealt with the unsafe expression argument), this new flaw targets a different input pathway, potentially allowing attackers to run malicious Python code on vulnerable systems.
In this post, we’ll dive into what this CVE means, how the exploit works (with code snippets!), and how to protect your applications if you use Pillow.
What is Pillow, and what’s ImageMath.eval?
Pillow is a widely used Python library for opening, manipulating, and saving different image file formats. Many websites, tools, and machine learning projects use Pillow for processing images. The ImageMath.eval() function evaluates mathematical expressions on images—but it does so by executing Python code passed as strings.
Here’s a basic (safe) example
from PIL import Image, ImageMath
im = Image.open("cat.jpg")
result = ImageMath.eval("convert(a + 10, 'L')", a=im)
result.show()
However, not all ways of using eval() are safe—especially when user inputs are involved.
The Vulnerability: Exploiting the environment Parameter
The problem lies in how ImageMath.eval handles the optional environment parameter, which lets callers supply a dictionary of custom variables and functions the expression can use.
CVE-2023-50447 arises because there is no proper sanitization or restriction on what you put in the environment. Malicious code can be placed there, which gets evaluated. For example, if an attacker can control or inject the environment, they could run os.system("evil command") or import Python modules to do almost anything.
Suppose you trust the image but allow the user to specify variables or functions via environment
from PIL import ImageMath
environment = {'os': __import__('os')}
ImageMath.eval("os.system('touch /tmp/pwned')", environment=environment)
This would execute the shell command touch /tmp/pwned.
Even without direct control over the expression, a crafty attacker might sneak dangerous objects into environment and call them through indirect references!
Here’s a self-contained proof-of-concept exploiting the vulnerability in Pillow ≤10.1.
from PIL import ImageMath
# Malicious environment dictionary
mal_env = {'__builtins__': __import__('builtins'), 'os': __import__('os')}
# Expression that uses the environment
payload = "os.system('echo HACKED > /tmp/hacked.txt')"
ImageMath.eval(payload, environment=mal_env)
What does this do?
- It adds both Python’s builtins and os modules into the environment used by the evaluated expression.
- The expression runs os.system, which creates a file /tmp/hacked.txt (or does something more harmful in real attacks).
The key takeaway:
If environment contains objects like os, sys, or builtins, you can escape the intended math world and run arbitrary Python code.
Official CVE entry:
https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2023-50447
Pillow Project's Security Advisory:
https://github.com/python-pillow/Pillow/security/advisories/GHSA-8mf4-68gx-75f4
Relevant Python code in Pillow source:
https://github.com/python-pillow/Pillow/blob/10.1./src/PIL/ImageMath.py#L274
Reference commit fixing the issue:
https://github.com/python-pillow/Pillow/commit/f04641d5a672e0476bf982344226b4294deeced
Upgrade Pillow:
Update to the latest version of Pillow (10.2. or newer), which disables the dangerous use of environment by default and blocks unsafe objects.
pip install --upgrade Pillow
<br><br>- <b>Never pass untrusted input to ImageMath.eval:</b> <br> Only use hardcoded or tightly controlled variables and functions in environment.<br><br>- <b>Audit Your Code:</b> <br> If you have custom code passing user input or external data to eval() or environment`, review and refactor.
---
## Summary
CVE-2023-50447 is a big deal because it’s so easy to weaponize, and it’s different from previous bugs like CVE-2022-22817. If you run a website, processing pipeline, or any automated setup with Pillow, patch as soon as possible. Arbitrary code execution means attackers can do practically anything on your server.
Keep your dependencies up to date, and always treat anything that evaluates Python code as potentially dangerous!
---
*This summary is based on public information and direct study of the affected code. For the latest advice, always check the Pillow project’s own documentation and security notices.*
Timeline
Published on: 01/19/2024 20:15:11 UTC
Last modified on: 03/27/2024 21:15:48 UTC