eval safe?” Is impossible to count. There is always someone who claims to have found a way to protect themselves from all the possible consequences of performing this function.eval() function that executes a string with code and returns the execution result: assert eval("2 + 3 * len('hello')") == 17 eval are not from a trusted source. What happens if the line we decide to feed to eval 'y is os.system('rm -rf /') ? The interpreter will honestly start the process of deleting all data from the computer, and it’s good if it runs on behalf of the least privileged user (in the following examples I will use clear ( cls if you use Windows) instead of rm -rf / so that none of the readers will accidentally did not shoot himself in the leg ).eval safe if you run it without accessing the symbols from globals . As a second (optional) argument, eval() takes a dictionary that will be used instead of the global namespace (all classes, methods, variables, etc., declared at the “upper” level, accessible from any point of the code) by the code that will be executed by eval 'om If eval is called without this argument, it uses the current global namespace into which the os module could be imported. If you pass an empty dictionary, the global namespace for eval 'a will be empty. Here such code can no longer be executed and NameError: name 'os' is not defined exception NameError: name 'os' is not defined : eval("os.system('clear')", {}) __import__ built-in function. So, the code below will work without errors: eval("__import__('os').system('clear')", {}) __builtins__ from within eval 'a, since names like __import__ are available to us because they are in the global variable __builtins__ . If we explicitly pass an empty dictionary instead, the code below can no longer be executed: eval("__import__('os').system('clear')", {'__builtins__':{}}) # NameError: name '__import__' is not defined segfault if you run it in CPython: 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,"KABOOM",(),(),(),"","",0,"" ),{} )() )() """ eval(s, {'__builtins__':{}}) ().__class__.__bases__[0] object . We cannot simply write object , since __builtins__ are empty, but we can create an empty tuple (tuple), the first base class of which is object and, walking through its properties, access the object class.object or, in other words, a list of all classes declared in the program at the moment: ().__class__.__bases__[0].__subclasses__() ALL_CLASSES for ALL_CLASSES , it will be easy to see that the expression below finds the class by its name: [c for c in ALL_CLASSES if c.__name__ == n][0] lambda n: [c for c in ALL_CLASSES if c.__name__ == n][0] eval 'a, we can neither declare a function (using def ) nor use an assignment operator to bind our lambda to any variable . (lambda fc=( lambda n: [ c for c in ALL_CLASSES if c.__name__ == n ][0] ): # fc )() code (an internal class, its instance, for example, is the property func_code a function object): fc("code")(0,0,0,0,"KABOOM",(),(),(),"","",0,"") segfault 'in CPython. " KABOOM " just looks funnier, thanks lvh for this example.code , but we cannot directly execute it. Then create a function, the code of which will be our object: fc("function")(CODE_OBJECT, {}) (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,""),{})() )() eval NOT SAFE , even if you remove access to global and embedded variables.object class to create code and function objects. In exactly the same way, you can get (and instantiate) any class that exists in the program at the time of the eval() call. s = """ [ c for c in ().__class__.__bases__[0].__subclasses__() if c.__name__ == "Quitter" ][0](0)() """ eval(s, {'__builtins__':{}}) Quitter class, which is called by the interpreter when you type quit() .eval in an empty environment, based on the fact that the code specified in the article is the entire code of our program.eval 'and in a real application, an attacker can gain access to all the classes that you use, so that its capabilities will not be limited to almost nothing.eval safe is that they are all based on the idea of “blacklists”, the idea that we need to remove access to all things that we think can be dangerous when used in eval 'e. With such a strategy, there is virtually no chance of winning, because if anything turns out to be unlawful, the system will be vulnerable.eval 'and in Python, which is another attempt to overcome this problem: >>> eval("(lambda:0).func_code", {'__builtins__':{}}) Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<string>", line 1, in <module> RuntimeError: function attributes not accessible in restricted mode __builtins__ inside eval are different from “official” - eval goes into protected mode, in which access to some dangerous properties, such as func_code for functions, is func_code . A more detailed description of this mode can be found here , but, as we have already seen above , it is not a “silver bullet” either.eval safe to make? It is hard to say. It seems to me that the attacker cannot be harmed without access to objects with two lower underscores framing the name, so it is possible if we exclude all lines with two lower underscores from processing, we will be safe. Maybe... [ c for c in ().__class__.__base__.__subclasses__() if c.__name__ == 'catch_warnings' ][0]()._module.__builtins__ Source: https://habr.com/ru/post/221937/
All Articles